1 /* timetunnel. Based on dangerball.c, hack by Sean Brennan <zettix@yahoo.com>*/
2 /* dangerball, Copyright (c) 2001-2004 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
13 #define GL_GLEXT_PROTOTYPES 1
15 #include <math.h> /* for log2 */
17 #define DEFAULTS "*delay: 30000 \n" \
19 "*showFPS: False \n" \
20 "*timeStart: 0.0 \n" \
21 "*timeEnd: 27.79 \n" \
22 "*wireframe: False \n" \
26 # define refresh_tunnel 0
27 # define release_tunnel 0
29 #define countof(x) (sizeof((x))/sizeof((*x)))
31 #include "xlockmore.h"
34 #include "gltrackball.h"
37 #define DEF_START "0.00"
38 #define DEF_DILATE "1.00"
39 #define DEF_END "27.79"
40 #define DEF_LOCKLOGO "False"
41 #define DEF_DRAWLOGO "True"
42 #define DEF_REVERSE "False"
43 #define DEF_FOG "True"
44 #define DEF_TEXTURE "True"
45 #define MAX_TEXTURE 10
47 #define DIAMOND_LEN 10.0
49 static float start, end, dilate;
50 static Bool do_texture, drawlogo, wire, reverse, do_fog;
51 static const char *do_tx1, *do_tx2, *do_tx3, *do_tun1, *do_tun2, *do_tun3;
53 static XrmOptionDescRec opts[] = {
54 {"-texture" , ".texture", XrmoptionNoArg, "true" },
55 {"+texture" , ".texture", XrmoptionNoArg, "false" },
56 {"-start" , ".start", XrmoptionSepArg, 0 },
57 {"-end" , ".end", XrmoptionSepArg, 0 },
58 {"-dilate" , ".dilate", XrmoptionSepArg, 0 },
59 {"+logo" , ".drawlogo", XrmoptionNoArg, "false" },
60 {"-reverse" , ".reverse", XrmoptionNoArg, "true" },
61 {"+fog" , ".fog", XrmoptionNoArg, "false" },
62 {"-marquee" , ".marquee", XrmoptionSepArg, 0},
63 /* {"+marquee" , ".marquee", XrmoptionNoArg, "(none)"}, */
64 {"-tardis" , ".tardis", XrmoptionSepArg, 0},
65 /* {"+tardis" , ".tardis", XrmoptionNoArg, "(none)"}, */
66 {"-head" , ".head", XrmoptionSepArg, 0},
67 /* {"+head" , ".head", XrmoptionNoArg, "(none)"}, */
68 {"-tun1" , ".tun1", XrmoptionSepArg, 0},
69 /* {"+tun1" , ".tun1", XrmoptionNoArg, "(none)"}, */
70 {"-tun2" , ".tun2", XrmoptionSepArg, 0},
71 /* {"+tun2" , ".tun2", XrmoptionNoArg, "(none)"}, */
72 {"-tun3" , ".tun3", XrmoptionSepArg, 0},
73 /* {"+tun3" , ".tun3", XrmoptionNoArg, "(none)"}, */
76 static argtype vars[] = {
77 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
78 {&start, "start", "Start", DEF_START, t_Float},
79 {&end, "end", "End", DEF_END , t_Float},
80 {&dilate, "dilate", "Dilate", DEF_DILATE , t_Float},
81 {&drawlogo, "drawlogo", "DrawLogo", DEF_DRAWLOGO , t_Bool},
82 {&reverse, "reverse", "Reverse", DEF_REVERSE , t_Bool},
83 {&do_fog, "fog", "Fog", DEF_FOG , t_Bool},
84 {&do_tx1, "marquee", "Marquee", "(none)", t_String},
85 {&do_tx2, "tardis", "Tardis", "(none)", t_String},
86 {&do_tx3, "head", "Head", "(none)", t_String},
87 {&do_tun1, "tun1", "Tunnel 1", "(none)", t_String},
88 {&do_tun2, "tun2", "Tunnel 2", "(none)", t_String},
89 {&do_tun3, "tun3", "Tunnel 3", "(none)", t_String},
92 ENTRYPOINT ModeSpecOpt tunnel_opts = {countof(opts), opts, countof(vars), vars, NULL};
93 #include "xpm-ximage.h"
94 #include "images/logo-180.xpm"
95 #include "images/tunnelstar.xpm"
96 #include "images/timetunnel0.xpm"
97 #include "images/timetunnel1.xpm"
98 #include "images/timetunnel2.xpm"
101 #ifdef USE_GL /* whole file */
103 /* ANIMATION CONTROLS */
104 /* an effect is a collection of floating point variables that vary with time.
105 A knot is a timestamp with an array of floats. State is the current values of the floats.
106 State is set by linearly interpolating between knots */
108 float *knots, *state;
109 int numknots, knotwidth;
114 GLXContext *glx_context;
116 trackball_state *trackball;
119 int time_oldusec, time_oldsec;
121 int num_texshifts; /* animates tunnels. Not an effect. */
122 GLfloat pos, *texshift;
124 GLuint texture_binds[MAX_TEXTURE], cyllist, diamondlist;
126 float effect_time, effect_maxsecs; /* global time controls */
127 float start_time, end_time;
130 effect_t *effects; /* array of all effects */
132 } tunnel_configuration;
134 static tunnel_configuration *tconf = NULL;
136 /* allocate memory and populate effect with knot data */
137 static void init_effect(effect_t *e, int numk, int kwidth,
138 float dir, float *data )
143 e->knotwidth = kwidth;
145 e->knots = calloc(numk * kwidth, sizeof(float));
146 e->state = calloc(numk, sizeof(float));
147 for ( i = 0 ; i < e->numknots ; i++)
148 for ( j = 0 ; j < e->knotwidth; j++)
149 e->knots[i * kwidth + j] = data[i * kwidth + j];
152 /* static knot data. each effect is listed and knot data is hard coded.
153 Knots are linerally interpolated to yield float values, depending on
154 knot width. knot format is [time, data, data, data...].
155 Data can be alpha, zvalue, etc. */
156 static void init_effects(effect_t *e, int effectnum)
158 /* effect 1: wall tunnel. percent closed */
166 /* effect 2: tardis. distance and alpha */
176 /* effect 3: cylinder. alpha */
184 /* effect 4: fog. color, density, start, end */
186 {{0.0 , 1.0, 0.45, 3.0, 15.0},
187 {6.40, 1.0, 0.45, 3.0, 14.0},
188 {8.08, 1.0, 0.95, 1.0, 14.0},
189 {15.17, 1.0, 0.95, 1.0, 6.0},
190 {15.51, 1.0, 0.95, 3.0, 8.0},
191 {23.35, 1.0, 0.95, 3.0, 8.0},
192 {24.02, 0.0, 0.95, 2.3, 5.0},
193 {26.02, 0.0, 0.95, 2.3, 5.0},
194 {27.72, 0.0, 1.00, 0.3, 0.9}
197 /* effect 5: logo. dist, alpha */
207 /* effect 6: diamond tunnel. alpha */
213 /* effect 7: tardis cap draw . positive draws cap*/
219 /* effect 8: star/asterisk: alpha */
227 /* effect 9: whohead 1 alpha */
237 /* effect 10: whohead-brite alpha */
244 /* {13.95, 0.00}}; */
246 /* effect 11: whohead-psy alpha */
254 /* effect 12: whohead-silhouette pos-z, alpha */
261 {16.78, 0.1, 0.00} };
263 /* effect 1: wall tunnel */
265 init_effect(e, 6, 2, -0.2, (float *) e1d);
267 /* effect 2: tardisl */
269 init_effect(e, 8, 3, 1.0, (float *) e2d);
271 /* effect 3: cylinder tunnel */
273 init_effect(e, 5, 2, 0.889 , (float *) e3d);
275 /* effect 4: fog color */
277 init_effect(e, 9, 5, 1.0, (float *) e4d);
278 /* effect 5: logo distance, alpha*/
280 init_effect(e, 7, 3, 1.0, (float *) e5d);
281 /* effect 6: diamond tunnel, alpha*/
283 init_effect(e, 3, 2, 0.24 , (float *) e6d);
285 /* effect 7: cap wall tunnel*/
287 init_effect(e, 3, 2, 1.0, (float *) e7d);
289 /* effect 8: asterisk */
291 init_effect(e, 5, 2, 1.0, (float *) e8d);
293 /* effect 9, 10, 11, 12: whoheads */
295 init_effect(e, 5, 2, 1.0, (float *) e9d);
296 if (effectnum == 10 )
297 init_effect(e, 5, 2, 1.0, (float *) e10d);
298 if (effectnum == 11 )
299 init_effect(e, 5, 2, 1.0, (float *) e11d);
300 if (effectnum == 12 )
301 init_effect(e, 6, 3, 1.0, (float *) e12d);
305 /* set fog parameters, controlled by effect */
306 static void update_fog(float color, float density, float start, float end)
310 col[0] = col[1] = col[2] = color;
313 glFogi(GL_FOG_MODE, GL_LINEAR);
314 glFogfv(GL_FOG_COLOR, col);
315 glFogf(GL_FOG_DENSITY, density);
316 glFogf(GL_FOG_START, start);
317 glFogf(GL_FOG_END, end);
320 /* set effect's floating point data values by linearally interpolating
321 between two knots whose times bound the current time: eff_time */
323 static void update_knots(effect_t *e, float eff_time)
326 float timedelta, lowknot, highknot, *curknot, *nextknot;
328 for ( i = 0 ; i < e->numknots ; i++)
329 if (e->knots[i * e->knotwidth] <= eff_time) {
330 if ( i < e->numknots - 1)
331 nextknot = e->knots + (i + 1) * e->knotwidth;
333 /*repeat last knot to carry knot data forward*/
334 nextknot = e->knots + (i) * e->knotwidth;
335 curknot = e->knots + i * e->knotwidth;
336 if (*nextknot - *curknot <= 0.0) timedelta = 1.0;
338 timedelta = (eff_time-*curknot)/(*nextknot-*curknot);
339 if (timedelta > 1.0) timedelta = 1.0;
340 for (j = 1 ; j < e->knotwidth ; j++) {
341 highknot = (float) *(nextknot + j);
342 lowknot = (float) *(curknot + j);
343 e->state[j - 1 ] = lowknot+(highknot-lowknot)*timedelta;
350 /* Window management, etc
353 reshape_tunnel (ModeInfo *mi, int width, int height)
355 GLfloat h = (GLfloat) height / (GLfloat) width;
357 glViewport (0, 0, (GLint) width, (GLint) height);
359 glMatrixMode(GL_PROJECTION);
361 gluPerspective (90.0, 1/h, 0.2, 50.0);
363 glMatrixMode(GL_MODELVIEW);
365 gluLookAt( 0.0, 0.0, 0.3,
369 glClear(GL_COLOR_BUFFER_BIT);
376 tunnel_handle_event (ModeInfo *mi, XEvent *event)
378 tunnel_configuration *tc = &tconf[MI_SCREEN(mi)];
380 if (event->xany.type == ButtonPress &&
381 event->xbutton.button == Button1)
383 tc->button_down_p = True;
384 gltrackball_start (tc->trackball,
385 event->xbutton.x, event->xbutton.y,
386 MI_WIDTH (mi), MI_HEIGHT (mi));
389 else if (event->xany.type == ButtonRelease &&
390 event->xbutton.button == Button1)
392 tc->button_down_p = False;
395 else if (event->xany.type == ButtonPress &&
396 (event->xbutton.button == Button4 ||
397 event->xbutton.button == Button5))
399 gltrackball_mousewheel (tc->trackball, event->xbutton.button, 10,
400 !!event->xbutton.state);
403 else if (event->xany.type == MotionNotify &&
406 gltrackball_track (tc->trackball,
407 event->xmotion.x, event->xmotion.y,
408 MI_WIDTH (mi), MI_HEIGHT (mi));
415 static void setTexParams(void)
417 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
418 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
419 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
420 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
421 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
424 static void update_animation(tunnel_configuration *tc) {
426 /* time based, of course*/
427 /* shift texture based on elapsed time since previous call*/
430 int elapsed_usecs, elapsed_secs, i;
431 float computed_timeshift;
433 /* get new animation time */
434 gettimeofday(&tv, &tz);
435 elapsed_secs = tv.tv_sec - tc->time_oldsec;
436 elapsed_usecs = tv.tv_usec - tc->time_oldusec;
437 /* store current time */
438 tc->time_oldsec = tv.tv_sec ;
439 tc->time_oldusec = tv.tv_usec;
440 /* elaped time. computed timeshift is tenths of a second */
441 computed_timeshift = (float) (elapsed_secs * 1000000. + elapsed_usecs)/
444 /* calibrate effect time to lie between start and end times */
445 /* loop if time exceeds end time */
447 tc->effect_time -= computed_timeshift / 10.0 * dilate;
449 tc->effect_time += computed_timeshift / 10.0 * dilate;
450 if ( tc->effect_time >= tc->end_time)
451 tc->effect_time = tc->start_time;
452 if ( tc->effect_time < tc->start_time)
453 tc->effect_time = tc->end_time;;
455 /* move texture shifters in effect's direction, e.g. tardis
456 tunnel moves backward, effect 1's direction */
458 tc->texshift[0] -= tc->effects[1].direction * computed_timeshift/ 10.0;
459 tc->texshift[1] -= tc->effects[3].direction * computed_timeshift/ 10.0;
460 tc->texshift[2] -= tc->effects[6].direction * computed_timeshift/ 10.0;
463 tc->texshift[0] += tc->effects[1].direction * computed_timeshift/ 10.0;
464 tc->texshift[1] += tc->effects[3].direction * computed_timeshift/ 10.0;
465 tc->texshift[2] += tc->effects[6].direction * computed_timeshift/ 10.0;
468 /* loop texture shifters if necessary */
469 for ( i = 0 ; i < tc->num_texshifts; i++) {
470 if (tc->texshift[i] > 1.0)
471 tc->texshift[i] -= (int) tc->texshift[i];
472 if (tc->texshift[i]< -1.0)
473 tc->texshift[i] -= (int) tc->texshift[i];
476 /* update effect data with current time. Uses linear interpolation */
477 for ( i = 1 ; i <= tc->num_effects ; i++)
478 update_knots(&tc->effects[i], tc->effect_time);
480 } /*update_animation*/
482 /* draw a textured(tex) quad at a certain depth (z), and certain alpha (alpha),
483 with aspect ratio (aspect), and blending mode (blend_mode) of either adding
484 or subtracting. if alpha is zero or less, nothing happens */
485 static void draw_sign(ModeInfo *mi, tunnel_configuration *tc, float z, float alpha, float aspect,
486 GLuint tex, int blend_mode)
490 mi->polygon_count ++;
491 /* glEnable(GL_BLEND); */
492 glBlendColor(0.0, 0.0, 0.0, alpha);
493 /*glBlendColor(0.0, 0.0, 0.0, 0.0); */
494 if (blend_mode == 1) {
495 glBlendFunc(GL_CONSTANT_ALPHA,
497 glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
498 } else if (blend_mode == 2) {
499 glBlendFunc(GL_CONSTANT_ALPHA,
501 glBlendEquation(GL_FUNC_ADD);
503 glBlendFunc(GL_CONSTANT_ALPHA,
504 GL_ONE_MINUS_CONSTANT_ALPHA);
505 glBlendEquation(GL_FUNC_ADD);
506 } /* blend mode switch */
508 #ifdef HAVE_GLBINDTEXTURE
510 glBindTexture(GL_TEXTURE_2D, tc->texture_binds[tex]);
513 glTexCoord2f(1.0, 0.0);
514 glVertex3f(-1.0 , -1.0 * aspect , z);
515 glTexCoord2f(1.0, 1.0);
516 glVertex3f(-1.0 , 1.0 * aspect , z);
517 glTexCoord2f(0.0, 1.0);
518 glVertex3f(1.0 , 1.0 * aspect , z);
519 glTexCoord2f(0.0, 0.0);
520 glVertex3f(1.0 , -1.0 * aspect , z);
522 if (blend_mode != 0) {
523 glBlendFunc(GL_CONSTANT_ALPHA,
524 GL_ONE_MINUS_CONSTANT_ALPHA);
525 glBlendEquation(GL_FUNC_ADD);
527 /* glDisable(GL_BLEND); */
533 /* draw a time tunnel. used for both cylinder and diamond tunnels.
534 uses texture shifter (indexed by shiftnum) to simulate motion.
535 tunnel does not move, and is acutally a display list. if alpha = 0, skip */
536 static void draw_cyl(ModeInfo *mi, tunnel_configuration *tc, float alpha, int texnum, int listnum, int shiftnum)
539 if (listnum == tc->diamondlist)
540 mi->polygon_count += 4;
541 if (listnum == tc->cyllist)
542 mi->polygon_count += 30;
543 glMatrixMode(GL_TEXTURE);
545 glTranslatef(tc->texshift[shiftnum], 0.0, 0.0);
546 glMatrixMode(GL_MODELVIEW);
547 /* glEnable(GL_BLEND); */
548 glBlendColor(0.0, 0.0, 0.0, alpha);
549 glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
551 #ifdef HAVE_GLBINDTEXTURE
553 glBindTexture(GL_TEXTURE_2D, tc->texture_binds[texnum]);
557 glMatrixMode(GL_TEXTURE);
559 glMatrixMode(GL_MODELVIEW);
560 /* glDisable(GL_BLEND); */
565 /* make tardis type tunnel. Starts as walls and then
566 grows to outline of tardis. percent is how complete
567 tardis outline is. cap is to draw cap for nice fog effects */
569 static void make_wall_tunnel(ModeInfo *mi, tunnel_configuration *tc, float percent, float cap)
571 /* tardis is about 2x1, so wrap tex around, starting at the base*/
579 that's br=bottom right, etc. ttr is top-top-right */
581 float half_floor= 0.08333333333333333,
582 full_wall = 0.33333333333333333;
588 bl0, depth=0.3, zdepth=15.0;
589 /* zdepth is how far back tunnel goes */
590 /* depth is tex coord scale. low number = fast texture shifting */
592 float textop, texbot;
599 tr1 = r1 + half_floor;
601 tl1 = tl0 + half_floor;
606 glMatrixMode(GL_TEXTURE);
608 glRotatef(90.0, 0.0, 0.0, 1.0);
609 glTranslatef(tc->texshift[0], 0.0, 0.0);
610 glMatrixMode(GL_MODELVIEW);
612 #ifdef HAVE_GLBINDTEXTURE
614 glBindTexture(GL_TEXTURE_2D, tc->texture_binds[0]);
616 glColor3f(1.0, 1.0, 0.0);
617 if (cap > 0.0 && percent > 0.0 && drawlogo && do_fog) {
618 mi->polygon_count += 6;
619 glBegin(GL_TRIANGLE_FAN);
620 glVertex3f(0.0, 0.0, zdepth);
621 glVertex3f(-1.0, -2.0, zdepth);
622 glVertex3f(1.0, -2.0, zdepth);
623 glVertex3f(1.0, 2.0, zdepth);
624 glVertex3f(0.2, 2.0, zdepth);
625 glVertex3f(0.2, 2.2, zdepth);
626 glVertex3f(-0.2, 2.2, zdepth);
627 glVertex3f(-0.2, 2.0, zdepth);
628 glVertex3f(-1.0, 2.0, zdepth);
629 glVertex3f(-1.0, -2.0, zdepth);
632 if (percent > ( full_wall * 2.0)) {
635 height = (percent - full_wall * 2.0) /( 1.0 - full_wall * 2.0);
636 if (height > 1.0) height = 1.0;
640 mi->polygon_count += 2;
641 if ( height > 0.90) {
642 mi->polygon_count += 2;
645 textop = tr0 + half_floor * height;
646 glTexCoord2f(0.0, texbot);
647 glVertex3f(0.2, 2.2, 0.0);
649 glTexCoord2f(0.0, textop);
650 glVertex3f(2.0 - height * 2.0, 2.2, 0.0);
652 glTexCoord2f(depth, textop);
653 glVertex3f(2.0 - height * 2.0, 2.2, zdepth);
655 glTexCoord2f(depth, texbot);
656 glVertex3f(0.2, 2.2, zdepth);
659 texbot = tl1 - half_floor * height;
661 glTexCoord2f(0.0, texbot);
662 glVertex3f(-2.0 + height * 2.0, 2.2, 0.0);
664 glTexCoord2f(0.0, textop);
665 glVertex3f(-0.2, 2.2, 0.0);
667 glTexCoord2f(depth, textop);
668 glVertex3f(-0.2, 2.2, zdepth);
670 glTexCoord2f(depth, texbot);
671 glVertex3f(-2.0 + height * 2.0, 2.2, zdepth);
673 if (height > 0.90) height = 0.90;
677 textop = tr0 + half_floor * height;
678 glTexCoord2f(0.0, texbot);
679 glVertex3f(0.2, 2.0, 0.0);
681 glTexCoord2f(0.0, textop);
682 glVertex3f(0.2, 0.4 + height * 2.0, 0.0);
684 glTexCoord2f(depth, textop);
685 glVertex3f(0.2, 0.4 + height * 2.0, zdepth);
687 glTexCoord2f(depth, texbot);
688 glVertex3f(0.2, 2.0, zdepth);
691 texbot = tl1 - half_floor * height;
693 glTexCoord2f(0.0, texbot);
694 /*glVertex3f(-.2, 2.0 + (0.9 - height) * 2.0, 0.0); */
695 glVertex3f(-.2, 0.4 + height * 2.0, 0.0);
697 glTexCoord2f(0.0, textop);
698 glVertex3f(-.2, 2.0, 0.0);
700 glTexCoord2f(depth, textop);
701 glVertex3f(-.2, 2.0, zdepth);
703 glTexCoord2f(depth, texbot);
704 glVertex3f(-.2, 0.4 + height * 2.0, zdepth);
707 height = (percent - full_wall * 2.0) /( 1.0 - full_wall * 2.0);
708 if (height > 0.8) height = 0.8;
711 mi->polygon_count += 2;
714 textop = tr0 + half_floor * height;
715 glTexCoord2f(0.0, texbot);
716 glVertex3f(1.0, 2.0, 0.0);
718 glTexCoord2f(0.0, textop);
719 glVertex3f(1.0 - height, 2.0, 0.0);
721 glTexCoord2f(depth, textop);
722 glVertex3f(1.0 - height, 2.0, zdepth);
724 glTexCoord2f(depth, texbot);
725 glVertex3f(1.0, 2.0, zdepth);
728 texbot = tl1 - half_floor * height;
730 glTexCoord2f(0.0, texbot);
731 glVertex3f(-1.0 + height, 2.0, 0.0);
733 glTexCoord2f(0.0, textop);
734 glVertex3f(-1.0, 2.0, 0.0);
736 glTexCoord2f(depth, textop);
737 glVertex3f(-1.0, 2.0, zdepth);
739 glTexCoord2f(depth, texbot);
740 glVertex3f(-1.0 + height, 2.0, zdepth);
742 height = (percent - full_wall * 2.0) /( 1.0 - full_wall * 2.0);
744 if (height > 1.0) height = 1.0;
747 mi->polygon_count += 2;
750 textop = tr0 + half_floor * height;
751 glTexCoord2f(0.0, texbot);
752 glVertex3f(1.0, -2.0, 0.0);
754 glTexCoord2f(0.0, textop);
755 glVertex3f(1.0 - height, -2.0, 0.0);
757 glTexCoord2f(depth, textop);
758 glVertex3f(1.0 - height, -2.0, zdepth);
760 glTexCoord2f(depth, texbot);
761 glVertex3f(1.0, -2.0, zdepth);
764 texbot = tl1 - half_floor * height;
766 glTexCoord2f(0.0, texbot);
767 glVertex3f(-1.0 + height, -2.0, 0.0);
769 glTexCoord2f(0.0, textop);
770 glVertex3f(-1.0, -2.0, 0.0);
772 glTexCoord2f(depth, textop);
773 glVertex3f(-1.0, -2.0, zdepth);
775 glTexCoord2f(depth, texbot);
776 glVertex3f(-1.0 + height, -2.0, zdepth);
783 mi->polygon_count += 2;
785 height = percent / ( full_wall * 2.0);
786 if (height > 1.0) height = 1.0;
787 textop = (l0 + l1) / 2.0 - full_wall * 0.5 * height;
788 texbot = (l0 + l1) / 2.0 + full_wall * 0.5 * height;
790 glTexCoord2f(0.0, textop);
791 glVertex3f(-1.0, height * 2, 0.0);
793 glTexCoord2f(0.0, texbot);
794 glVertex3f(-1.0, -height * 2, 0.0);
796 glTexCoord2f(depth, texbot);
797 glVertex3f(-1.0, -height * 2, zdepth);
799 glTexCoord2f(depth, textop);
800 glVertex3f(-1.0, height * 2, zdepth);
802 textop = (r0 + r1) / 2.0 - full_wall * 0.5 * height;
803 texbot = (r0 + r1) / 2.0 + full_wall * 0.5 * height;
805 glTexCoord2f(0.0, texbot);
806 glVertex3f(1.0, height * 2, 0.0);
808 glTexCoord2f(0.0, textop);
809 glVertex3f(1.0, -height * 2, 0.0);
811 glTexCoord2f(depth, textop);
812 glVertex3f(1.0, -height * 2, zdepth);
814 glTexCoord2f(depth, texbot);
815 glVertex3f(1.0, height * 2, zdepth);
820 glMatrixMode(GL_TEXTURE);
822 glMatrixMode(GL_MODELVIEW);
823 } /* make_wall_tunnel */
825 /* wraps an int to between min and max.
826 Kind of like the remainder when devided by (max - min).
827 Used to create torus mapping on square array */
828 static int wrapVal(int val, int min, int max)
834 ret = min + (val - max ) % (max - min);
836 ret = max - (min - val) % (max - min);
840 /*=================== Load Texture =========================================*/
841 /* ripped from atunnel.c, Copyright (c) E. Lassauge, 2003-2004. */
842 /* modified like so by Sean Brennan:
843 take texture object for glbind
846 blur color / alpha channel [3x3 box filter, done [blur] times
847 anegative : create b/w image from zero alpha. zero alpha gets bw_color,
848 nonzero alpha gets 1.0 - bwcolor, then alpha flipped to 1-alpha.
850 Inputs: xpm structure, or filename of xmp image. if filename == NULL, use structure.
851 Outputs: texture bound to texutre Id texbind.
855 static float mylog2(float x) { return ( log(x) / log(2));}
857 static void LoadTexture(ModeInfo * mi, char **fn, const char *filename, GLuint texbind, int blur, float bw_color, Bool anegative, Bool onealpha)
859 /* looping and temporary array index variables */
860 int ix, iy, bx, by, indx, indy, boxsize, cchan, tmpidx, dtaidx;
862 float boxdiv, tmpfa, blursum ;
863 unsigned char *tmpbuf, tmpa;
867 XImage *teximage; /* Texture data */
872 boxdiv = 1.0 / ( boxsize * 2.0 + 1.0) / ( boxsize * 2.0 + 1.0);
876 teximage = xpm_file_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi),
877 MI_COLORMAP(mi), filename);
879 teximage = xpm_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi),
880 MI_COLORMAP(mi), fn);
881 if (teximage == NULL) {
882 fprintf(stderr, "%s: error reading the texture.\n", progname);
883 glDeleteTextures(1, &texbind);
888 /* check if image is 2^kumquat, where kumquat is an integer between 1 and 10. Recale to
889 nearest power of 2. */
890 tmpfa = mylog2((float) teximage->width);
891 bx = 2 << (int) (tmpfa -1);
892 if (bx != teximage->width) {
894 if ((tmpfa - (int) tmpfa) > 0.5849)
897 tmpfa = mylog2((float) teximage->height);
898 by = 2 << (int) (tmpfa - 1);
899 if (by != teximage->height) {
901 if ((tmpfa - (int) tmpfa) > 0.5849)
906 tmpbuf = calloc(bx * by * 4, sizeof(unsigned char));
907 if (gluScaleImage(GL_RGBA, teximage->width, teximage->height, GL_UNSIGNED_BYTE, teximage->data,
908 bx, by, GL_UNSIGNED_BYTE, tmpbuf))
909 check_gl_error("scale image");
911 free(teximage->data);
912 teximage->data = (char *) tmpbuf;
913 teximage->width = bx;
914 teximage->height= by;
916 /* end rescale code */
919 for (ix = 0 ; ix < teximage->height * teximage->width; ix++)
921 if (!teximage->data[ ix * 4 + 3]) {
922 teximage->data[ ix * 4 + 3] = (unsigned char) 0xff;
923 tmpa = (unsigned char) (bw_color * 0xff);
926 teximage->data[ ix * 4 + 3] = (unsigned char) 0xff;
928 teximage->data[ ix * 4 + 3] = (unsigned char) 0xff -
929 teximage->data[ ix * 4 + 3];
930 tmpa = (unsigned char) ((1.0 - bw_color) * 0xff);
932 /* make texture uniform b/w color */
933 teximage->data[ ix * 4 + 0] =
934 (unsigned char) ( tmpa);
935 teximage->data[ ix * 4 + 1] =
936 (unsigned char) ( tmpa);
937 teximage->data[ ix * 4 + 2] =
938 (unsigned char) ( tmpa);
944 if (! anegative ) /* anegative alread b/w's the whole image */
945 for (ix = 0 ; ix < teximage->height * teximage->width; ix++)
946 if (!teximage->data[ ix * 4 + 3])
948 teximage->data[ ix * 4 + 0] =
949 (unsigned char) ( 255.0 * bw_color);
950 teximage->data[ ix * 4 + 1] =
951 (unsigned char) ( 255.0 * bw_color);
952 teximage->data[ ix * 4 + 2] =
953 (unsigned char) ( 255.0 * bw_color);
956 tmpbuf = calloc(teximage->height * teximage->width * 4, sizeof(unsigned char) ) ;
958 /* zero out tmp alpha buffer */
959 for (iy = 0 ; iy <teximage->height * teximage->width * 4 ; iy++)
961 for (cchan = 0; cchan < 4 ; cchan++) {
962 for (iy = 0 ; iy < teximage->height ; iy++) {
963 for (ix = 0 ; ix < teximage->width ; ix++) {
964 dtaidx = (teximage->width * iy + ix) * 4;
965 tmpa = teximage->data[dtaidx + cchan];
966 tmpfa = (float) tmpa * boxdiv;
969 for (by = -boxsize ; by <= boxsize; by++) {
970 for (bx = -boxsize ; bx <= boxsize; bx++) {
971 indx = wrapVal(ix + bx, 0, teximage->width);
972 indy = wrapVal(iy + by, 0, teximage->height);
973 tmpidx = (teximage->width * indy + indx) * 4;
975 tmpbuf[tmpidx + cchan] += (unsigned char) blursum;
981 /* copy back buffer */
982 for (ix = 0 ; ix < teximage->height * teximage->width * 4; ix++)
983 teximage->data[ix] = tmpbuf[ix];
985 free(tmpbuf); /*tidy*/
992 #ifdef HAVE_GLBINDTEXTURE
993 glBindTexture(GL_TEXTURE_2D, texbind);
995 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
996 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, teximage->width, teximage->height,
997 0, GL_RGBA, GL_UNSIGNED_BYTE, teximage->data);
998 check_gl_error("texture");
1000 XDestroyImage(teximage);
1003 /* creates cylinder for time tunnel. sides, zmin, zmax, rad(ius) obvious.
1004 stretch scales texture coords; makes tunnel go slower the larger it is.
1005 not drawn, but put into display list. */
1006 static void makecyl(int sides, float zmin, float zmax, float rad, float stretch)
1013 glBegin(GL_TRIANGLE_FAN);
1014 glTexCoord2f(1.0, 0.0);
1015 glVertex3f(0.0 , 0.0 , zmax);
1016 for (i = 0 ; i <= sides; i++) {
1017 theta = 2.0 * M_PI * ((float) i / (float) sides);
1018 glVertex3f(cos(theta) * rad, sin(theta) * rad, zmax);
1020 glVertex3f(cos(0.0) * rad, sin(0.0) * rad, zmax);
1024 glBegin(GL_QUAD_STRIP);
1025 for (i = 0 ; i <= sides; i++)
1028 theta = 2.0 * M_PI * ((float) i / (float) sides);
1029 glTexCoord2f(0.0, 1.0 * (float) i / (float) sides);
1030 glVertex3f(cos(theta) * rad, sin(theta) * rad, zmin);
1031 glTexCoord2f(stretch, 1.0 * (float) i / (float) sides);
1032 glVertex3f(cos(theta) * rad, sin(theta) * rad, zmax);
1035 glTexCoord2f(0.0, 1.0);
1036 glVertex3f(cos(theta) * rad, sin(theta) * rad, zmin);
1037 glTexCoord2f(stretch, 1.0);
1038 glVertex3f(cos(theta) * rad, sin(theta) * rad, zmax);
1045 init_tunnel (ModeInfo *mi)
1049 tunnel_configuration *tc;
1051 wire = MI_IS_WIREFRAME(mi);
1054 tconf = (tunnel_configuration *)
1055 calloc (MI_NUM_SCREENS(mi), sizeof (tunnel_configuration));
1057 fprintf(stderr, "%s: out of memory\n", progname);
1061 tc = &tconf[MI_SCREEN(mi)];
1064 tc = &tconf[MI_SCREEN(mi)];
1066 tc->glx_context = init_GL(mi);
1068 tc->cyllist = glGenLists(1);
1069 tc->diamondlist = glGenLists(1);
1070 tc->num_effects = 12;
1071 tc->num_texshifts = 3;
1072 tc->effect_time = 0.0;
1073 tc->effect_maxsecs = 30.00;
1074 /* check bounds on cmd line opts */
1075 if (start > tc->effect_maxsecs) start = tc->effect_maxsecs;
1076 if (end > tc->effect_maxsecs) end = tc->effect_maxsecs;
1077 if (start < tc->effect_time) start = tc->effect_time;
1078 if (end < tc->effect_time) end = tc->effect_time;
1080 /* set loop times, in seconds */
1081 tc->start_time = start;
1084 /* reset animation knots, effect 0 not defined. */
1085 tc->effects = malloc(sizeof(effect_t) * ( tc->num_effects + 1));
1086 for ( i = 1; i <= tc->num_effects ; i++)
1087 init_effects(&tc->effects[i], i);
1090 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1096 /* the following textures are loaded, and possible overridden:
1097 tunnel 1, tunnel 2, tunnel 3, marquee, tardis, head */
1098 glGenTextures(MAX_TEXTURE, tc->texture_binds);
1100 /*LoadTexture(*mi, **fn, *filename, texbind, bluralpha, bw_color, anegative, onealpha)*/
1101 if (strcasecmp (do_tun1, "(none)")) /* tunnel 1 */
1102 LoadTexture(mi, NULL, do_tun1, tc->texture_binds[0], 0,0.0, False, False);
1104 LoadTexture(mi, timetunnel0_xpm, NULL, tc->texture_binds[0], 0, 0.0, False, False);
1105 if (strcasecmp (do_tun2, "(none)")) /* tunnel 2 */
1106 LoadTexture(mi, NULL, do_tun2, tc->texture_binds[2], 0,0.0, False, False);
1108 LoadTexture(mi, timetunnel1_xpm, NULL, tc->texture_binds[2], 0, 0.0, False, False);
1109 if (strcasecmp (do_tun3, "(none)")) /* tunnel 3 */
1110 LoadTexture(mi, NULL, do_tun3, tc->texture_binds[5], 0,0.0, False, False);
1112 LoadTexture(mi, timetunnel2_xpm, NULL, tc->texture_binds[5], 0, 0.0, False, False);
1113 LoadTexture(mi, tunnelstar_xpm, NULL, tc->texture_binds[4], 0, 0.0, False, False);
1114 if (strcasecmp (do_tx1, "(none)")) /* marquee */
1115 LoadTexture(mi, NULL, do_tx1, tc->texture_binds[3], 0,0.0, False, False);
1117 LoadTexture(mi, (char **) logo_180_xpm, NULL, tc->texture_binds[3], 0,0.0, False, False);
1118 if (strcasecmp (do_tx2, "(none)")) /* tardis */
1119 LoadTexture(mi, NULL, do_tx2, tc->texture_binds[1], 0, 0.0 ,False, False);
1121 LoadTexture(mi, (char **) logo_180_xpm, NULL, tc->texture_binds[1], 0,0.0, False, False);
1122 if (strcasecmp (do_tx3, "(none)")) { /* head */
1123 LoadTexture(mi, NULL, do_tx3, tc->texture_binds[6], 0, 0.0 ,False, False);
1125 LoadTexture(mi, NULL, do_tx3, tc->texture_binds[9], 2,1.0, True, True);
1127 LoadTexture(mi, (char **) logo_180_xpm, NULL, tc->texture_binds[6], 0,0.0, False, False);
1129 LoadTexture(mi, (char **) logo_180_xpm, NULL, tc->texture_binds[9], 2,1.0, True, True);
1131 glEnable(GL_TEXTURE_2D);
1132 check_gl_error("tex");
1135 reshape_tunnel (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1137 glDisable(GL_DEPTH_TEST); /* who needs it? ;-) */
1144 glEnable(GL_ALPHA_TEST);
1145 glAlphaFunc(GL_GREATER, 0.5);
1148 tc->trackball = gltrackball_init ();
1151 tc->texshift = calloc(tc->num_texshifts, sizeof(GLfloat));
1152 for ( i = 0 ; i < tc->num_texshifts; i++)
1153 tc->texshift[i] = 0.0;
1155 glNewList(tc->cyllist, GL_COMPILE);
1156 makecyl(30, -0.1, CYL_LEN, 1., 10. / 40.0 * CYL_LEN);
1157 /*makecyl(30, -0.5, DIAMOND_LEN, 1., 4. / 40 * DIAMOND_LEN); */
1160 glNewList(tc->diamondlist, GL_COMPILE);
1161 makecyl(4, -0.5, DIAMOND_LEN, 1., 4. / 40 * DIAMOND_LEN);
1167 draw_tunnel (ModeInfo *mi)
1169 tunnel_configuration *tc = &tconf[MI_SCREEN(mi)];
1170 Display *dpy = MI_DISPLAY(mi);
1171 Window window = MI_WINDOW(mi);
1174 if (!tc->glx_context)
1177 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tc->glx_context));
1179 glShadeModel(GL_SMOOTH);
1181 glEnable(GL_NORMALIZE);
1182 /* glEnable(GL_CULL_FACE); */
1184 glClear(GL_COLOR_BUFFER_BIT );
1186 update_animation(tc);
1191 glRotatef(180., 0., 1., 0.);
1192 gltrackball_rotate (tc->trackball);
1193 glRotatef(180., 0., 1., 0.);
1197 mi->polygon_count = 0;
1199 update_fog(tc->effects[4].state[0], /*color*/
1200 tc->effects[4].state[1], /*density*/
1201 tc->effects[4].state[2], /*start*/
1202 tc->effects[4].state[3]); /*end*/
1204 /* --- begin composite image assembly --- */
1206 /* head mask and draw diamond tunnel */
1209 draw_cyl(mi, tc, tc->effects[6].state[0], 5, tc->diamondlist, 2);
1211 draw_sign(mi, tc,tc->effects[12].state[0], tc->effects[12].state[1], 1.0 / 1.33, 9, 1);
1212 glDisable(GL_BLEND);
1213 /* then tardis tunnel */
1214 make_wall_tunnel(mi, tc, tc->effects[1].state[0], tc->effects[7].state[0]);
1216 /* then cylinder tunnel */
1218 draw_cyl(mi, tc, tc->effects[3].state[0], 2, tc->cyllist, 1);
1220 /*void draw_sign(mi, tc,z,alpha,aspect,tex,blendmode)*/
1223 draw_sign(mi, tc, tc->effects[2].state[0], tc->effects[2].state[1], 2.0, 1, 0);
1226 draw_sign(mi, tc, tc->effects[5].state[0], tc->effects[5].state[1], 1.0, 3, 0);
1229 draw_sign(mi, tc,1.0, tc->effects[10].state[0], 1.0 / 1.33, 6, 2);
1230 /*who head psychadelic REMOVED*/
1231 /* draw_sign(mi, tc,1.0, tc->effects[11].state[0], 1.0 / 1.33, 8, 0); */
1234 /* draw_sign(mi, tc, tc->effects[8].state[0]tc->effects[8].state[0], 1.0 , 1.0, 4, 1); */
1235 draw_sign(mi, tc, tc->effects[8].state[0], tc->effects[8].state[0], 1.0, 4, 1);
1239 draw_sign(mi, tc,1.0, tc->effects[9].state[0], 1.0 / 1.33, 6, 0);
1241 /* --- end composite image assembly --- */
1246 if (mi->fps_p) do_fps (mi);
1249 check_gl_error("drawing done, calling swap buffers");
1250 glXSwapBuffers(dpy, window);
1253 XSCREENSAVER_MODULE_2 ("TimeTunnel", timetunnel, tunnel)