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 #include <math.h> /* for log2 */
15 #define DEFAULTS "*delay: 30000 \n" \
17 "*showFPS: False \n" \
18 "*timeStart: 0.0 \n" \
19 "*timeEnd: 27.79 \n" \
20 "*wireframe: False \n" \
24 # define refresh_tunnel 0
25 # define release_tunnel 0
27 #define countof(x) (sizeof((x))/sizeof((*x)))
29 #include "xlockmore.h"
32 #include "gltrackball.h"
35 #define DEF_START "0.00"
36 #define DEF_DILATE "1.00"
37 #define DEF_END "27.79"
38 #define DEF_LOCKLOGO "False"
39 #define DEF_DRAWLOGO "True"
40 #define DEF_REVERSE "False"
41 #define DEF_FOG "True"
42 #define DEF_TEXTURE "True"
43 #define MAX_TEXTURE 10
45 #define DIAMOND_LEN 10.0
47 static float start, end, dilate;
48 static Bool do_texture, drawlogo, wire, reverse, do_fog;
49 #ifdef GET_SUED_BY_THE_BBC
53 static XrmOptionDescRec opts[] = {
54 {"-texture" , ".texture", XrmoptionNoArg, "true" },
55 {"-start" , ".start", XrmoptionSepArg, 0 },
56 {"-end" , ".end", XrmoptionSepArg, 0 },
57 {"-dilate" , ".dilate", XrmoptionSepArg, 0 },
58 #ifdef GET_SUED_BY_THE_BBC
59 {"-locklogo" , ".locklogo", XrmoptionNoArg, "true" },
61 {"-logo" , ".drawlogo", XrmoptionNoArg, "true" },
62 {"+logo" , ".drawlogo", XrmoptionNoArg, "false" },
63 {"-reverse" , ".reverse", XrmoptionNoArg, "true" },
64 {"-fog" , ".fog", XrmoptionNoArg, "false" },
67 static argtype vars[] = {
68 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
69 {&start, "start", "Start", DEF_START, t_Float},
70 {&end, "end", "End", DEF_END , t_Float},
71 {&dilate, "dilate", "Dilate", DEF_DILATE , t_Float},
72 #ifdef GET_SUED_BY_THE_BBC
73 {&locklogo, "locklogo", "LockLogo", DEF_LOCKLOGO , t_Bool},
75 {&drawlogo, "drawlogo", "DrawLogo", DEF_DRAWLOGO , t_Bool},
76 {&reverse, "reverse", "Reverse", DEF_REVERSE , t_Bool},
77 {&do_fog, "fog", "Fog", DEF_FOG , t_Bool},
80 ENTRYPOINT ModeSpecOpt tunnel_opts = {countof(opts), opts, countof(vars), vars, NULL};
81 #include "xpm-ximage.h"
82 #include "images/logo-180.xpm"
83 #include "images/tunnelstar.xpm"
84 #include "images/timetunnel0.xpm"
85 #include "images/timetunnel1.xpm"
86 #include "images/timetunnel2.xpm"
87 #ifdef GET_SUED_BY_THE_BBC
88 # include "images/tardis.xpm"
89 # include "images/whologo.xpm"
90 # include "images/whohead1.xpm"
91 /* #include "images/whohead_psy.xpm" */
92 # endif /* GET_SUED_BY_THE_BBC */
95 #ifdef USE_GL /* whole file */
97 /* ANIMATION CONTROLS */
98 /* an effect is a collection of floating point variables that vary with time.
99 A knot is a timestamp with an array of floats. State is the current values of the floats.
100 State is set by linearly interpolating between knots */
102 float *knots, *state;
103 int numknots, knotwidth;
108 GLXContext *glx_context;
110 trackball_state *trackball;
113 int time_oldusec, time_oldsec;
115 int num_texshifts; /* animates tunnels. Not an effect. */
116 GLfloat pos, *texshift;
118 GLuint texture_binds[MAX_TEXTURE], cyllist, diamondlist;
120 float effect_time, effect_maxsecs; /* global time controls */
121 float start_time, end_time;
124 effect_t *effects; /* array of all effects */
126 } tunnel_configuration;
128 static tunnel_configuration *tconf = NULL;
130 /* allocate memory and populate effect with knot data */
131 static void init_effect(effect_t *e, int numk, int kwidth,
132 float dir, float *data )
137 e->knotwidth = kwidth;
139 e->knots = calloc(numk * kwidth, sizeof(float));
140 e->state = calloc(numk, sizeof(float));
141 for ( i = 0 ; i < e->numknots ; i++)
142 for ( j = 0 ; j < e->knotwidth; j++)
143 e->knots[i * kwidth + j] = data[i * kwidth + j];
146 /* static knot data. each effect is listed and knot data is hard coded.
147 Knots are linerally interpolated to yield float values, depending on
148 knot width. knot format is [time, data, data, data...].
149 Data can be alpha, zvalue, etc. */
150 static void init_effects(effect_t *e, int effectnum)
152 /* effect 1: wall tunnel. percent closed */
160 /* effect 2: tardis. distance and alpha */
170 /* effect 3: cylender. alpha */
178 /* effect 4: fog. color, density, start, end */
180 {{0.0 , 1.0, 0.45, 3.0, 15.0},
181 {6.40, 1.0, 0.45, 3.0, 14.0},
182 {8.08, 1.0, 0.95, 1.0, 14.0},
183 {15.17, 1.0, 0.95, 1.0, 6.0},
184 {15.51, 1.0, 0.95, 3.0, 8.0},
185 {23.35, 1.0, 0.95, 3.0, 8.0},
186 {24.02, 0.0, 0.95, 2.3, 5.0},
187 {26.02, 0.0, 0.95, 2.3, 5.0},
188 {27.72, 0.0, 1.00, 0.3, 0.9}
191 /* effect 5: logo. dist, alpha */
201 /* effect 6: diamond tunnel. alpha */
207 /* effect 7: tardis cap draw . positive draws cap*/
213 /* effect 8: star/asterisk: alpha */
221 /* effect 9: whohead 1 alpha */
231 /* effect 10: whohead-brite alpha */
238 /* {13.95, 0.00}}; */
240 /* effect 11: whohead-psy alpha */
248 /* effect 12: whohead-silhouette pos-z, alpha */
255 {16.78, 0.1, 0.00} };
257 /* effect 1: wall tunnel */
259 init_effect(e, 6, 2, -0.2, (float *) e1d);
261 /* effect 2: tardisl */
263 init_effect(e, 8, 3, 1.0, (float *) e2d);
265 /* effect 3: cylender tunnel */
267 init_effect(e, 5, 2, 0.889 , (float *) e3d);
269 /* effect 4: fog color */
271 init_effect(e, 9, 5, 1.0, (float *) e4d);
272 /* effect 5: logo distance, alpha*/
274 init_effect(e, 7, 3, 1.0, (float *) e5d);
275 /* effect 6: diamond tunnel, alpha*/
277 init_effect(e, 3, 2, 0.24 , (float *) e6d);
279 /* effect 7: cap wall tunnel*/
281 init_effect(e, 3, 2, 1.0, (float *) e7d);
283 /* effect 8: asterisk */
285 init_effect(e, 5, 2, 1.0, (float *) e8d);
287 /* effect 9, 10, 11, 12: whoheads */
289 init_effect(e, 5, 2, 1.0, (float *) e9d);
290 if (effectnum == 10 )
291 init_effect(e, 5, 2, 1.0, (float *) e10d);
292 if (effectnum == 11 )
293 init_effect(e, 5, 2, 1.0, (float *) e11d);
294 if (effectnum == 12 )
295 init_effect(e, 6, 3, 1.0, (float *) e12d);
299 /* set fog parameters, controlled by effect */
300 static void update_fog(float color, float density, float start, float end)
304 col[0] = col[1] = col[2] = color;
307 glFogi(GL_FOG_MODE, GL_LINEAR);
308 glFogfv(GL_FOG_COLOR, col);
309 glFogf(GL_FOG_DENSITY, density);
310 glFogf(GL_FOG_START, start);
311 glFogf(GL_FOG_END, end);
314 /* set effect's floating point data values by linearally interpolating
315 between two knots whose times bound the current time: eff_time */
317 static void update_knots(effect_t *e, float eff_time)
320 float timedelta, lowknot, highknot, *curknot, *nextknot;
322 for ( i = 0 ; i < e->numknots ; i++)
323 if (e->knots[i * e->knotwidth] <= eff_time) {
324 if ( i < e->numknots - 1)
325 nextknot = e->knots + (i + 1) * e->knotwidth;
327 /*repeat last knot to carry knot data forward*/
328 nextknot = e->knots + (i) * e->knotwidth;
329 curknot = e->knots + i * e->knotwidth;
330 if (*nextknot - *curknot <= 0.0) timedelta = 1.0;
332 timedelta = (eff_time-*curknot)/(*nextknot-*curknot);
333 if (timedelta > 1.0) timedelta = 1.0;
334 for (j = 1 ; j < e->knotwidth ; j++) {
335 highknot = (float) *(nextknot + j);
336 lowknot = (float) *(curknot + j);
337 e->state[j - 1 ] = lowknot+(highknot-lowknot)*timedelta;
344 /* Window management, etc
347 reshape_tunnel (ModeInfo *mi, int width, int height)
349 GLfloat h = (GLfloat) height / (GLfloat) width;
351 glViewport (0, 0, (GLint) width, (GLint) height);
353 glMatrixMode(GL_PROJECTION);
355 gluPerspective (90.0, 1/h, 0.2, 50.0);
357 glMatrixMode(GL_MODELVIEW);
359 gluLookAt( 0.0, 0.0, 0.3,
363 glClear(GL_COLOR_BUFFER_BIT);
370 tunnel_handle_event (ModeInfo *mi, XEvent *event)
372 tunnel_configuration *tc = &tconf[MI_SCREEN(mi)];
374 if (event->xany.type == ButtonPress &&
375 event->xbutton.button == Button1)
377 tc->button_down_p = True;
378 gltrackball_start (tc->trackball,
379 event->xbutton.x, event->xbutton.y,
380 MI_WIDTH (mi), MI_HEIGHT (mi));
383 else if (event->xany.type == ButtonRelease &&
384 event->xbutton.button == Button1)
386 tc->button_down_p = False;
389 else if (event->xany.type == ButtonPress &&
390 (event->xbutton.button == Button4 ||
391 event->xbutton.button == Button5))
393 gltrackball_mousewheel (tc->trackball, event->xbutton.button, 10,
394 !!event->xbutton.state);
397 else if (event->xany.type == MotionNotify &&
400 gltrackball_track (tc->trackball,
401 event->xmotion.x, event->xmotion.y,
402 MI_WIDTH (mi), MI_HEIGHT (mi));
409 static void setTexParams(void)
411 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
412 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
413 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
414 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
415 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
418 static void update_animation(tunnel_configuration *tc) {
420 /* time based, of course*/
421 /* shift texture based on elapsed time since previous call*/
424 int elapsed_usecs, elapsed_secs, i;
425 float computed_timeshift;
427 /* get new animation time */
428 gettimeofday(&tv, &tz);
429 elapsed_secs = tv.tv_sec - tc->time_oldsec;
430 elapsed_usecs = tv.tv_usec - tc->time_oldusec;
431 /* store current time */
432 tc->time_oldsec = tv.tv_sec ;
433 tc->time_oldusec = tv.tv_usec;
434 /* elaped time. computed timeshift is tenths of a second */
435 computed_timeshift = (float) (elapsed_secs * 1000000. + elapsed_usecs)/
438 /* calibrate effect time to lie between start and end times */
439 /* loop if time exceeds end time */
441 tc->effect_time -= computed_timeshift / 10.0 * dilate;
443 tc->effect_time += computed_timeshift / 10.0 * dilate;
444 if ( tc->effect_time >= tc->end_time)
445 tc->effect_time = tc->start_time;
446 if ( tc->effect_time < tc->start_time)
447 tc->effect_time = tc->end_time;;
449 /* move texture shifters in effect's direction, e.g. tardis
450 tunnel moves backward, effect 1's direction */
452 tc->texshift[0] -= tc->effects[1].direction * computed_timeshift/ 10.0;
453 tc->texshift[1] -= tc->effects[3].direction * computed_timeshift/ 10.0;
454 tc->texshift[2] -= tc->effects[6].direction * computed_timeshift/ 10.0;
457 tc->texshift[0] += tc->effects[1].direction * computed_timeshift/ 10.0;
458 tc->texshift[1] += tc->effects[3].direction * computed_timeshift/ 10.0;
459 tc->texshift[2] += tc->effects[6].direction * computed_timeshift/ 10.0;
462 /* loop texture shifters if necessary */
463 for ( i = 0 ; i < tc->num_texshifts; i++) {
464 if (tc->texshift[i] > 1.0)
465 tc->texshift[i] -= (int) tc->texshift[i];
466 if (tc->texshift[i]< -1.0)
467 tc->texshift[i] -= (int) tc->texshift[i];
470 /* update effect data with current time. Uses linear interpolation */
471 for ( i = 1 ; i <= tc->num_effects ; i++)
472 update_knots(&tc->effects[i], tc->effect_time);
474 } /*update_animation*/
476 /* draw a textured(tex) quad at a certain depth (z), and certain alpha (alpha),
477 with aspect ratio (aspect), and blending mode (blend_mode) of either adding
478 or subtracting. if alpha is zero or less, nothing happens */
479 static void draw_sign(ModeInfo *mi, tunnel_configuration *tc, float z, float alpha, float aspect,
480 GLuint tex, int blend_mode)
484 mi->polygon_count ++;
485 /* glEnable(GL_BLEND); */
486 glBlendColor(0.0, 0.0, 0.0, alpha);
487 /*glBlendColor(0.0, 0.0, 0.0, 0.0); */
488 if (blend_mode == 1) {
489 glBlendFunc(GL_CONSTANT_ALPHA,
491 glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
492 } else if (blend_mode == 2) {
493 glBlendFunc(GL_CONSTANT_ALPHA,
495 glBlendEquation(GL_FUNC_ADD);
497 glBlendFunc(GL_CONSTANT_ALPHA,
498 GL_ONE_MINUS_CONSTANT_ALPHA);
499 glBlendEquation(GL_FUNC_ADD);
500 } /* blend mode switch */
502 #ifdef HAVE_GLBINDTEXTURE
504 glBindTexture(GL_TEXTURE_2D, tc->texture_binds[tex]);
507 glTexCoord2f(1.0, 0.0);
508 glVertex3f(-1.0 , -1.0 * aspect , z);
509 glTexCoord2f(1.0, 1.0);
510 glVertex3f(-1.0 , 1.0 * aspect , z);
511 glTexCoord2f(0.0, 1.0);
512 glVertex3f(1.0 , 1.0 * aspect , z);
513 glTexCoord2f(0.0, 0.0);
514 glVertex3f(1.0 , -1.0 * aspect , z);
516 if (blend_mode != 0) {
517 glBlendFunc(GL_CONSTANT_ALPHA,
518 GL_ONE_MINUS_CONSTANT_ALPHA);
519 glBlendEquation(GL_FUNC_ADD);
521 /* glDisable(GL_BLEND); */
527 /* draw a time tunnel. used for both cylender and diamond tunnels.
528 uses texture shifter (indexed by shiftnum) to simulate motion.
529 tunnel does not move, and is acutally a display list. if alpha = 0, skip */
530 static void draw_cyl(ModeInfo *mi, tunnel_configuration *tc, float alpha, int texnum, int listnum, int shiftnum)
533 if (listnum == tc->diamondlist)
534 mi->polygon_count += 4;
535 if (listnum == tc->cyllist)
536 mi->polygon_count += 30;
537 glMatrixMode(GL_TEXTURE);
539 glTranslatef(tc->texshift[shiftnum], 0.0, 0.0);
540 glMatrixMode(GL_MODELVIEW);
541 /* glEnable(GL_BLEND); */
542 glBlendColor(0.0, 0.0, 0.0, alpha);
543 glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
545 #ifdef HAVE_GLBINDTEXTURE
547 glBindTexture(GL_TEXTURE_2D, tc->texture_binds[texnum]);
551 glMatrixMode(GL_TEXTURE);
553 glMatrixMode(GL_MODELVIEW);
554 /* glDisable(GL_BLEND); */
559 /* make tardis type tunnel. Starts as walls and then
560 grows to outline of tardis. percent is how complete
561 tardis outline is. cap is to draw cap for nice fog effects */
563 static void make_wall_tunnel(ModeInfo *mi, tunnel_configuration *tc, float percent, float cap)
565 /* tardis is about 2x1, so wrap tex around, starting at the base*/
573 that's br=bottom right, etc. ttr is top-top-right */
575 float half_floor= 0.08333333333333333,
576 full_wall = 0.33333333333333333;
582 bl0, depth=0.3, zdepth=15.0;
583 /* zdepth is how far back tunnel goes */
584 /* depth is tex coord scale. low number = fast texture shifting */
586 float textop, texbot;
593 tr1 = r1 + half_floor;
595 tl1 = tl0 + half_floor;
600 glMatrixMode(GL_TEXTURE);
602 glRotatef(90.0, 0.0, 0.0, 1.0);
603 glTranslatef(tc->texshift[0], 0.0, 0.0);
604 glMatrixMode(GL_MODELVIEW);
606 #ifdef HAVE_GLBINDTEXTURE
608 glBindTexture(GL_TEXTURE_2D, tc->texture_binds[0]);
610 glColor3f(1.0, 1.0, 0.0);
611 if (cap > 0.0 && percent > 0.0 && drawlogo && do_fog) {
612 mi->polygon_count += 6;
613 glBegin(GL_TRIANGLE_FAN);
614 glVertex3f(0.0, 0.0, zdepth);
615 glVertex3f(-1.0, -2.0, zdepth);
616 glVertex3f(1.0, -2.0, zdepth);
617 glVertex3f(1.0, 2.0, zdepth);
618 glVertex3f(0.2, 2.0, zdepth);
619 glVertex3f(0.2, 2.2, zdepth);
620 glVertex3f(-0.2, 2.2, zdepth);
621 glVertex3f(-0.2, 2.0, zdepth);
622 glVertex3f(-1.0, 2.0, zdepth);
623 glVertex3f(-1.0, -2.0, zdepth);
626 if (percent > ( full_wall * 2.0)) {
629 height = (percent - full_wall * 2.0) /( 1.0 - full_wall * 2.0);
630 if (height > 1.0) height = 1.0;
634 mi->polygon_count += 2;
635 if ( height > 0.90) {
636 mi->polygon_count += 2;
639 textop = tr0 + half_floor * height;
640 glTexCoord2f(0.0, texbot);
641 glVertex3f(0.2, 2.2, 0.0);
643 glTexCoord2f(0.0, textop);
644 glVertex3f(2.0 - height * 2.0, 2.2, 0.0);
646 glTexCoord2f(depth, textop);
647 glVertex3f(2.0 - height * 2.0, 2.2, zdepth);
649 glTexCoord2f(depth, texbot);
650 glVertex3f(0.2, 2.2, zdepth);
653 texbot = tl1 - half_floor * height;
655 glTexCoord2f(0.0, texbot);
656 glVertex3f(-2.0 + height * 2.0, 2.2, 0.0);
658 glTexCoord2f(0.0, textop);
659 glVertex3f(-0.2, 2.2, 0.0);
661 glTexCoord2f(depth, textop);
662 glVertex3f(-0.2, 2.2, zdepth);
664 glTexCoord2f(depth, texbot);
665 glVertex3f(-2.0 + height * 2.0, 2.2, zdepth);
667 if (height > 0.90) height = 0.90;
671 textop = tr0 + half_floor * height;
672 glTexCoord2f(0.0, texbot);
673 glVertex3f(0.2, 2.0, 0.0);
675 glTexCoord2f(0.0, textop);
676 glVertex3f(0.2, 0.4 + height * 2.0, 0.0);
678 glTexCoord2f(depth, textop);
679 glVertex3f(0.2, 0.4 + height * 2.0, zdepth);
681 glTexCoord2f(depth, texbot);
682 glVertex3f(0.2, 2.0, zdepth);
685 texbot = tl1 - half_floor * height;
687 glTexCoord2f(0.0, texbot);
688 /*glVertex3f(-.2, 2.0 + (0.9 - height) * 2.0, 0.0); */
689 glVertex3f(-.2, 0.4 + height * 2.0, 0.0);
691 glTexCoord2f(0.0, textop);
692 glVertex3f(-.2, 2.0, 0.0);
694 glTexCoord2f(depth, textop);
695 glVertex3f(-.2, 2.0, zdepth);
697 glTexCoord2f(depth, texbot);
698 glVertex3f(-.2, 0.4 + height * 2.0, zdepth);
701 height = (percent - full_wall * 2.0) /( 1.0 - full_wall * 2.0);
702 if (height > 0.8) height = 0.8;
705 mi->polygon_count += 2;
708 textop = tr0 + half_floor * height;
709 glTexCoord2f(0.0, texbot);
710 glVertex3f(1.0, 2.0, 0.0);
712 glTexCoord2f(0.0, textop);
713 glVertex3f(1.0 - height, 2.0, 0.0);
715 glTexCoord2f(depth, textop);
716 glVertex3f(1.0 - height, 2.0, zdepth);
718 glTexCoord2f(depth, texbot);
719 glVertex3f(1.0, 2.0, zdepth);
722 texbot = tl1 - half_floor * height;
724 glTexCoord2f(0.0, texbot);
725 glVertex3f(-1.0 + height, 2.0, 0.0);
727 glTexCoord2f(0.0, textop);
728 glVertex3f(-1.0, 2.0, 0.0);
730 glTexCoord2f(depth, textop);
731 glVertex3f(-1.0, 2.0, zdepth);
733 glTexCoord2f(depth, texbot);
734 glVertex3f(-1.0 + height, 2.0, zdepth);
736 height = (percent - full_wall * 2.0) /( 1.0 - full_wall * 2.0);
738 if (height > 1.0) height = 1.0;
741 mi->polygon_count += 2;
744 textop = tr0 + half_floor * height;
745 glTexCoord2f(0.0, texbot);
746 glVertex3f(1.0, -2.0, 0.0);
748 glTexCoord2f(0.0, textop);
749 glVertex3f(1.0 - height, -2.0, 0.0);
751 glTexCoord2f(depth, textop);
752 glVertex3f(1.0 - height, -2.0, zdepth);
754 glTexCoord2f(depth, texbot);
755 glVertex3f(1.0, -2.0, zdepth);
758 texbot = tl1 - half_floor * height;
760 glTexCoord2f(0.0, texbot);
761 glVertex3f(-1.0 + height, -2.0, 0.0);
763 glTexCoord2f(0.0, textop);
764 glVertex3f(-1.0, -2.0, 0.0);
766 glTexCoord2f(depth, textop);
767 glVertex3f(-1.0, -2.0, zdepth);
769 glTexCoord2f(depth, texbot);
770 glVertex3f(-1.0 + height, -2.0, zdepth);
777 mi->polygon_count += 2;
779 height = percent / ( full_wall * 2.0);
780 if (height > 1.0) height = 1.0;
781 textop = (l0 + l1) / 2.0 - full_wall * 0.5 * height;
782 texbot = (l0 + l1) / 2.0 + full_wall * 0.5 * height;
784 glTexCoord2f(0.0, textop);
785 glVertex3f(-1.0, height * 2, 0.0);
787 glTexCoord2f(0.0, texbot);
788 glVertex3f(-1.0, -height * 2, 0.0);
790 glTexCoord2f(depth, texbot);
791 glVertex3f(-1.0, -height * 2, zdepth);
793 glTexCoord2f(depth, textop);
794 glVertex3f(-1.0, height * 2, zdepth);
796 textop = (r0 + r1) / 2.0 - full_wall * 0.5 * height;
797 texbot = (r0 + r1) / 2.0 + full_wall * 0.5 * height;
799 glTexCoord2f(0.0, texbot);
800 glVertex3f(1.0, height * 2, 0.0);
802 glTexCoord2f(0.0, textop);
803 glVertex3f(1.0, -height * 2, 0.0);
805 glTexCoord2f(depth, textop);
806 glVertex3f(1.0, -height * 2, zdepth);
808 glTexCoord2f(depth, texbot);
809 glVertex3f(1.0, height * 2, zdepth);
814 glMatrixMode(GL_TEXTURE);
816 glMatrixMode(GL_MODELVIEW);
817 } /* make_wall_tunnel */
819 /* wraps an int to between min and max.
820 Kind of like the remainder when devided by (max - min).
821 Used to create torus mapping on square array */
822 static int wrapVal(int val, int min, int max)
828 ret = min + (val - max ) % (max - min);
830 ret = max - (min - val) % (max - min);
834 /*=================== Load Texture =========================================*/
835 /* ripped from atunnel.c, Copyright (c) E. Lassauge, 2003-2004. */
836 /* modified like so by Sean Brennan:
837 take texture object for glbind
840 blur color / alpha channel [3x3 box filter, done [blur] times
841 anegative : create b/w image from zero alpha. zero alpha gets bw_color,
842 nonzero alpha gets 1.0 - bwcolor, then alpha flipped to 1-alpha.
846 static float mylog2(float x) { return ( log(x) / log(2));}
848 static void LoadTexture(ModeInfo * mi, char **fn, GLuint texbind, int blur, float bw_color, Bool anegative, Bool onealpha)
850 /* looping and temporary array index variables */
851 int ix, iy, bx, by, indx, indy, boxsize, cchan, tmpidx, dtaidx;
853 float boxdiv, tmpfa, blursum ;
854 unsigned char *tmpbuf, tmpa;
858 XImage *teximage; /* Texture data */
863 boxdiv = 1.0 / ( boxsize * 2.0 + 1.0) / ( boxsize * 2.0 + 1.0);
865 if ((teximage = xpm_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi),
866 MI_COLORMAP(mi), fn)) == None) {
867 fprintf(stderr, "%s: error reading the texture.\n", progname);
868 glDeleteTextures(1, &texbind);
873 /* check if image is 2^kumquat, where kumquat is an integer between 1 and 10. Recale to
874 nearest power of 2. */
875 tmpfa = mylog2((float) teximage->width);
876 bx = 2 << (int) (tmpfa -1);
877 if (bx != teximage->width) {
879 if ((tmpfa - (int) tmpfa) > 0.5849)
882 tmpfa = mylog2((float) teximage->height);
883 by = 2 << (int) (tmpfa - 1);
884 if (by != teximage->height) {
886 if ((tmpfa - (int) tmpfa) > 0.5849)
891 tmpbuf = calloc(bx * by * 4, sizeof(unsigned char));
892 if (gluScaleImage(GL_RGBA, teximage->width, teximage->height, GL_UNSIGNED_BYTE, teximage->data,
893 bx, by, GL_UNSIGNED_BYTE, tmpbuf))
894 check_gl_error("scale image");
896 free(teximage->data);
897 teximage->data = (char *) tmpbuf;
898 teximage->width = bx;
899 teximage->height= by;
901 /* end rescale code */
904 for (ix = 0 ; ix < teximage->height * teximage->width; ix++)
906 if (!teximage->data[ ix * 4 + 3]) {
907 teximage->data[ ix * 4 + 3] = (unsigned char) 0xff;
908 tmpa = (unsigned char) (bw_color * 0xff);
911 teximage->data[ ix * 4 + 3] = (unsigned char) 0xff;
913 teximage->data[ ix * 4 + 3] = (unsigned char) 0xff -
914 teximage->data[ ix * 4 + 3];
915 tmpa = (unsigned char) ((1.0 - bw_color) * 0xff);
917 /* make texture uniform b/w color */
918 teximage->data[ ix * 4 + 0] =
919 (unsigned char) ( tmpa);
920 teximage->data[ ix * 4 + 1] =
921 (unsigned char) ( tmpa);
922 teximage->data[ ix * 4 + 2] =
923 (unsigned char) ( tmpa);
929 if (! anegative ) /* anegative alread b/w's the whole image */
930 for (ix = 0 ; ix < teximage->height * teximage->width; ix++)
931 if (!teximage->data[ ix * 4 + 3])
933 teximage->data[ ix * 4 + 0] =
934 (unsigned char) ( 255.0 * bw_color);
935 teximage->data[ ix * 4 + 1] =
936 (unsigned char) ( 255.0 * bw_color);
937 teximage->data[ ix * 4 + 2] =
938 (unsigned char) ( 255.0 * bw_color);
941 tmpbuf = calloc(teximage->height * teximage->width * 4, sizeof(unsigned char) ) ;
943 /* zero out tmp alpha buffer */
944 for (iy = 0 ; iy <teximage->height * teximage->width * 4 ; iy++)
946 for (cchan = 0; cchan < 4 ; cchan++) {
947 for (iy = 0 ; iy < teximage->height ; iy++) {
948 for (ix = 0 ; ix < teximage->width ; ix++) {
949 dtaidx = (teximage->width * iy + ix) * 4;
950 tmpa = teximage->data[dtaidx + cchan];
951 tmpfa = (float) tmpa * boxdiv;
954 for (by = -boxsize ; by <= boxsize; by++) {
955 for (bx = -boxsize ; bx <= boxsize; bx++) {
956 indx = wrapVal(ix + bx, 0, teximage->width);
957 indy = wrapVal(iy + by, 0, teximage->height);
958 tmpidx = (teximage->width * indy + indx) * 4;
960 tmpbuf[tmpidx + cchan] += (unsigned char) blursum;
966 /* copy back buffer */
967 for (ix = 0 ; ix < teximage->height * teximage->width * 4; ix++)
968 teximage->data[ix] = tmpbuf[ix];
970 free(tmpbuf); /*tidy*/
977 #ifdef HAVE_GLBINDTEXTURE
978 glBindTexture(GL_TEXTURE_2D, texbind);
980 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
981 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, teximage->width, teximage->height,
982 0, GL_RGBA, GL_UNSIGNED_BYTE, teximage->data);
983 check_gl_error("texture");
985 XDestroyImage(teximage);
988 /* creates cylender for time tunnel. sides, zmin, zmax, rad(ius) obvious.
989 stretch scales texture coords; makes tunnel go slower the larger it is.
990 not drawn, but put into display list. */
991 static void makecyl(int sides, float zmin, float zmax, float rad, float stretch)
998 glBegin(GL_TRIANGLE_FAN);
999 glTexCoord2f(1.0, 0.0);
1000 glVertex3f(0.0 , 0.0 , zmax);
1001 for (i = 0 ; i <= sides; i++) {
1002 theta = 2.0 * M_PI * ((float) i / (float) sides);
1003 glVertex3f(cos(theta) * rad, sin(theta) * rad, zmax);
1005 glVertex3f(cos(0.0) * rad, sin(0.0) * rad, zmax);
1009 glBegin(GL_QUAD_STRIP);
1010 for (i = 0 ; i <= sides; i++)
1013 theta = 2.0 * M_PI * ((float) i / (float) sides);
1014 glTexCoord2f(0.0, 1.0 * (float) i / (float) sides);
1015 glVertex3f(cos(theta) * rad, sin(theta) * rad, zmin);
1016 glTexCoord2f(stretch, 1.0 * (float) i / (float) sides);
1017 glVertex3f(cos(theta) * rad, sin(theta) * rad, zmax);
1020 glTexCoord2f(0.0, 1.0);
1021 glVertex3f(cos(theta) * rad, sin(theta) * rad, zmin);
1022 glTexCoord2f(stretch, 1.0);
1023 glVertex3f(cos(theta) * rad, sin(theta) * rad, zmax);
1030 init_tunnel (ModeInfo *mi)
1034 tunnel_configuration *tc;
1036 wire = MI_IS_WIREFRAME(mi);
1039 tconf = (tunnel_configuration *)
1040 calloc (MI_NUM_SCREENS(mi), sizeof (tunnel_configuration));
1042 fprintf(stderr, "%s: out of memory\n", progname);
1046 tc = &tconf[MI_SCREEN(mi)];
1049 tc = &tconf[MI_SCREEN(mi)];
1051 tc->glx_context = init_GL(mi);
1053 tc->cyllist = glGenLists(1);
1054 tc->diamondlist = glGenLists(1);
1055 tc->num_effects = 12;
1056 tc->num_texshifts = 3;
1057 tc->effect_time = 0.0;
1058 tc->effect_maxsecs = 30.00;
1059 /* check bounds on cmd line opts */
1060 if (start > tc->effect_maxsecs) start = tc->effect_maxsecs;
1061 if (end > tc->effect_maxsecs) end = tc->effect_maxsecs;
1062 if (start < tc->effect_time) start = tc->effect_time;
1063 if (end < tc->effect_time) end = tc->effect_time;
1065 /* set loop times, in seconds */
1066 tc->start_time = start;
1069 /* reset animation knots, effect 0 not defined. */
1070 tc->effects = malloc(sizeof(effect_t) * ( tc->num_effects + 1));
1071 for ( i = 1; i <= tc->num_effects ; i++)
1072 init_effects(&tc->effects[i], i);
1075 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1081 glGenTextures(MAX_TEXTURE, tc->texture_binds);
1083 /*LoadTexture(*mi, **fn, texbind, bluralpha, bw_color, anegative, onealpha)*/
1084 LoadTexture(mi, timetunnel0_xpm, tc->texture_binds[0], 0, 0.0, False, False);
1085 LoadTexture(mi, timetunnel1_xpm, tc->texture_binds[2], 0, 0.0, False, False);
1086 LoadTexture(mi, timetunnel2_xpm, tc->texture_binds[5], 0, 0.0, False, False);
1087 LoadTexture(mi, tunnelstar_xpm, tc->texture_binds[4], 0, 0.0, False, False);
1088 # ifdef GET_SUED_BY_THE_BBC
1090 # endif /* GET_SUED_BY_THE_BBC */
1091 LoadTexture(mi, (char **) logo_180_xpm, tc->texture_binds[3], 0,0.0, False, False);
1092 tc->texture_binds[1] = tc->texture_binds[3];
1093 tc->texture_binds[6] = tc->texture_binds[3];
1094 tc->texture_binds[8] = tc->texture_binds[3];
1096 LoadTexture(mi, (char **) logo_180_xpm, tc->texture_binds[9], 2,1.0, True, True);
1097 # ifdef GET_SUED_BY_THE_BBC
1099 LoadTexture(mi, whologo_xpm, tc->texture_binds[3], 0,0.0, False, False);
1100 LoadTexture(mi, tardis_xpm, tc->texture_binds[1], 0, 0.0 ,False, False);
1101 LoadTexture(mi, whohead1_xpm, tc->texture_binds[6], 0, 1.0, False, False);
1102 /* LoadTexture(mi, whohead_psy_xpm, tc->texture_binds[8], 1, 0.7, False, False); */
1104 LoadTexture(mi, whohead1_xpm, tc->texture_binds[9], 2, 1.0, True, True);
1106 # endif /* GET_SUED_BY_THE_BBC */
1107 glEnable(GL_TEXTURE_2D);
1108 check_gl_error("tex");
1111 reshape_tunnel (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1113 glDisable(GL_DEPTH_TEST); /* who needs it? ;-) */
1120 glEnable(GL_ALPHA_TEST);
1121 glAlphaFunc(GL_GREATER, 0.5);
1124 tc->trackball = gltrackball_init ();
1127 tc->texshift = calloc(tc->num_texshifts, sizeof(GLfloat));
1128 for ( i = 0 ; i < tc->num_texshifts; i++)
1129 tc->texshift[i] = 0.0;
1131 glNewList(tc->cyllist, GL_COMPILE);
1132 makecyl(30, -0.1, CYL_LEN, 1., 10. / 40.0 * CYL_LEN);
1133 /*makecyl(30, -0.5, DIAMOND_LEN, 1., 4. / 40 * DIAMOND_LEN); */
1136 glNewList(tc->diamondlist, GL_COMPILE);
1137 makecyl(4, -0.5, DIAMOND_LEN, 1., 4. / 40 * DIAMOND_LEN);
1143 draw_tunnel (ModeInfo *mi)
1145 tunnel_configuration *tc = &tconf[MI_SCREEN(mi)];
1146 Display *dpy = MI_DISPLAY(mi);
1147 Window window = MI_WINDOW(mi);
1150 if (!tc->glx_context)
1153 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tc->glx_context));
1155 glShadeModel(GL_SMOOTH);
1157 glEnable(GL_NORMALIZE);
1158 /* glEnable(GL_CULL_FACE); */
1160 glClear(GL_COLOR_BUFFER_BIT );
1162 update_animation(tc);
1167 glRotatef(180., 0., 1., 0.);
1168 gltrackball_rotate (tc->trackball);
1169 glRotatef(180., 0., 1., 0.);
1173 mi->polygon_count = 0;
1175 update_fog(tc->effects[4].state[0], /*color*/
1176 tc->effects[4].state[1], /*density*/
1177 tc->effects[4].state[2], /*start*/
1178 tc->effects[4].state[3]); /*end*/
1180 /* --- begin composite image assembly --- */
1182 /* head mask and draw diamond tunnel */
1185 draw_cyl(mi, tc, tc->effects[6].state[0], 5, tc->diamondlist, 2);
1187 draw_sign(mi, tc,tc->effects[12].state[0], tc->effects[12].state[1], 1.0 / 1.33, 9, 1);
1188 glDisable(GL_BLEND);
1189 /* then tardis tunnel */
1190 make_wall_tunnel(mi, tc, tc->effects[1].state[0], tc->effects[7].state[0]);
1192 /* then cylender tunnel */
1194 draw_cyl(mi, tc, tc->effects[3].state[0], 2, tc->cyllist, 1);
1196 /*void draw_sign(mi, tc,z,alpha,aspect,tex,blendmode)*/
1199 draw_sign(mi, tc, tc->effects[2].state[0], tc->effects[2].state[1], 2.0, 1, 0);
1202 draw_sign(mi, tc, tc->effects[5].state[0], tc->effects[5].state[1], 1.0, 3, 0);
1205 draw_sign(mi, tc,1.0, tc->effects[10].state[0], 1.0 / 1.33, 6, 2);
1206 /*who head psychadelic REMOVED*/
1207 /* draw_sign(mi, tc,1.0, tc->effects[11].state[0], 1.0 / 1.33, 8, 0); */
1210 /* draw_sign(mi, tc, tc->effects[8].state[0]tc->effects[8].state[0], 1.0 , 1.0, 4, 1); */
1211 draw_sign(mi, tc, tc->effects[8].state[0], tc->effects[8].state[0], 1.0, 4, 1);
1215 draw_sign(mi, tc,1.0, tc->effects[9].state[0], 1.0 / 1.33, 6, 0);
1217 /* --- end composite image assembly --- */
1222 if (mi->fps_p) do_fps (mi);
1225 check_gl_error("drawing done, calling swap buffers");
1226 glXSwapBuffers(dpy, window);
1229 XSCREENSAVER_MODULE_2 ("TimeTunnel", timetunnel, tunnel)