http://www.uw-madison.lkams.kernel.org/pub/mirrors/fink/distfiles/xscreensaver-4...
[xscreensaver] / hacks / glx / glsnake.c
1 /* glsnake, Copyright (c) 2001,2002
2  * Jamie Wilkinson,    Andrew Bennetts,     Peter Aylett
3  * jaq@spacepants.org, andrew@puzzling.org, peter@ylett.com
4  *
5  * ported to xscreensaver 15th Jan 2002 by Jamie Wilkinson
6  * http://spacepants.org/src/glsnake/
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22
23 #include <X11/Intrinsic.h>
24
25 extern XtAppContext app;
26
27 #define PROGCLASS     "glsnake"
28 #define HACK_INIT     glsnake_init
29 #define HACK_DRAW     glsnake_draw
30 #define HACK_RESHAPE  glsnake_reshape
31 #define sws_opts      xlockmore_opts
32
33 #define DEF_SPEED       "0.05"
34 #define DEF_EXPLODE     "0.03"
35 #define DEF_VELOCITY    "1.0"
36 #define DEF_ACCEL       "0.1"
37 #define DEF_STATICTIME  "5000"
38 #define DEF_YSPIN       "0.10"
39 #define DEF_ZSPIN       "0.14"
40 #define DEF_SCARYCOLOUR "False"
41 #define DEF_LABELS      "True"
42 #define DEF_BBOX        "False"
43
44 #define DEFAULTS        "*delay:        30000        \n" \
45                         "*count:        30            \n" \
46                         "*showFPS:      False         \n" \
47                         "*wireframe:    False         \n" \
48                         "*speed:      " DEF_SPEED "   \n" \
49                         "*explode:    " DEF_EXPLODE " \n" \
50                         "*velocity:   " DEF_VELOCITY " \n" \
51 /*                      "*accel:      " DEF_ACCEL "   \n" */ \
52                         "*statictime: " DEF_STATICTIME " \n" \
53                         "*yspin:      " DEF_YSPIN "   \n" \
54                         "*zspin:      " DEF_ZSPIN "   \n" \
55                         "*scarycolour:" DEF_SCARYCOLOUR " \n" \
56                         "*labels:     " DEF_LABELS "  \n" \
57                         "*labelfont:  -*-times-bold-r-normal-*-180-*\n" \
58                         "*bbox:       " DEF_BBOX     "\n" \
59
60
61
62 #undef countof
63 #define countof(x) (sizeof((x))/sizeof((*x)))
64
65 #include "xlockmore.h"
66
67 #ifdef USE_GL /* whole file */
68
69 #include <GL/glu.h>
70 #include <sys/time.h>
71 #include <string.h>
72 #include <unistd.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <dirent.h>
77 #include <sys/stat.h>
78 #include <sys/types.h>
79
80 #define ZERO  0
81 #define LEFT  1
82 #define PIN   2
83 #define RIGHT 3
84
85 typedef struct model_s {
86   char * name;
87   float node[24];
88 } model_t;
89
90 typedef struct nodeang_s {
91   float cur_angle;
92   float dest_angle;
93 } nodeang_t;
94
95 typedef struct {
96   GLXContext * glx_context;
97
98   int node_list; /* name of the display list */
99   int is_cyclic;
100   int is_legal;
101   int last_turn;
102   int selected;
103   struct timeval last_iteration;
104   struct timeval last_morph;
105   int morphing;
106   nodeang_t node[24];
107   GLfloat roty;
108   GLfloat rotz;
109   int paused;
110   int dragging;
111   int m_count;
112   int m;
113   int cur_model;
114   int interactive;
115   GLfloat colour_t[3];
116   GLfloat colour_i[3];
117   GLfloat colour[3];
118   model_t * models;
119
120   XFontStruct * font;
121   GLuint font_list;
122 } glsnake_configuration;
123
124 static glsnake_configuration *glc = NULL;
125
126 static GLfloat speed;
127 static GLfloat explode;
128 static GLfloat velocity;
129 /* static GLfloat accel; */
130 static long statictime;
131 static GLfloat yspin;
132 static GLfloat zspin;
133 static Bool scarycolour;
134 static Bool labels;
135 static Bool do_bbox;
136
137 static XrmOptionDescRec opts[] = {
138   { "-speed", ".speed", XrmoptionSepArg, 0 },
139   { "-explode", ".explode", XrmoptionSepArg, 0 },
140   { "-velocity", ".velocity", XrmoptionSepArg, 0 },
141 /*  { "-accel", ".accel", XrmoptionSepArg, 0 }, */
142   { "-statictime", ".statictime", XrmoptionSepArg, 0 },
143   { "-yspin", ".yspin", XrmoptionSepArg, 0 },
144   { "-zspin", ".zspin", XrmoptionSepArg, 0 },
145   { "-scarycolour", ".scarycolour", XrmoptionNoArg, "True" },
146   { "+scarycolour", ".scarycolour", XrmoptionNoArg, "False" },
147   { "-labels", ".labels", XrmoptionNoArg, "True" },
148   { "+labels", ".labels", XrmoptionNoArg, "False" },
149 #if 0
150   { "-bbox",   ".bbox",  XrmoptionNoArg, "True" },
151   { "+bbox",   ".bbox",  XrmoptionNoArg, "False" },
152 #endif
153 };
154
155 static argtype vars[] = {
156   {(caddr_t *) &speed, "speed", "Speed", DEF_SPEED, t_Float},
157   {(caddr_t *) &explode, "explode", "Explode", DEF_EXPLODE, t_Float},
158   {(caddr_t *) &velocity, "velocity", "Velocity", DEF_VELOCITY, t_Float},
159 /*  {(caddr_t *) &accel, "accel", "Acceleration", DEF_ACCEL, t_Float}, */
160   {(caddr_t *) &statictime, "statictime", "Static Time", DEF_STATICTIME, t_Int},
161   {(caddr_t *) &yspin, "yspin", "Y Spin", DEF_YSPIN, t_Float},
162   {(caddr_t *) &zspin, "zspin", "Z Spin", DEF_ZSPIN, t_Float},
163 /*  {(caddr_t *) &interactive, "interactive", "Interactive", DEF_INTERACTIVE, t_Bool}, */
164   {(caddr_t *) &scarycolour, "scarycolour", "Scary Colour", DEF_SCARYCOLOUR, t_Bool},
165   {(caddr_t *) &labels, "labels", "Labels", DEF_LABELS, t_Bool},
166   {(caddr_t *) &do_bbox, "bbox",  "BBox",   DEF_BBOX,   t_Bool},
167 };
168
169 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
170
171 /* prism models */
172 #define VOFFSET 0.045
173 float solid_prism_v[][3] = {
174   /* first corner, bottom left front */
175   { VOFFSET, VOFFSET, 1.0 },
176   { VOFFSET, 0.0, 1.0 - VOFFSET },
177   { 0.0, VOFFSET, 1.0 - VOFFSET },
178   /* second corner, rear */
179   { VOFFSET, VOFFSET, 0.00 },
180   { VOFFSET, 0.0, VOFFSET },
181   { 0.0, VOFFSET, VOFFSET },
182   /* third, right front */
183   { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 1.0 },
184   { 1.0 - VOFFSET / M_SQRT1_2, 0.0, 1.0 - VOFFSET },
185   { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, 1.0 - VOFFSET },
186   /* fourth, right rear */
187   { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 0.0 },
188   { 1.0 - VOFFSET / M_SQRT1_2, 0.0, VOFFSET },
189   { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, VOFFSET },
190   /* fifth, upper front */
191   { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 1.0 },
192   { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, 1.0 - VOFFSET },
193   { 0.0, 1.0 - VOFFSET / M_SQRT1_2, 1.0 - VOFFSET},
194   /* sixth, upper rear */
195   { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 0.0 },
196   { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, VOFFSET },
197   { 0.0, 1.0 - VOFFSET / M_SQRT1_2, VOFFSET }
198 };
199
200 /* normals */
201 float solid_prism_n[][3] = {
202   /* corners */
203   { -VOFFSET, -VOFFSET, VOFFSET },
204   { VOFFSET, -VOFFSET, VOFFSET },
205   { -VOFFSET, VOFFSET, VOFFSET },
206   { -VOFFSET, -VOFFSET, -VOFFSET },
207   { VOFFSET, -VOFFSET, -VOFFSET },
208   { -VOFFSET, VOFFSET, -VOFFSET },
209   /* edges */
210   { -VOFFSET, 0.0, VOFFSET },
211   { 0.0, -VOFFSET, VOFFSET },
212   { VOFFSET, VOFFSET, VOFFSET },
213   { -VOFFSET, 0.0, -VOFFSET },
214   { 0.0, -VOFFSET, -VOFFSET },
215   { VOFFSET, VOFFSET, -VOFFSET },
216   { -VOFFSET, -VOFFSET, 0.0 },
217   { VOFFSET, -VOFFSET, 0.0 },
218   { -VOFFSET, VOFFSET, 0.0 },
219   /* faces */
220   { 0.0, 0.0, 1.0 },
221   { 0.0, -1.0, 0.0 },
222   { M_SQRT1_2, M_SQRT1_2, 0.0 },
223   { -1.0, 0.0, 0.0 },
224   { 0.0, 0.0, -1.0 }
225 };
226
227 /* vertices */
228 float wire_prism_v[][3] = {
229   { 0.0, 0.0, 1.0 },
230   { 1.0, 0.0, 1.0 },
231   { 0.0, 1.0, 1.0 },
232   { 0.0, 0.0, 0.0 },
233   { 1.0, 0.0, 0.0 },
234   { 0.0, 1.0, 0.0 }
235 };
236
237 /* normals */
238 float wire_prism_n[][3] = {
239   { 0.0, 0.0, 1.0},
240   { 0.0,-1.0, 0.0},
241   { M_SQRT1_2, M_SQRT1_2, 0.0},
242   {-1.0, 0.0, 0.0},
243   { 0.0, 0.0,-1.0}
244 };
245
246 /* default models */
247 #define Z   0.0
248 #define L  90.0
249 #define P 180.0
250 #define R 270.0
251
252 static model_t default_models[] = { 
253   { "Ball", 
254     { R, R, L, L, R, L, R, R, L, R, L, L, R, R, L, L, R, L, R, R, L, R, L }
255   },
256   { "Snow",
257     { R, R, R, R, L, L, L, L, R, R, R, R, L, L, L, L, R, R, R, R, L, L, L }
258   },
259   { "Propellor",
260     { Z, Z, Z, R, L, R, Z, L, Z, Z, Z, R, L, R, Z, L, Z, Z, Z, R, L, R, Z }
261   },
262   { "Flamingo",
263     { Z, P, Z, Z, Z, Z, Z, P, R, R, P, R, L, P, L, R, P, R, R, Z, Z, Z, P }
264   },
265   { "Cat",
266     { Z, P, P, Z, P, P, Z, L, Z, P, P, Z, P, P, Z, P, P, Z, Z, Z, Z, Z, Z }
267   },
268   { "Rooster",
269     { Z, Z, P, P, Z, L, Z, L, R, P, R, Z, P, P, Z, R, P, R, L, Z, L, Z, P }
270   }
271 };
272
273 /* add a model to the model list */
274 model_t * add_model(model_t * models, char * name, int * rotations, int * count)
275 {
276   int i;
277   
278   (*count)++;
279   models = realloc(models, sizeof(model_t) * (*count));
280   models[(*count)-1].name = strdup(name);
281 #ifdef DEBUG
282   fprintf(stderr, "resized models to %d bytes for model %s\n", sizeof(model_t) * (*count), models[(*count)-1].name);
283 #endif
284   for (i = 0; i < 24; i++) {
285     models[(*count)-1].node[i] = rotations[i] * 90.0;
286   }
287   return models;
288 }
289
290 /* filename is the name of the file to load
291  * models is the pointer to where the models will be kept
292  * returns a new pointer to models
293  * count is number of models read
294  */
295 model_t * load_modelfile(char * filename, model_t * models, int * count)
296 {
297   int c;
298   FILE * f;
299   char buffy[256];
300   int rotations[24];
301   int name = 1;
302   int rots = 0;
303
304   f = fopen(filename, "r");
305   if (f == NULL) {
306     int error_msg_len = strlen(filename) + 33 + 1;
307     char * error_msg = (char *) malloc(sizeof(char) * error_msg_len);
308     sprintf(error_msg, "Unable to open model data file \"%s\"", filename);
309     perror(error_msg);
310     free(error_msg);
311     return models;
312   }
313     
314   while ((c = getc(f)) != EOF) {
315     switch (c) {
316       /* ignore comments */
317       case '#':
318         while (c != '\n')
319           c = getc(f);
320         break;
321       case ':':
322         buffy[name-1] = '\0';
323         name = 0;
324         break;
325       case '\n':
326         if (rots > 0) {
327 #ifdef DEBUG
328           /* print out the model we just read in */
329           int i;
330           printf("%s: ", buffy);
331           for (i = 0; i < rots; i++) {
332             switch (rotations[i]) {
333               case LEFT:
334                 printf("L");
335                 break;
336               case RIGHT:
337                 printf("R");
338                 break;
339               case PIN:
340                 printf("P");
341                 break;
342               case ZERO:
343                 printf("Z");
344                 break;
345             }
346           }
347           printf("\n");
348 #endif
349           models = add_model(models, buffy, rotations, count);
350         }
351         name = 1;
352         rots = 0;
353         break;
354       default:
355         if (name) {
356           buffy[name-1] = c;
357           name++;
358           if (name > 255)
359             fprintf(stderr, "buffy overflow warning\n");
360         } else {
361           switch (c) {
362             case '0':
363             case 'Z':
364               rotations[rots] = ZERO;
365               rots++;
366               break;
367             case '1':
368             case 'L':
369               rotations[rots] = LEFT;
370               rots++;
371               break;
372             case '2':
373             case 'P':
374               rotations[rots] = PIN;
375               rots++;
376               break;
377             case '3':
378             case 'R':
379               rotations[rots] = RIGHT;
380               rots++;
381               break;
382             default:
383               break;
384           }
385         }
386         break;
387     }
388   }
389   return models;
390 }
391
392 model_t * load_models(char * dirpath, model_t * models, int * count)
393 {
394   char name[1024];
395   struct dirent * dp;
396   DIR * dfd;
397
398   if ((dfd = opendir(dirpath)) == NULL) {
399     if (strstr(dirpath, "data") == NULL)
400 /*      fprintf(stderr, "load_models: can't read %s/\n", dirpath); */
401     return models;
402   }
403   while ((dp = readdir(dfd)) != NULL) {
404     if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
405       continue;
406     if (strlen(dirpath) + strlen(dp->d_name) + 2 > sizeof(name))
407       fprintf(stderr, "load_models: name %s/%s too long\n", dirpath, dp->d_name);
408     else {
409       sprintf(name, "%s/%s", dirpath, dp->d_name);
410       if (strcmp(&name[(int) strlen(name) - 7], "glsnake") == 0) {
411 #ifdef DEBUG
412         fprintf(stderr, "load_models: opening %s\n", name); 
413 #endif
414         models = load_modelfile(name, models, count);
415       }
416     }
417   }
418   closedir(dfd);
419   return models;
420 }
421
422 /* snake metrics */
423 #define X_MASK 1
424 #define Y_MASK 2
425 #define Z_MASK 4
426 #define GETSCALAR(vec,mask) ((vec)==(mask) ? 1 : ((vec)==-(mask) ? -1 : 0 ))
427
428 int cross_product(int src_dir, int dest_dir)
429 {
430   return X_MASK * (GETSCALAR(src_dir, Y_MASK) * GETSCALAR(dest_dir, Z_MASK) -
431       GETSCALAR(src_dir, Z_MASK) * GETSCALAR(dest_dir, Y_MASK)) +
432     Y_MASK * (GETSCALAR(src_dir, Z_MASK) * GETSCALAR(dest_dir, X_MASK) -
433       GETSCALAR(src_dir, X_MASK) * GETSCALAR(dest_dir, Z_MASK)) +
434     Z_MASK * (GETSCALAR(src_dir, X_MASK) * GETSCALAR(dest_dir, Y_MASK) -
435       GETSCALAR(src_dir, Y_MASK) * GETSCALAR(dest_dir, X_MASK));
436 }
437
438 void calc_snake_metrics(glsnake_configuration * bp)
439 {
440   int src_dir, dest_dir;
441   int i, x, y, z;
442   int prev_src_dir = -Y_MASK;
443   int prev_dest_dir = Z_MASK;
444   int grid[25][25][25];
445
446   /* zero the grid */
447   memset(&grid, 0, sizeof(int) * 25*25*25);
448
449   bp->is_legal = 1;
450   x = y = z = 12;
451
452   /* trace path of snake and keep record for is_legal */
453   for (i = 0; i < 23; i++) {
454     /* establish new state variables */
455     src_dir = -prev_dest_dir;
456     x += GETSCALAR(prev_dest_dir, X_MASK);
457     y += GETSCALAR(prev_dest_dir, Y_MASK);
458     z += GETSCALAR(prev_dest_dir, Z_MASK);
459
460     switch ((int) (bp->node[i].dest_angle / 90.0)) {
461       case ZERO:
462         dest_dir = -prev_src_dir;
463         break;
464       case PIN:
465         dest_dir = prev_src_dir;
466         break;
467       case RIGHT:
468       case LEFT:
469         dest_dir = cross_product(prev_src_dir, prev_dest_dir);
470         if (bp->node[i].dest_angle == (int) (RIGHT * 90.0))
471           dest_dir = -dest_dir;
472         break;
473       default:
474         /* prevent spurious "might be used uninitialised" warnings */
475         dest_dir = 0;
476         break;
477     }
478
479     if (grid[x][y][z] == 0)
480       grid[x][y][z] = src_dir + dest_dir;
481     else if (grid[x][y][z] + src_dir + dest_dir == 0)
482       grid[x][y][z] = 8;
483     else
484       bp->is_legal = 0;
485
486     prev_src_dir = src_dir;
487     prev_dest_dir = dest_dir;
488   }
489
490   /* determine if the snake is cyclic */
491   bp->is_cyclic = (dest_dir == Y_MASK && x == 12 && y == 11 && x == 12);
492
493   /* determine last turn */
494   bp->last_turn = -1;
495   if (bp->is_cyclic) {
496     switch (src_dir) {
497       case -Z_MASK:
498         bp->last_turn = ZERO * 90.0;
499         break;
500       case Z_MASK:
501         bp->last_turn = PIN * 90.0;
502         break;
503       case X_MASK:
504         bp->last_turn = LEFT * 90.0;
505         break;
506       case -X_MASK:
507         bp->last_turn = RIGHT * 90.0;
508         break;
509     }
510   }
511 }
512
513 void set_colours(glsnake_configuration * bp, int immediate)
514 {
515   /* set target colour */
516   if (!bp->is_legal) {
517     bp->colour_t[0] = 0.5;
518     bp->colour_t[1] = 0.5;
519     bp->colour_t[2] = 0.5;
520   } else if (bp->is_cyclic) {
521     bp->colour_t[0] = 0.4;
522     bp->colour_t[1] = 0.8;
523     bp->colour_t[2] = 0.2;
524   } else {
525     bp->colour_t[0] = 0.3;
526     bp->colour_t[1] = 0.1;
527     bp->colour_t[2] = 0.9;
528   }
529   if (immediate) {
530     bp->colour_i[0] = bp->colour_t[0] - bp->colour[0];
531     bp->colour_i[1] = bp->colour_t[1] - bp->colour[1];
532     bp->colour_i[2] = bp->colour_t[2] - bp->colour[2];
533   } else {
534     /* instead of 50.0, I should actually work out how many times this gets
535      * called during a morph */
536     bp->colour_i[0] = (bp->colour_t[0] - bp->colour[0]) / 50.0;
537     bp->colour_i[1] = (bp->colour_t[1] - bp->colour[1]) / 50.0;
538     bp->colour_i[2] = (bp->colour_t[2] - bp->colour[2]) / 50.0;
539   }
540 }
541
542 void start_morph(int model_index, int immediate, glsnake_configuration * bp)
543 {
544   int i;
545
546   for (i = 0; i < 23; i++) {
547     bp->node[i].dest_angle = bp->models[model_index].node[i];
548     if (immediate)
549       bp->node[i].cur_angle = bp->models[model_index].node[i];
550   }
551   
552   calc_snake_metrics(bp);
553   set_colours(bp, 0);
554   bp->cur_model = model_index;
555   bp->morphing = 1;
556 }
557
558 /* convex hull */
559
560 /* model labels */
561 void draw_label(ModeInfo * mi)
562 {
563   glsnake_configuration *bp = &glc[MI_SCREEN(mi)];
564   
565   glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT);
566   glDisable(GL_LIGHTING);
567   glDisable(GL_DEPTH_TEST);
568   glMatrixMode(GL_PROJECTION);
569   glPushMatrix();
570   glLoadIdentity();
571   glMatrixMode(GL_MODELVIEW);
572   glPushMatrix();
573   glLoadIdentity();
574   gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
575   glColor3f(1.0, 1.0, 0.0);
576   {
577     char * s;
578     int i, /* w, */ l;
579
580     if (bp->interactive)
581       s = "interactive";
582     else
583       s = bp->models[bp->cur_model].name;
584
585     l = strlen(s);
586     /*
587     w = 0;
588     for (i = 0; i < l; i++) {
589       w += (bp->font->per_char 
590           ? bp->font->per_char[((int)s[i]) - bp->font->min_char_or_byte2].rbearing 
591           : bp->font->min_bounds.rbearing);
592     }
593     */
594     
595     glRasterPos2f(10, mi->xgwa.height - 10 - (bp->font->ascent + bp->font->descent));
596         /* mi->xgwa.width - w, bp->font->descent + bp->font->ascent); */
597
598     /* fprintf(stderr, "afaf.width = %d, w = %d\n", mi->xgwa.width, w); */
599     
600     for (i = 0; i < l; i++)
601       glCallList(bp->font_list + (int)s[i]);
602   }
603   glPopMatrix();
604   glMatrixMode(GL_PROJECTION);
605   glPopMatrix();
606   glPopAttrib();
607 }
608
609 /* load the fonts -- this function borrowed from molecule.c */
610 static void load_font(ModeInfo * mi, char * res, XFontStruct ** fontp, GLuint * dlistp)
611 {
612   const char * font = get_string_resource(res, "Font");
613   XFontStruct * f;
614   Font id;
615   int first, last;
616
617   if (!font)
618     font = "-*-helvetica-medium-r-*-*-*-120-*";
619
620   f = XLoadQueryFont(mi->dpy, font);
621   if (!f)
622     f = XLoadQueryFont(mi->dpy, "fixed");
623
624   id = f->fid;
625   first = f->min_char_or_byte2;
626   last = f->max_char_or_byte2;
627   
628   clear_gl_error();
629   *dlistp = glGenLists((GLuint) last + 1);
630   check_gl_error("glGenLists");
631   glXUseXFont(id, first, last - first + 1, *dlistp + first);
632   check_gl_error("glXUseXFont");
633
634   *fontp = f;
635 }
636
637
638
639 /* window management */
640 void glsnake_reshape(ModeInfo *mi, int w, int h)
641 {
642   glViewport (0, 0, (GLint) w, (GLint) h);
643   glMatrixMode(GL_PROJECTION);
644   glLoadIdentity();
645   gluPerspective(25.0, w/(GLfloat)h, 1.0, 100.0 );
646   glMatrixMode(GL_MODELVIEW);
647   glLoadIdentity();
648 }
649
650 static void gl_init(ModeInfo *mi)
651 {
652   /* glsnake_configuration *bp = &glc[MI_SCREEN(mi)]; */
653   int wire = MI_IS_WIREFRAME(mi);
654   float light_pos[][3] = {{0.0,0.0,20.0},{0.0,20.0,0.0}};
655   float light_dir[][3] = {{0.0,0.0,-20.0},{0.0,-20.0,0.0}};
656
657   glClearColor(0.0, 0.0, 0.0, 0.0);
658   glEnable(GL_DEPTH_TEST);
659   glShadeModel(GL_SMOOTH);
660   glCullFace(GL_BACK);
661   glEnable(GL_CULL_FACE);
662   glEnable(GL_NORMALIZE);
663
664   if (!wire) {
665     glColor3f(1.0, 1.0, 1.0);
666     glLightfv(GL_LIGHT0, GL_POSITION, light_pos[0]);
667     glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_dir[0]);
668     glLightfv(GL_LIGHT1, GL_POSITION, light_pos[1]);
669     glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir[1]);
670     glEnable(GL_LIGHTING);
671     glEnable(GL_LIGHT0);
672     glEnable(GL_LIGHT1);
673     glEnable(GL_COLOR_MATERIAL);
674   }
675 }
676
677 /* lifted from lament.c */
678 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
679 #define RANDSIGN() ((random() & 1) ? 1 : -1)
680
681 void glsnake_init(ModeInfo *mi)
682 {
683   glsnake_configuration * bp;
684   int wire = MI_IS_WIREFRAME(mi);
685
686   if (!glc) {
687     glc = (glsnake_configuration *) calloc(MI_NUM_SCREENS(mi), sizeof(glsnake_configuration));
688     if (!glc) {
689       fprintf(stderr, "%s: out of memory\n", progname);
690       exit(1);
691     }
692     bp = &glc[MI_SCREEN(mi)];
693   }
694
695   bp = &glc[MI_SCREEN(mi)];
696
697   if ((bp->glx_context = init_GL(mi)) != NULL) {
698     gl_init(mi);
699     glsnake_reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
700   }
701
702   /* initialise config variables */
703   memset(&bp->node, 0, sizeof(nodeang_t) * 24);
704   bp->m_count = sizeof(default_models) / sizeof(model_t); /* overwrite this in a bit */
705   bp->selected = 11;
706   bp->is_cyclic = 0;
707   bp->is_legal = 1;
708   bp->last_turn = -1;
709   bp->roty = 0.0;
710   bp->rotz = 0.0;
711   bp->morphing = 0;
712   bp->paused = 0;
713   bp->dragging = 0;
714   bp->interactive = 0;
715
716         {
717 # ifdef GETTIMEOFDAY_TWO_ARGS
718           struct timezone tzp;
719           gettimeofday(&bp->last_iteration, &tzp);
720 # else
721           gettimeofday(&bp->last_iteration);
722 # endif
723         }
724
725   memcpy(&bp->last_morph, &(bp->last_iteration),
726                sizeof(bp->last_morph));
727   /* srand((unsigned int) bp->last_iteration.time); */
728
729   /* load the model files */
730   /* first copy the defaults to bp->m_count */
731   bp->models = (model_t *) malloc(sizeof(model_t) * bp->m_count);
732   memcpy(bp->models, default_models, bp->m_count * sizeof(model_t));
733   /* then add on models from the Debian model file location */
734   bp->models = load_models("/usr/share/glsnake", bp->models, &(bp->m_count));
735
736   bp->m = bp->cur_model = RAND(bp->m_count);
737   start_morph(bp->cur_model, 1, bp);
738
739   calc_snake_metrics(bp);
740   set_colours(bp, 1);
741
742   /* set up a font for the labels */
743   if (labels)
744     load_font(mi, "labelfont", &bp->font, &bp->font_list);
745   
746   bp->node_list = glGenLists(1);
747   glNewList(bp->node_list, GL_COMPILE);
748   if (!wire) {
749     /* corners */
750     glBegin(GL_TRIANGLES);
751     glNormal3fv(solid_prism_n[0]);
752     glVertex3fv(solid_prism_v[0]);
753     glVertex3fv(solid_prism_v[2]);
754     glVertex3fv(solid_prism_v[1]);
755     
756     glNormal3fv(solid_prism_n[1]);
757     glVertex3fv(solid_prism_v[6]);
758     glVertex3fv(solid_prism_v[7]);
759     glVertex3fv(solid_prism_v[8]);
760
761     glNormal3fv(solid_prism_n[2]);
762     glVertex3fv(solid_prism_v[12]);
763     glVertex3fv(solid_prism_v[13]);
764     glVertex3fv(solid_prism_v[14]);
765     
766     glNormal3fv(solid_prism_n[3]);
767     glVertex3fv(solid_prism_v[3]);
768     glVertex3fv(solid_prism_v[4]);
769     glVertex3fv(solid_prism_v[5]);
770   
771     glNormal3fv(solid_prism_n[4]);
772     glVertex3fv(solid_prism_v[9]);
773     glVertex3fv(solid_prism_v[11]);
774     glVertex3fv(solid_prism_v[10]);
775
776     glNormal3fv(solid_prism_n[5]);
777     glVertex3fv(solid_prism_v[16]);
778     glVertex3fv(solid_prism_v[15]);
779     glVertex3fv(solid_prism_v[17]);
780     glEnd();
781
782     /* edges */
783     glBegin(GL_QUADS);
784     glNormal3fv(solid_prism_n[6]);
785     glVertex3fv(solid_prism_v[0]);
786     glVertex3fv(solid_prism_v[12]);
787     glVertex3fv(solid_prism_v[14]);
788     glVertex3fv(solid_prism_v[2]);
789   
790     glNormal3fv(solid_prism_n[7]);
791     glVertex3fv(solid_prism_v[0]);
792     glVertex3fv(solid_prism_v[1]);
793     glVertex3fv(solid_prism_v[7]);
794     glVertex3fv(solid_prism_v[6]);
795   
796     glNormal3fv(solid_prism_n[8]);
797     glVertex3fv(solid_prism_v[6]);
798     glVertex3fv(solid_prism_v[8]);
799     glVertex3fv(solid_prism_v[13]);
800     glVertex3fv(solid_prism_v[12]);
801   
802     glNormal3fv(solid_prism_n[9]);
803     glVertex3fv(solid_prism_v[3]);
804     glVertex3fv(solid_prism_v[5]);
805     glVertex3fv(solid_prism_v[17]);
806     glVertex3fv(solid_prism_v[15]);
807   
808     glNormal3fv(solid_prism_n[10]);
809     glVertex3fv(solid_prism_v[3]);
810     glVertex3fv(solid_prism_v[9]);
811     glVertex3fv(solid_prism_v[10]);
812     glVertex3fv(solid_prism_v[4]);
813   
814     glNormal3fv(solid_prism_n[11]);
815     glVertex3fv(solid_prism_v[15]);
816     glVertex3fv(solid_prism_v[16]);
817     glVertex3fv(solid_prism_v[11]);
818     glVertex3fv(solid_prism_v[9]);
819   
820     glNormal3fv(solid_prism_n[12]);
821     glVertex3fv(solid_prism_v[1]);
822     glVertex3fv(solid_prism_v[2]);
823     glVertex3fv(solid_prism_v[5]);
824     glVertex3fv(solid_prism_v[4]);
825   
826     glNormal3fv(solid_prism_n[13]);
827     glVertex3fv(solid_prism_v[8]);
828     glVertex3fv(solid_prism_v[7]);
829     glVertex3fv(solid_prism_v[10]);
830     glVertex3fv(solid_prism_v[11]);
831   
832     glNormal3fv(solid_prism_n[14]);
833     glVertex3fv(solid_prism_v[13]);
834     glVertex3fv(solid_prism_v[16]);
835     glVertex3fv(solid_prism_v[17]);
836     glVertex3fv(solid_prism_v[14]);
837     glEnd();
838   
839     /* faces */
840     glBegin(GL_TRIANGLES);
841     glNormal3fv(solid_prism_n[15]);
842     glVertex3fv(solid_prism_v[0]);
843     glVertex3fv(solid_prism_v[6]);
844     glVertex3fv(solid_prism_v[12]);
845   
846     glNormal3fv(solid_prism_n[19]);
847     glVertex3fv(solid_prism_v[3]);
848     glVertex3fv(solid_prism_v[15]);
849     glVertex3fv(solid_prism_v[9]);
850     glEnd();
851   
852     glBegin(GL_QUADS);
853     glNormal3fv(solid_prism_n[16]);
854     glVertex3fv(solid_prism_v[1]);
855     glVertex3fv(solid_prism_v[4]);
856     glVertex3fv(solid_prism_v[10]);
857     glVertex3fv(solid_prism_v[7]);
858   
859     glNormal3fv(solid_prism_n[17]);
860     glVertex3fv(solid_prism_v[8]);
861     glVertex3fv(solid_prism_v[11]);
862     glVertex3fv(solid_prism_v[16]);
863     glVertex3fv(solid_prism_v[13]);
864   
865     glNormal3fv(solid_prism_n[18]);
866     glVertex3fv(solid_prism_v[2]);
867     glVertex3fv(solid_prism_v[14]);
868     glVertex3fv(solid_prism_v[17]);
869     glVertex3fv(solid_prism_v[5]);
870     glEnd();
871   } else {
872     /* build wire display list */
873     glBegin(GL_LINE_STRIP);
874     glVertex3fv(wire_prism_v[0]);
875     glVertex3fv(wire_prism_v[1]);
876     glVertex3fv(wire_prism_v[2]);
877     glVertex3fv(wire_prism_v[0]);
878     glVertex3fv(wire_prism_v[3]);
879     glVertex3fv(wire_prism_v[4]);
880     glVertex3fv(wire_prism_v[5]);
881     glVertex3fv(wire_prism_v[3]);
882     glEnd();
883   
884     glBegin(GL_LINES);
885     glVertex3fv(wire_prism_v[1]);
886     glVertex3fv(wire_prism_v[4]);
887     glVertex3fv(wire_prism_v[2]);
888     glVertex3fv(wire_prism_v[5]);
889     glEnd();
890   }
891   glEndList();
892 }
893
894 /* "jwz?  no way man, he's my idle" -- Jaq, 2001.
895  * I forget the context :( */
896 void glsnake_idol(glsnake_configuration * bp)
897 {
898   /* time since last iteration */
899   long iter_msec;
900   /* time since the beginning of last morph */
901   long morf_msec;
902   float iter_angle_max;
903   int i;
904   struct timeval current_time;
905   int still_morphing;
906
907   /* Do nothing to the model if we are paused */
908   if (bp->paused) {
909     /* Avoid busy waiting when nothing is changing */
910     usleep(1);
911     return;
912   }
913
914         {
915 # ifdef GETTIMEOFDAY_TWO_ARGS
916           struct timezone tzp;
917           gettimeofday(&current_time, &tzp);
918 # else
919           gettimeofday(&current_time);
920 # endif
921         }
922
923   /* <spiv> Well, ftime gives time with millisecond resolution.
924    * <Jaq> if current time is exactly equal to last iteration, 
925    *       then don't do this block
926    * <spiv> (or worse, perhaps... who knows what the OS will do)
927    * <spiv> So if no discernable amount of time has passed:
928    * <spiv>   a) There's no point updating the screen, because
929    *             it would be the same
930    * <spiv>   b) The code will divide by zero
931    */
932   iter_msec = ((long) current_time.tv_usec - bp->last_iteration.tv_usec)/1000L + 
933         ((long) current_time.tv_sec - bp->last_iteration.tv_sec) * 1000L;
934   if (iter_msec) {
935     /* save the current time */
936     memcpy(&bp->last_iteration, &current_time,
937                        sizeof(bp->last_iteration));
938     
939     /* work out if we have to switch models */
940     morf_msec = (bp->last_iteration.tv_usec - bp->last_morph.tv_usec)/1000L +
941       ((long) (bp->last_iteration.tv_sec - bp->last_morph.tv_sec) * 1000L);
942
943     if ((morf_msec > statictime) && !bp->interactive) {
944       memcpy(&bp->last_morph, &(bp->last_iteration),
945                                sizeof(bp->last_morph));
946       start_morph(RAND(bp->m_count), 0, bp);
947     }
948
949     if (bp->interactive && !bp->morphing) {
950       usleep(1);
951       return;
952     }
953
954     if (!bp->dragging && !bp->interactive) {
955       bp->roty += 360/((1000/yspin)/iter_msec);
956       bp->rotz += 360/((1000/zspin)/iter_msec);
957     }
958
959     /* work out the maximum angle for this iteration */
960     iter_angle_max = 90.0 * (velocity/1000.0) * iter_msec;
961
962     still_morphing = 0;
963     for (i = 0; i < 24; i++) {
964       float cur_angle = bp->node[i].cur_angle;
965       float dest_angle = bp->node[i].dest_angle;
966       if (cur_angle != dest_angle) {
967         still_morphing = 1;
968         if (fabs(cur_angle - dest_angle) <= iter_angle_max)
969           bp->node[i].cur_angle = dest_angle;
970         else if (fmod(cur_angle - dest_angle + 360, 360) > 180)
971           bp->node[i].cur_angle = fmod(cur_angle + iter_angle_max, 360);
972         else
973           bp->node[i].cur_angle = fmod(cur_angle + 360 - iter_angle_max, 360);
974       }
975     }
976
977     if (!still_morphing)
978       bp->morphing = 0;
979
980     /* colour cycling */
981     if (fabs(bp->colour[0] - bp->colour_t[0]) <= fabs(bp->colour_i[0]))
982       bp->colour[0] = bp->colour_t[0];
983     else
984       bp->colour[0] += bp->colour_i[0];
985     if (fabs(bp->colour[1] - bp->colour_t[1]) <= fabs(bp->colour_i[1]))
986       bp->colour[1] = bp->colour_t[1];
987     else
988       bp->colour[1] += bp->colour_i[1];
989     if (fabs(bp->colour[2] - bp->colour_t[2]) <= fabs(bp->colour_i[2]))
990       bp->colour[2] = bp->colour_t[2];
991     else
992       bp->colour[2] += bp->colour_i[2];
993   } else {
994     /* We are going too fast, so we may as well let the 
995      * cpu relax a little by sleeping for a millisecond. */
996     usleep(1);
997   }
998 }
999
1000 static void
1001 snake_bounding_box (ModeInfo *mi,
1002                     GLfloat *x1, GLfloat *y1, GLfloat *z1,
1003                     GLfloat *x2, GLfloat *y2, GLfloat *z2)
1004 {
1005   glsnake_configuration *bp = &glc[MI_SCREEN(mi)];
1006   int i;
1007   GLdouble identity[16] = { 1, 0, 0, 0,
1008                             0, 1, 0, 0,
1009                             0, 0, 1, 0,
1010                             0, 0, 0, 1 };
1011   GLint vp[4] = { 0, 0, 1, 1 };
1012   *x1 = *x2 = 0;
1013   *y1 = *y2 = 0;
1014   *z1 = *z2 = 0;
1015
1016   glPushMatrix();
1017   glLoadIdentity();
1018   for (i = 0; i < 24; i++)
1019     {
1020       GLdouble model[16];
1021       GLdouble x, y, z;
1022       GLfloat ang = bp->node[i].cur_angle;
1023
1024       glGetDoublev (GL_MODELVIEW_MATRIX, model);
1025       gluProject (0, 0, 0, model, identity, vp, &x, &y, &z);
1026 fprintf (stderr, "%2d: %5.2f %5.2f %5.2f\n", i, (float)x, (float)y, (float)z);
1027
1028       if      (x < *x1) *x1 = x;
1029       else if (x > *x2) *x2 = x;
1030       if      (y < *y1) *y1 = y;
1031       else if (y > *y2) *y2 = y;
1032       if      (z < *z1) *z1 = z;
1033       else if (z > *z2) *z2 = z;
1034
1035       glTranslatef(0.5, 0.5, 0.5);
1036       glRotatef(90, 0.0, 0.0, -1.0);
1037       glTranslatef(1.0 + explode, 0.0, 0.0);
1038       glRotatef(180 + ang, 1.0, 0.0, 0.0);
1039       glTranslatef(-0.5, -0.5, -0.5);
1040     }
1041 fprintf(stderr, "\n");
1042   glPopMatrix();
1043
1044 #if 0
1045   *x1 -= 1;
1046   *y1 -= 1;
1047   *z1 -= 1;
1048   *x2 += 1;
1049   *y2 += 1;
1050   *z2 += 1;
1051 #endif
1052 }
1053
1054
1055 static void
1056 draw_bounding_box (ModeInfo *mi)
1057 {
1058   static GLfloat c1[4] = { 0.4, 0.4, 0.4, 1.0 };
1059   static GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 };
1060   int wire = MI_IS_WIREFRAME(mi);
1061   GLfloat x1, y1, z1, x2, y2, z2;
1062   snake_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2);
1063
1064   glColor3f (c1[0], c1[1], c1[2]);
1065 /*  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c1);*/
1066   glFrontFace(GL_CCW);
1067
1068   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
1069   glNormal3f(0, 1, 0);
1070   glVertex3f(x1, y1, z1); glVertex3f(x1, y1, z2);
1071   glVertex3f(x2, y1, z2); glVertex3f(x2, y1, z1);
1072   glEnd();
1073   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
1074   glNormal3f(0, -1, 0);
1075   glVertex3f(x2, y2, z1); glVertex3f(x2, y2, z2);
1076   glVertex3f(x1, y2, z2); glVertex3f(x1, y2, z1);
1077   glEnd();
1078   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
1079   glNormal3f(0, 0, 1);
1080   glVertex3f(x1, y1, z1); glVertex3f(x2, y1, z1);
1081   glVertex3f(x2, y2, z1); glVertex3f(x1, y2, z1);
1082   glEnd();
1083   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
1084   glNormal3f(0, 0, -1);
1085   glVertex3f(x1, y2, z2); glVertex3f(x2, y2, z2);
1086   glVertex3f(x2, y1, z2); glVertex3f(x1, y1, z2);
1087   glEnd();
1088   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
1089   glNormal3f(1, 0, 0);
1090   glVertex3f(x1, y2, z1); glVertex3f(x1, y2, z2);
1091   glVertex3f(x1, y1, z2); glVertex3f(x1, y1, z1);
1092   glEnd();
1093   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
1094   glNormal3f(-1, 0, 0);
1095   glVertex3f(x2, y1, z1); glVertex3f(x2, y1, z2);
1096   glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1);
1097   glEnd();
1098
1099   glPushAttrib (GL_LIGHTING);
1100   glDisable (GL_LIGHTING);
1101
1102   glColor3f (c2[0], c2[1], c2[2]);
1103   glBegin(GL_LINES);
1104   if (x1 > 0) x1 = 0; if (x2 < 0) x2 = 0;
1105   if (y1 > 0) y1 = 0; if (y2 < 0) y2 = 0;
1106   if (z1 > 0) z1 = 0; if (z2 < 0) z2 = 0;
1107   glVertex3f(x1, 0,  0);  glVertex3f(x2, 0,  0); 
1108   glVertex3f(0 , y1, 0);  glVertex3f(0,  y2, 0); 
1109   glVertex3f(0,  0,  z1); glVertex3f(0,  0,  z2); 
1110   glEnd();
1111
1112   glPopAttrib();
1113 }
1114
1115
1116 void glsnake_draw(ModeInfo *mi)
1117 {
1118   glsnake_configuration *bp = &glc[MI_SCREEN(mi)];
1119   Display *dpy = MI_DISPLAY(mi);
1120   Window window = MI_WINDOW(mi);
1121
1122   int i;
1123   float ang;
1124
1125   if (!bp->glx_context)
1126       return;
1127
1128   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1129
1130   glMatrixMode(GL_MODELVIEW);
1131   glLoadIdentity();
1132   gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
1133
1134   /* rotate and translate into snake space */
1135   glRotatef(45.0, -5.0, 0.0, 1.0);
1136   glTranslatef(-0.5, 0.0, 0.5);
1137
1138   /* rotate the 0th junction */
1139   glTranslatef(0.5, 0.0, 0.5);
1140   /* glMultMatrix(rotation); -- quaternion rotation */
1141   glRotatef(bp->roty, 0.0, 1.0, 0.0);
1142   glRotatef(bp->rotz, 0.0, 0.0, 1.0);
1143   glTranslated(-0.5, 0.0, -0.5);
1144
1145   /* translate middle node to centre */
1146   for (i = 11; i >= 0; i--) {
1147     ang = bp->node[i].cur_angle;
1148     glTranslatef(0.5, 0.5, 0.5);
1149     glRotatef(180+ang, -1.0, 0.0, 0.0);
1150     glTranslatef(-1.0 - explode, 0.0, 0.0);
1151     glRotatef(90, 0.0, 0.0, 1.0);
1152     glTranslatef(-0.5, -0.5, -0.5);
1153   }
1154
1155   /* now draw each node along the snake */
1156   glPushMatrix();
1157   for (i = 0; i < 24; i++) {
1158
1159     /* choose a colour for this node */
1160     if (bp->interactive && (i == bp->selected || i == bp->selected+1))
1161       glColor3f(1.0, 1.0, 0.0);
1162     else {
1163       if (i % 2) {
1164         if (scarycolour)
1165           glColor3f(0.6, 0.0, 0.9);
1166         else
1167           glColor3fv(bp->colour);
1168       } else {
1169         if (scarycolour)
1170           glColor3f(0.2, 0.9, 1.0);
1171         else
1172           glColor3f(1.0, 1.0, 1.0);
1173       }
1174     }
1175
1176 #if 0 /* #### */ 
1177     if (i == 0) glColor3f(0.0, 1.0, 1.0);
1178 #endif
1179
1180     /* draw the node */
1181     glCallList(bp->node_list);
1182
1183     /* now work out where to draw the next one */
1184
1185     /* interpolate between models */
1186     ang = bp->node[i].cur_angle;
1187
1188     glTranslatef(0.5, 0.5, 0.5);
1189     glRotatef(90, 0.0, 0.0, -1.0);
1190     glTranslatef(1.0 + explode, 0.0, 0.0);
1191     glRotatef(180 + ang, 1.0, 0.0, 0.0);
1192     glTranslatef(-0.5, -0.5, -0.5);
1193   }
1194
1195   glPopMatrix();
1196
1197   if (do_bbox)
1198     draw_bounding_box (mi);
1199
1200   if (labels)
1201     draw_label(mi);
1202   
1203   if (mi->fps_p)
1204     do_fps (mi);
1205
1206   glsnake_idol(bp);
1207   
1208   glFlush();
1209   glXSwapBuffers(dpy, window);
1210 }
1211
1212 #endif /* USE_GL */