From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / tunnel_draw.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* atunnels --- OpenGL Advanced Tunnel Demo */
3
4 #if 0
5 static const char sccsid[] = "@(#)tunnel_draw.c 5.13 2004/05/25 xlockmore";
6 #endif
7
8 /* Copyright (c) E. Lassauge, 2002-2004. */
9
10 /*
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * The original code for this mode was written by Roman Podobedov
24  * Email: romka@ut.ee
25  * WEB: http://romka.demonews.com
26  *
27  * Eric Lassauge  (May-25-2004) <lassauge@users.sourceforge.net>
28  *                                  http://lassauge.free.fr/linux.html
29  *
30  * REVISION HISTORY:
31  * E.Lassauge - 25-May-2004:
32  *      - added more texture
33  *      - random texture init
34  *
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #ifdef USE_GL /* whole file */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <math.h>
46
47 #ifdef STANDALONE
48 # ifndef HAVE_JWXYZ
49 #  include <GL/gl.h>
50 #  include <GL/glu.h>
51 # endif
52 #endif
53 #ifdef HAVE_ANDROID
54 #include <GLES/gl.h>
55 #endif
56
57
58 #include "tunnel_draw.h"
59
60 #ifdef STANDALONE /* For NRAND() */
61 #include "xlockmoreI.h"          /* in xscreensaver distribution */
62 #else /* STANDALONE */
63 #include "xlock.h"              /* in xlockmore distribution */
64 #endif /* STANDALONE */
65
66 typedef struct 
67 {
68   float x, y, z; /* Point coordinates */
69 } cvPoint;
70
71 typedef struct _tnPath
72 {
73         cvPoint p;
74         struct _tnPath *next;
75 } tnPath;
76
77
78 static const cvPoint initpath[]={
79 {0.000000, 0.000000, 0.000000},
80 {2.000000, 1.000000, 0.000000},
81 {4.000000, 0.000000, 0.000000},
82 {6.000000, 1.000000, 0.000000},
83 {8.000000, 0.000000, 1.000000},
84 {10.000000, 1.000000, 1.000000},
85 {12.000000, 1.500000, 0.000000},
86 {14.000000, 0.000000, 0.000000},
87 {16.000000, 1.000000, 0.000000},
88 {18.000000, 0.000000, 0.000000},
89 {20.000000, 0.000000, 1.000000},
90 {22.000000, 1.000000, 0.000000},
91 {24.000000, 0.000000, 1.000000},
92 {26.000000, 0.000000, 1.000000},
93 {28.000000, 1.000000, 0.000000},
94 {30.000000, 0.000000, 2.000000},
95 {32.000000, 1.000000, 0.000000},
96 {34.000000, 0.000000, 2.000000},
97 {-1.000000, -1.000000, -1.000000}
98 };
99
100
101 struct tunnel_state {
102
103   tnPath *path;
104
105   float cam_t;                                  /* Camera variables */
106   tnPath *cam_pos;
107   float alpha;
108
109   int tFlag;                                    /* Tunnel Drawing Variables */
110   cvPoint prev_points[10];
111   int current_texture;
112
113   float ModeX;                                  /* Modes */
114   int ModeXFlag;
115 };
116
117 /*=================== Vector normalization ==================================*/
118 static void
119 normalize(cvPoint *V)
120 {
121   float d;
122
123   /* Vector length */
124   d = (float)sqrt(V->x*V->x + V->y*V->y + V->z*V->z);
125
126   /* Normalization */
127   V->x /= d; 
128   V->y /= d; 
129   V->z /= d; 
130 }
131 /*=================== C = A x B  (Vector multiply) ==========================*/
132 #if 0
133 static void
134 vect_mult(cvPoint *A, cvPoint *B, cvPoint *C)
135 {
136         /* Vector multiply */
137         C->x = A->y*B->z - A->z*B->y;
138         C->y = A->z*B->x - A->x*B->z;
139         C->z = A->x*B->y - A->y*B->x;
140 }
141 #endif
142
143 /* Catmull-Rom Curve calculations */
144 static void
145 cvCatmullRom(cvPoint *p, float t, cvPoint *outp)
146 {
147         float t2, t3, t1;
148
149         t2 = t*t;
150         t3 = t*t*t;
151         t1 = (1-t)*(1-t);
152
153         outp->x = (-t*t1*p[0].x + (2-5*t2+3*t3)*p[1].x + t*(1+4*t-3*t2)*p[2].x - t2*(1-t)*p[3].x)/2;
154         outp->y = (-t*t1*p[0].y + (2-5*t2+3*t3)*p[1].y + t*(1+4*t-3*t2)*p[2].y - t2*(1-t)*p[3].y)/2;
155         outp->z = (-t*t1*p[0].z + (2-5*t2+3*t3)*p[1].z + t*(1+4*t-3*t2)*p[2].z - t2*(1-t)*p[3].z)/2;
156 }
157
158 /*=================== Point Rotating Around Line ===========================
159 // p    - original point
160 // pp   - pivot point
161 // pl   - pivot line (vector)
162 // a    - angle to rotate in radians
163 // outp - output point
164 //==========================================================================
165 */
166 static void
167 RotateAroundLine(cvPoint *p, cvPoint *pp, cvPoint *pl, float a, cvPoint *outp)
168 {
169         cvPoint p1, p2;
170         float l, m, n, ca, sa;
171
172         p1.x = p->x - pp->x;
173         p1.y = p->y - pp->y;
174         p1.z = p->z - pp->z;
175
176         l = pl->x;
177         m = pl->y;
178         n = pl->z;
179
180         ca = cos(a);
181         sa = sin(a);
182
183         p2.x = p1.x*((l*l)+ca*(1-l*l)) + p1.y*(l*(1-ca)*m+n*sa) + p1.z*(l*(1-ca)*n-m*sa);
184         p2.y = p1.x*(l*(1-ca)*m-n*sa) + p1.y*(m*m+ca*(1-m*m)) + p1.z*(m*(1-ca)*n+l*sa);
185         p2.z = p1.x*(l*(1-ca)*n+m*sa) + p1.y*(m*(1-ca)*n-l*sa) + p1.z*(n*n+ca*(1-n*n));
186
187         outp->x = p2.x + pp->x;
188         outp->y = p2.y + pp->y;
189         outp->z = p2.z + pp->z;
190 }
191
192
193 /*=================== Load camera and tunnel path ==========================*/
194 static void
195 LoadPath(struct tunnel_state *st)
196 {
197         float x, y, z;
198         tnPath *path1=NULL, *path2=NULL;
199
200         cvPoint *f = (cvPoint *)initpath;
201         
202         while (f->x != -1.0)
203         {
204                 x = f->x;
205                 y = f->y;
206                 z = f->z;
207                 f++;
208
209                 if (st->path == NULL)
210                 {
211                         st->path = (tnPath *)malloc(sizeof(tnPath));
212                         path1 = st->path;
213                 }
214                 else
215                 {
216                         if (!path1) path1 = st->path;
217                         path2 = (tnPath *)malloc(sizeof(tnPath));
218                         path1->next = path2;
219                         path1 = path2;
220                 }
221                 
222                 path1->next = NULL;
223                 path1->p.x = x;
224                 path1->p.y = y;
225                 path1->p.z = z;
226         }
227
228         st->cam_pos = st->path;
229         st->cam_t = 0;
230 }
231
232 /*=================== Tunnel Initialization ================================*/
233 struct tunnel_state *
234 atunnel_InitTunnel(void)
235 {
236     struct tunnel_state *st = (struct tunnel_state *) calloc (1, sizeof(*st));
237         LoadPath(st);
238         st->current_texture = NRAND(MAX_TEXTURE);
239     return st;
240 }
241
242 void
243 atunnel_DrawTunnel(struct tunnel_state *st, 
244                 int do_texture, int do_light, GLuint *textures)
245 {
246         tnPath *p, *p1, *cmpos;
247         cvPoint op, p4[4], T, ppp, op1, op2;
248         float t;
249         int i, j, k, flag;
250         cvPoint points[10];
251         GLfloat light_position[4];
252
253
254         /* Select current tunnel texture */
255         if (do_texture)
256                 glBindTexture(GL_TEXTURE_2D, textures[st->current_texture]);
257         
258         cmpos = st->cam_pos;
259         /* Get current curve */
260         if (st->cam_pos->next &&
261             st->cam_pos->next->next &&
262             st->cam_pos->next->next->next)
263         {
264                 p1 = st->cam_pos;
265                 for (i=0; i<4; i++)
266                 {
267                         p4[i].x = p1->p.x;
268                         p4[i].y = p1->p.y;
269                         p4[i].z = p1->p.z;
270                         p1 = p1->next;
271                 }
272         }
273         else 
274         {
275                 /* End of tunnel */
276                 st->ModeX = 1.0;
277                 st->ModeXFlag = 0;
278                 return;
279         };
280                 
281         /* Get current camera position */
282         cvCatmullRom(p4, st->cam_t, &op);
283
284         /* Next camera position */
285         st->cam_t += 0.02f;
286         if (st->cam_t >= 1)
287         {
288                 st->cam_t = st->cam_t - 1;
289                 cmpos = st->cam_pos->next;
290         }
291                 
292         /* Get curve for next camera position */
293         if (cmpos->next->next->next)
294         {
295                 p1 = cmpos;
296                 for (i=0; i<4; i++)
297                 {
298                         p4[i].x = p1->p.x;
299                         p4[i].y = p1->p.y;
300                         p4[i].z = p1->p.z;
301                         p1 = p1->next;
302                 }
303         }
304         else 
305         {       
306                 /*  End of tunnel */
307                 st->ModeX = 1.0;
308                 st->ModeXFlag = 0;
309                 return;
310         }
311         
312         /*  Get next camera position */
313         cvCatmullRom(p4, st->cam_t, &op1);
314         
315         /*  Rotate camera */
316         glRotatef(st->alpha, 0, 0, -1);
317         st->alpha += 1;
318         /*  Set camera position */
319         gluLookAt(op.x, op.y, op.z, op1.x, op1.y, op1.z, 0, 1, 0);
320
321         /*  Set light position */
322         if (do_light)
323         {
324                 light_position[0] = op.x;
325                 light_position[1] = op.y;
326                 light_position[2] = op.z;
327                 light_position[3] = 1;
328                 glLightfv(GL_LIGHT0, GL_POSITION, light_position);
329         }
330         
331         p = st->cam_pos;
332         flag = 0;
333         t = 0;
334         k = 0;
335         /*  Draw tunnel from current curve and next 2 curves */
336         glBegin(GL_QUADS);
337         while (k < 3)
338         {
339                 if (p->next->next->next)
340                 {
341                         p1 = p;
342                         for (i=0; i<4; i++)
343                         {
344                                 p4[i].x = p1->p.x;
345                                 p4[i].y = p1->p.y;
346                                 p4[i].z = p1->p.z;
347                                 p1 = p1->next;
348                         }
349                 }
350                 else
351                 {
352                         /*  End of tunnel */
353                         st->ModeX = 1.0;
354                         st->ModeXFlag = 0;
355             glEnd();
356                         return;
357                 }
358                 cvCatmullRom(p4, t, &op);
359
360                 ppp.x = op.x;
361                 ppp.y = op.y;
362                 ppp.z = op.z + 0.25;
363
364                 t += 0.1;
365                 if (t >= 1.0)
366                 {
367                         t = t - 1;
368                         k++;
369                         p = p->next;
370                 }
371         
372                 if (p->next->next->next)
373                 {
374                         p1 = p;
375                         for (i=0; i<4; i++)
376                         {
377                                 p4[i].x = p1->p.x;
378                                 p4[i].y = p1->p.y;
379                                 p4[i].z = p1->p.z;
380                                 p1 = p1->next;
381                         }
382                 } 
383                 else
384                 {
385                         /*  End of tunnel */
386                         st->ModeX = 1.0;
387                         st->ModeXFlag = 0;
388             glEnd();
389                         return;
390                 }
391                         
392                 cvCatmullRom(p4, t, &op1);
393
394                 T.x = op1.x - op.x;
395                 T.y = op1.y - op.y;
396                 T.z = op1.z - op.z;
397                         
398                 normalize(&T);
399
400                 for (i=0; i<10; i++)
401                 {
402                         RotateAroundLine(&ppp, &op, &T, ((float)i*36.0*M_PI/180.0), &op2);
403                         points[i].x = op2.x;
404                         points[i].y = op2.y;
405                         points[i].z = op2.z;
406                         if (!flag)
407                         {
408                                 st->prev_points[i].x = op2.x;
409                                 st->prev_points[i].y = op2.y;
410                                 st->prev_points[i].z = op2.z;
411                         }
412                 }
413         
414                 if (!flag)
415                 {
416                         flag = 1;
417                         continue;
418                 }
419                 
420                 /*  Draw 10 polygons for current point */
421                 for (i=0; i<10; i++)
422                 {
423                         j = i+1;
424                         if (j > 9) j = 0;
425                         glNormal3f(0, 0, 1); /*  Normal for lighting */
426                         glTexCoord2f(0, 0); glVertex3f(st->prev_points[i].x, st->prev_points[i].y, st->prev_points[i].z);
427                         glNormal3f(0, 0, 1);
428                         glTexCoord2f(1, 0); glVertex3f(points[i].x, points[i].y, points[i].z);
429                         glNormal3f(0, 0, 1);
430                         glTexCoord2f(1, 1); glVertex3f(points[j].x, points[j].y, points[j].z);
431                         glNormal3f(0, 0, 1);
432                         glTexCoord2f(0, 1); glVertex3f(st->prev_points[j].x, st->prev_points[j].y, st->prev_points[j].z);
433                 }
434                 /*  Save current polygon coordinates for next position */
435                 for (i=0; i<10; i++)
436                 {
437                         st->prev_points[i].x = points[i].x;
438                         st->prev_points[i].y = points[i].y;
439                         st->prev_points[i].z = points[i].z;
440                 }
441         }
442         glEnd();
443         st->cam_pos = cmpos;
444 }
445
446 /* =================== Show splash screen =================================== */
447 void
448 atunnel_SplashScreen(struct tunnel_state *st, 
449                   int do_wire, int do_texture, int do_light)
450 {
451         if (st->ModeX > 0)
452         {
453                 /*  Reset tunnel and camera position */
454                 if (!st->ModeXFlag)
455                 {
456                         st->cam_pos = st->path;                 
457                         st->cam_t = 0;
458                         st->tFlag = 0;
459                         st->ModeXFlag = 1;
460                         st->current_texture++;
461                         if (st->current_texture >= MAX_TEXTURE) st->current_texture = 0;
462                 }
463                 /*  Now we want to draw splash screen */
464                 glLoadIdentity();
465                 /*  Disable all unused features */
466                 glDisable(GL_DEPTH_TEST);
467                 glDisable(GL_LIGHTING);
468                 glDisable(GL_FOG);
469                 glDisable(GL_CULL_FACE);
470
471                 glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);
472                 glEnable(GL_BLEND);
473                 glDisable(GL_TEXTURE_2D);
474                 glColor4f(1, 1, 1, st->ModeX);
475                 
476                 /*  Draw splash screen (simply quad) */
477                 glBegin(GL_QUADS);
478                 glVertex3f(-10, -10, -1);
479                 glVertex3f(10, -10, -1);
480                 glVertex3f(10, 10, -1);
481                 glVertex3f(-10, 10, -1);
482                 glEnd();
483
484                 st->ModeX -= 0.05;
485                 if (st->ModeX <= 0)     st->ModeX = 0;
486
487                 if (!do_wire)
488                 {
489                         glEnable(GL_CULL_FACE);
490                         glEnable(GL_DEPTH_TEST);
491                 }
492                 if (do_light)
493                 {
494                         glEnable(GL_LIGHTING);
495                         glEnable(GL_FOG);
496                 }
497                 if (do_texture)
498                 {
499                         glEnable(GL_TEXTURE_2D);
500                 }
501                 glDisable(GL_BLEND);
502                 glColor4f(1, 1, 1, 1);
503         }
504 }
505
506 void
507 atunnel_FreeTunnel(struct tunnel_state *st)
508 {
509   free (st);
510 }
511
512 #endif