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