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