From http://www.jwz.org/xscreensaver/xscreensaver-5.16.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_COCOA
49 #  include <GL/gl.h>
50 #  include <GL/glu.h>
51 # endif
52 #endif
53
54 #include "tunnel_draw.h"
55
56 #ifdef STANDALONE /* For NRAND() */
57 #include "xlockmoreI.h"          /* in xscreensaver distribution */
58 #else /* STANDALONE */
59 #include "xlock.h"              /* in xlockmore distribution */
60 #endif /* STANDALONE */
61
62 typedef struct 
63 {
64   float x, y, z; /* Point coordinates */
65 } cvPoint;
66
67 typedef struct _tnPath
68 {
69         cvPoint p;
70         struct _tnPath *next;
71 } tnPath;
72
73
74 static const cvPoint initpath[]={
75 {0.000000, 0.000000, 0.000000},
76 {2.000000, 1.000000, 0.000000},
77 {4.000000, 0.000000, 0.000000},
78 {6.000000, 1.000000, 0.000000},
79 {8.000000, 0.000000, 1.000000},
80 {10.000000, 1.000000, 1.000000},
81 {12.000000, 1.500000, 0.000000},
82 {14.000000, 0.000000, 0.000000},
83 {16.000000, 1.000000, 0.000000},
84 {18.000000, 0.000000, 0.000000},
85 {20.000000, 0.000000, 1.000000},
86 {22.000000, 1.000000, 0.000000},
87 {24.000000, 0.000000, 1.000000},
88 {26.000000, 0.000000, 1.000000},
89 {28.000000, 1.000000, 0.000000},
90 {30.000000, 0.000000, 2.000000},
91 {32.000000, 1.000000, 0.000000},
92 {34.000000, 0.000000, 2.000000},
93 {-1.000000, -1.000000, -1.000000}
94 };
95
96
97 struct tunnel_state {
98
99   tnPath *path;
100
101   float cam_t;                                  /* Camera variables */
102   tnPath *cam_pos;
103   float alpha;
104
105   int tFlag;                                    /* Tunnel Drawing Variables */
106   cvPoint prev_points[10];
107   int current_texture;
108
109   float ModeX;                                  /* Modes */
110   int ModeXFlag;
111 };
112
113 /*=================== Vector normalization ==================================*/
114 static void normalize(cvPoint *V)
115 {
116   float d;
117
118   /* Vector length */
119   d = (float)sqrt(V->x*V->x + V->y*V->y + V->z*V->z);
120
121   /* Normalization */
122   V->x /= d; 
123   V->y /= d; 
124   V->z /= d; 
125 }
126 /*=================== C = A x B  (Vector multiply) ==========================*/
127 #if 0
128 static void vect_mult(cvPoint *A, cvPoint *B, cvPoint *C)
129 {
130         /* Vector multiply */
131         C->x = A->y*B->z - A->z*B->y;
132         C->y = A->z*B->x - A->x*B->z;
133         C->z = A->x*B->y - A->y*B->x;
134 }
135 #endif
136
137 /* Catmull-Rom Curve calculations */
138 static void cvCatmullRom(cvPoint *p, float t, cvPoint *outp)
139 {
140         float t2, t3, t1;
141
142         t2 = t*t;
143         t3 = t*t*t;
144         t1 = (1-t)*(1-t);
145
146         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;
147         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;
148         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;
149 }
150
151 /*=================== Point Rotating Around Line ===========================
152 // p    - original point
153 // pp   - pivot point
154 // pl   - pivot line (vector)
155 // a    - angle to rotate in radians
156 // outp - output point
157 //==========================================================================
158 */
159 static void RotateAroundLine(cvPoint *p, cvPoint *pp, cvPoint *pl, float a, cvPoint *outp)
160 {
161         cvPoint p1, p2;
162         float l, m, n, ca, sa;
163
164         p1.x = p->x - pp->x;
165         p1.y = p->y - pp->y;
166         p1.z = p->z - pp->z;
167
168         l = pl->x;
169         m = pl->y;
170         n = pl->z;
171
172         ca = cos(a);
173         sa = sin(a);
174
175         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);
176         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);
177         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));
178
179         outp->x = p2.x + pp->x;
180         outp->y = p2.y + pp->y;
181         outp->z = p2.z + pp->z;
182 }
183
184
185 /*=================== Load camera and tunnel path ==========================*/
186 static void LoadPath(struct tunnel_state *st)
187 {
188         float x, y, z;
189         tnPath *path1=NULL, *path2=NULL;
190
191         cvPoint *f = (cvPoint *)initpath;
192         
193         while (f->x != -1.0)
194         {
195                 x = f->x;
196                 y = f->y;
197                 z = f->z;
198                 f++;
199
200                 if (st->path == NULL)
201                 {
202                         st->path = (tnPath *)malloc(sizeof(tnPath));
203                         path1 = st->path;
204                 }
205                 else
206                 {
207                         if (!path1) path1 = st->path;
208                         path2 = (tnPath *)malloc(sizeof(tnPath));
209                         path1->next = path2;
210                         path1 = path2;
211                 }
212                 
213                 path1->next = NULL;
214                 path1->p.x = x;
215                 path1->p.y = y;
216                 path1->p.z = z;
217         }
218
219         st->cam_pos = st->path;
220         st->cam_t = 0;
221 }
222
223 /*=================== Tunnel Initialization ================================*/
224 struct tunnel_state *
225 InitTunnel(void)
226 {
227     struct tunnel_state *st = (struct tunnel_state *) calloc (1, sizeof(*st));
228         LoadPath(st);
229         st->current_texture = NRAND(MAX_TEXTURE);
230     return st;
231 }
232
233 void DrawTunnel(struct tunnel_state *st, 
234                 int do_texture, int do_light, GLuint *textures)
235 {
236         tnPath *p, *p1, *cmpos;
237         cvPoint op, p4[4], T, ppp, ppp1, op1, op2;
238         float t;
239         int i, j, k, flag;
240         cvPoint points[10];
241         GLfloat light_position[4];
242
243
244         /* Select current tunnel texture */
245         if (do_texture)
246                 glBindTexture(GL_TEXTURE_2D, textures[st->current_texture]);
247         
248         cmpos = st->cam_pos;
249         /* Get current curve */
250         if (st->cam_pos->next &&
251             st->cam_pos->next->next &&
252             st->cam_pos->next->next->next)
253         {
254                 p1 = st->cam_pos;
255                 for (i=0; i<4; i++)
256                 {
257                         p4[i].x = p1->p.x;
258                         p4[i].y = p1->p.y;
259                         p4[i].z = p1->p.z;
260                         p1 = p1->next;
261                 }
262         }
263         else 
264         {
265                 /* End of tunnel */
266                 st->ModeX = 1.0;
267                 st->ModeXFlag = 0;
268                 return;
269         };
270                 
271         /* Get current camera position */
272         cvCatmullRom(p4, st->cam_t, &op);
273
274         /* Next camera position */
275         st->cam_t += 0.02f;
276         if (st->cam_t >= 1)
277         {
278                 st->cam_t = st->cam_t - 1;
279                 cmpos = st->cam_pos->next;
280         }
281                 
282         /* Get curve for next camera position */
283         if (cmpos->next->next->next)
284         {
285                 p1 = cmpos;
286                 for (i=0; i<4; i++)
287                 {
288                         p4[i].x = p1->p.x;
289                         p4[i].y = p1->p.y;
290                         p4[i].z = p1->p.z;
291                         p1 = p1->next;
292                 }
293         }
294         else 
295         {       
296                 /*  End of tunnel */
297                 st->ModeX = 1.0;
298                 st->ModeXFlag = 0;
299                 return;
300         }
301         
302         /*  Get next camera position */
303         cvCatmullRom(p4, st->cam_t, &op1);
304         
305         /*  Rotate camera */
306         glRotatef(st->alpha, 0, 0, -1);
307         st->alpha += 1;
308         /*  Set camera position */
309         gluLookAt(op.x, op.y, op.z, op1.x, op1.y, op1.z, 0, 1, 0);
310
311         /*  Set light position */
312         if (do_light)
313         {
314                 light_position[0] = op.x;
315                 light_position[1] = op.y;
316                 light_position[2] = op.z;
317                 light_position[3] = 1;
318                 glLightfv(GL_LIGHT0, GL_POSITION, light_position);
319         }
320         
321         p = st->cam_pos;
322         flag = 0;
323         t = 0;
324         k = 0;
325         /*  Draw tunnel from current curve and next 2 curves */
326         glBegin(GL_QUADS);
327         while (k < 3)
328         {
329                 if (p->next->next->next)
330                 {
331                         p1 = p;
332                         for (i=0; i<4; i++)
333                         {
334                                 p4[i].x = p1->p.x;
335                                 p4[i].y = p1->p.y;
336                                 p4[i].z = p1->p.z;
337                                 p1 = p1->next;
338                         }
339                 }
340                 else
341                 {
342                         /*  End of tunnel */
343                         st->ModeX = 1.0;
344                         st->ModeXFlag = 0;
345             glEnd();
346                         return;
347                 }
348                 cvCatmullRom(p4, t, &op);
349
350                 ppp.x = op.x;
351                 ppp.y = op.y;
352                 ppp.z = op.z + 0.25;
353
354                 t += 0.1;
355                 if (t >= 1.0)
356                 {
357                         t = t - 1;
358                         k++;
359                         p = p->next;
360                 }
361         
362                 if (p->next->next->next)
363                 {
364                         p1 = p;
365                         for (i=0; i<4; i++)
366                         {
367                                 p4[i].x = p1->p.x;
368                                 p4[i].y = p1->p.y;
369                                 p4[i].z = p1->p.z;
370                                 p1 = p1->next;
371                         }
372                 } 
373                 else
374                 {
375                         /*  End of tunnel */
376                         st->ModeX = 1.0;
377                         st->ModeXFlag = 0;
378             glEnd();
379                         return;
380                 }
381                         
382                 cvCatmullRom(p4, t, &op1);
383
384                 ppp1.x = op1.x;
385                 ppp1.y = op1.y;
386                 ppp1.z = op1.z + 0.25;
387
388                 T.x = op1.x - op.x;
389                 T.y = op1.y - op.y;
390                 T.z = op1.z - op.z;
391                         
392                 normalize(&T);
393
394                 for (i=0; i<10; i++)
395                 {
396                         RotateAroundLine(&ppp, &op, &T, ((float)i*36.0*M_PI/180.0), &op2);
397                         points[i].x = op2.x;
398                         points[i].y = op2.y;
399                         points[i].z = op2.z;
400                         if (!flag)
401                         {
402                                 st->prev_points[i].x = op2.x;
403                                 st->prev_points[i].y = op2.y;
404                                 st->prev_points[i].z = op2.z;
405                         }
406                 }
407         
408                 if (!flag)
409                 {
410                         flag = 1;
411                         continue;
412                 }
413                 
414                 /*  Draw 10 polygons for current point */
415                 for (i=0; i<10; i++)
416                 {
417                         j = i+1;
418                         if (j > 9) j = 0;
419                         glNormal3f(0, 0, 1); /*  Normal for lighting */
420                         glTexCoord2f(0, 0); glVertex3f(st->prev_points[i].x, st->prev_points[i].y, st->prev_points[i].z);
421                         glNormal3f(0, 0, 1);
422                         glTexCoord2f(1, 0); glVertex3f(points[i].x, points[i].y, points[i].z);
423                         glNormal3f(0, 0, 1);
424                         glTexCoord2f(1, 1); glVertex3f(points[j].x, points[j].y, points[j].z);
425                         glNormal3f(0, 0, 1);
426                         glTexCoord2f(0, 1); glVertex3f(st->prev_points[j].x, st->prev_points[j].y, st->prev_points[j].z);
427                 }
428                 /*  Save current polygon coordinates for next position */
429                 for (i=0; i<10; i++)
430                 {
431                         st->prev_points[i].x = points[i].x;
432                         st->prev_points[i].y = points[i].y;
433                         st->prev_points[i].z = points[i].z;
434                 }
435         }
436         glEnd();
437         st->cam_pos = cmpos;
438 }
439
440 /* =================== Show splash screen =================================== */
441 void SplashScreen(struct tunnel_state *st, 
442                   int do_wire, int do_texture, int do_light)
443 {
444         if (st->ModeX > 0)
445         {
446                 /*  Reset tunnel and camera position */
447                 if (!st->ModeXFlag)
448                 {
449                         st->cam_pos = st->path;                 
450                         st->cam_t = 0;
451                         st->tFlag = 0;
452                         st->ModeXFlag = 1;
453                         st->current_texture++;
454                         if (st->current_texture >= MAX_TEXTURE) st->current_texture = 0;
455                 }
456                 /*  Now we want to draw splash screen */
457                 glLoadIdentity();
458                 /*  Disable all unused features */
459                 glDisable(GL_DEPTH_TEST);
460                 glDisable(GL_LIGHTING);
461                 glDisable(GL_FOG);
462                 glDisable(GL_CULL_FACE);
463
464                 glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);
465                 glEnable(GL_BLEND);
466                 glDisable(GL_TEXTURE_2D);
467                 glColor4f(1, 1, 1, st->ModeX);
468                 
469                 /*  Draw splash screen (simply quad) */
470                 glBegin(GL_QUADS);
471                 glVertex3f(-10, -10, -1);
472                 glVertex3f(10, -10, -1);
473                 glVertex3f(10, 10, -1);
474                 glVertex3f(-10, 10, -1);
475                 glEnd();
476
477                 st->ModeX -= 0.05;
478                 if (st->ModeX <= 0)     st->ModeX = 0;
479
480                 if (!do_wire)
481                 {
482                         glEnable(GL_CULL_FACE);
483                         glEnable(GL_DEPTH_TEST);
484                 }
485                 if (do_light)
486                 {
487                         glEnable(GL_LIGHTING);
488                         glEnable(GL_FOG);
489                 }
490                 if (do_texture)
491                 {
492                         glEnable(GL_TEXTURE_2D);
493                 }
494                 glDisable(GL_BLEND);
495                 glColor4f(1, 1, 1, 1);
496         }
497 }
498
499 void FreeTunnel(struct tunnel_state *st)
500 {
501   free (st);
502 }
503
504 #endif