http://svn.poeml.de/viewvc/ppc/src-unpacked/xscreensaver/xscreensaver-4.12.tar.bz2...
[xscreensaver] / hacks / glx / jigglypuff.c
1 /* jigglypuff - a most, most, unfortunate screensaver.
2  *
3  * Copyright (c) 2003 Keith Macleod (kmacleod@primus.ca)
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation.  No representations are made about the suitability of this
10  * software for any purpose.  It is provided "as is" without express or 
11  * implied warranty.
12  *
13  * Draws all varieties of obscene, spastic, puffy balls
14  * orbiting lazily about the screen. More of an accident
15  * than anything else.
16  *
17  * Apologies to anyone who thought they were getting a Pokemon
18  * out of this.
19  *
20  * Of course, if you modify it to do something interesting and/or
21  * funny, I'd appreciate receiving a copy.
22  *
23  * 04/06/2003 - Oops, figured out what was wrong with the sphere
24  *              mapping. I had assumed it was done in model space,
25  *              but of course I was totally wrong... Eye space you
26  *              say? Yup. km
27  *
28  * 03/31/2003 - Added chrome to the color options. The mapping
29  *              is anything but 'correct', but it's a pretty good
30  *              effect anyways, as long as the surface is jiggling
31  *              enough that you can't tell. Sure, it seems  kind of odd
32  *              that it's reflecting a sky that's obviously not there,
33  *              but who has time to worry about silly details like
34  *              that? Not me, ah rekkin'. km
35  *
36  */
37
38 #include <X11/Intrinsic.h>
39
40 #ifdef STANDALONE
41 # define PROGCLASS          "Jigglypuff"
42 # define HACK_INIT          init_jigglypuff
43 # define HACK_DRAW          draw_jigglypuff
44 # define HACK_RESHAPE       reshape_jigglypuff
45 # define HACK_HANDLE_EVENT  jigglypuff_handle_event
46 # define EVENT_MASK         PointerMotionMask 
47 # define jigglypuff_opts    xlockmore_opts
48
49 #define DEF_COLOR           "cycle"
50 #define DEF_SHININESS       "100"
51 #define DEF_COMPLEXITY      "2"
52 #define DEF_SPEED           "500"
53 #define DEF_DISTANCE        "100"
54 #define DEF_HOLD            "800"
55 #define DEF_SPHERISM        "75"
56 #define DEF_DAMPING         "500"
57
58 # define DEFAULTS           "*delay: 20000\n" \
59                             "*showFPS: False\n" \
60                             "*wireframe: False\n" \
61                             "*color: cycle\n" \
62                             "*shininess: 100\n" \
63                             "*complexity: 2\n" \
64                             "*speed: 500\n" \
65                             "*distance: 100\n" \
66                             "*hold: 800\n" \
67                             "*spherism: 200\n" \
68                             "*damping: 500\n"
69
70
71 # include "xlockmore.h"
72 #else
73 # include "xlock.h"
74 #endif
75
76 #ifdef HAVE_CONFIG_H
77 # include "config.h"
78 #endif
79
80 #include "xpm-ximage.h"
81 #include "gltrackball.h"
82 #include "../images/jigglymap.xpm"
83
84 #ifdef USE_GL
85 #include <GL/gl.h>
86
87 #ifndef max
88 #define max(a,b) (((a)>(b))?(a):(b))
89 #define min(a,b) (((a)<(b))?(a):(b))
90 #endif
91
92 /* Why isn't RAND_MAX correct in the first place? */
93 #define REAL_RAND_MAX (2.0*(float)RAND_MAX)
94
95 static int spherism;
96 static int hold;
97 static int distance;
98 static int damping;
99
100 static int complexity;
101 static int speed;
102
103 static int do_tetrahedron;
104 static int spooky;
105 static char *color;
106 static int shininess;
107
108 static int random_parms;
109
110 typedef struct solid solid;
111
112 typedef struct {
113     float stable_distance;
114     float hold_strength;
115     float spherify_strength;
116     float damping_velocity;
117     float damping_factor;
118
119     int do_wireframe;
120     int spooky;
121     int color_style;
122     GLint shininess;
123     GLfloat jiggly_color[4];
124     GLfloat color_dir[3];
125
126     solid *shape;
127
128     trackball_state *trackball;
129     int button_down;
130
131     float angle;
132     float axis;
133     float speed;
134
135     GLXContext *glx_context;
136 } jigglystruct;
137
138 static jigglystruct *jss = NULL;
139
140 static XrmOptionDescRec opts[] = {
141     {"-random", ".Jigglypuff.random", XrmoptionNoArg, (caddr_t)"true"},
142     {"+random", ".Jigglypuff.random", XrmoptionNoArg, (caddr_t)"false"},
143     {"-tetra", ".Jigglypuff.tetra", XrmoptionNoArg, (caddr_t)"true"},
144     {"+tetra", ".Jigglypuff.tetra", XrmoptionNoArg, (caddr_t)"false"},
145     {"-spooky", ".Jigglypuff.spooky", XrmoptionSepArg, (caddr_t)"0"},
146     {"-color", ".Jigglypuff.color", XrmoptionSepArg, (caddr_t)DEF_COLOR},
147     {"-shininess", ".Jigglypuff.shininess", XrmoptionSepArg, (caddr_t)DEF_SHININESS},
148     {"-complexity", ".Jigglypuff.complexity", XrmoptionSepArg, (caddr_t)DEF_COMPLEXITY},
149     {"-speed", ".Jigglypuff.speed", XrmoptionSepArg, (caddr_t)DEF_SPEED},
150     {"-spherism", ".Jigglypuff.spherism", XrmoptionSepArg, (caddr_t)DEF_SPHERISM},
151     {"-hold", ".Jigglypuff.hold", XrmoptionSepArg, (caddr_t)DEF_HOLD},
152     {"-distance", "Jigglypuff.distance", XrmoptionSepArg, (caddr_t)DEF_DISTANCE},
153     {"-damping", "Jigglypuff.damping", XrmoptionSepArg, (caddr_t)DEF_DAMPING}
154 };
155
156 static argtype vars[] = {
157     {(caddr_t*)&random_parms, "random", "Random", "False", t_Bool},
158     {(caddr_t*)&do_tetrahedron, "tetra", "Tetra", "False", t_Bool},
159     {(caddr_t*)&spooky, "spooky", "Spooky", "0", t_Int},
160     {(caddr_t*)&color, "color", "Color", DEF_COLOR, t_String},
161     {(caddr_t*)&shininess, "shininess", "Shininess", DEF_SHININESS, t_Int},
162     {(caddr_t*)&complexity, "complexity", "Complexity", DEF_COMPLEXITY, t_Int},
163     {(caddr_t*)&speed, "speed", "Speed", DEF_SPEED, t_Int},
164     {(caddr_t*)&spherism, "spherism", "Spherism", DEF_SPHERISM, t_Int},
165     {(caddr_t*)&hold, "hold", "Hold", DEF_HOLD, t_Int},
166     {(caddr_t*)&distance, "distance", "Distance", DEF_DISTANCE, t_Int},
167     {(caddr_t*)&damping, "damping", "Damping", DEF_DAMPING, t_Int}
168 };
169
170 #undef countof
171 #define countof(x) ((int)(sizeof(x)/sizeof(*(x))))
172
173 ModeSpecOpt jigglypuff_opts = {countof(opts), opts, countof(vars), vars, NULL};
174
175 #define COLOR_STYLE_NORMAL    0
176 #define COLOR_STYLE_CYCLE     1
177 #define COLOR_STYLE_CLOWNBARF 2
178 #define COLOR_STYLE_FLOWERBOX 3
179 #define COLOR_STYLE_CHROME    4
180
181 #define CLOWNBARF_NCOLORS 5
182
183 static GLfloat clownbarf_colors[CLOWNBARF_NCOLORS][4] = {
184     {0.7, 0.7, 0.0, 1.0},
185     {0.8, 0.1, 0.1, 1.0},
186     {0.1, 0.1, 0.8, 1.0},
187     {0.9, 0.9, 0.9, 1.0},
188     {0.0, 0.0, 0.0, 1.0}
189 };
190
191 static GLfloat flowerbox_colors[4][4] = {
192     {0.7, 0.7, 0.0, 1.0},
193     {0.9, 0.0, 0.0, 1.0},
194     {0.0, 0.9, 0.0, 1.0},
195     {0.0, 0.0, 0.9, 1.0},
196 };
197
198 # if 0 /* I am not even going to *try* and make this bullshit compile
199           without warning under gcc -std=c89 -pedantic.  -jwz. */
200 #ifdef DEBUG
201 # ifdef __GNUC__ /* GCC style */
202 #define _DEBUG(msg, args...) do { \
203     fprintf(stderr, "%s : %d : " msg ,__FILE__,__LINE__ ,##args); \
204 } while(0)
205 # else /* C99 standard style */
206 #define _DEBUG(msg, ...) do { \
207     fprintf(stderr, "%s : %d : " msg ,__FILE__,__LINE__,__VA_ARGS__); \
208 } while(0)
209 # endif
210 #else
211 # ifdef __GNUC__ /* GCC style */
212 #define _DEBUG(msg, args...)
213 # else /* C99 standard style */
214 #define _DEBUG(msg, ...)
215 # endif
216 #endif
217 #endif /* 0 */
218
219 /* This is all the half-edge b-rep code (as well as basic geometry) */
220 typedef struct face face;
221 typedef struct edge edge;
222 typedef struct hedge hedge;
223 typedef struct vertex vertex;
224 typedef GLfloat coord;
225 typedef coord vector[3];
226
227 struct solid {
228     face *faces;
229     edge *edges;
230     vertex *vertices;
231 };
232
233 struct face {
234     solid *s;
235     hedge *start;
236     GLfloat *color;
237     face *next;
238 };
239
240 struct edge {
241     solid *s;
242     hedge *left;
243     hedge *right;
244     edge *next;
245 };
246
247 struct hedge {
248     face *f;
249     edge *e;
250     vertex *vtx;
251     hedge *next;
252     hedge *prev;
253 };
254
255 struct vertex {
256     solid *s;
257     hedge *h;
258     vector v;
259     vector n;
260     vector f;
261     vector vel;
262     vertex *next;
263 };
264
265 static inline void vector_init(vector v, coord x, coord y, coord z)
266 {
267     v[0] = x;
268     v[1] = y;
269     v[2] = z;
270 }    
271
272 static inline void vector_copy(vector d, vector s)
273 {
274     d[0] = s[0];
275     d[1] = s[1];
276     d[2] = s[2];
277 }
278
279 static inline void vector_add(vector v1, vector v2, vector v)
280 {
281     vector_init(v, v1[0]+v2[0], v1[1]+v2[1], v1[2]+v2[2]);
282 }
283
284 static inline void vector_add_to(vector v1, vector v2)
285 {
286     v1[0] += v2[0];
287     v1[1] += v2[1];
288     v1[2] += v2[2];
289 }
290
291 static inline void vector_sub(vector v1, vector v2, vector v)
292 {
293     vector_init(v, v1[0]-v2[0], v1[1]-v2[1], v1[2]-v2[2]);
294 }
295
296 static inline void vector_scale(vector v, coord s)
297 {
298     v[0] *= s;
299     v[1] *= s;
300     v[2] *= s;
301 }
302
303 static inline coord dot(vector v1, vector v2)
304 {
305     return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
306 }
307
308 static inline void cross(vector v1, vector v2, vector v)
309 {
310     vector_init(v,
311                 v1[1]*v2[2] - v2[1]*v1[2],
312                 v1[2]*v2[0] - v2[2]*v1[0],
313                 v1[0]*v2[1] - v2[0]*v1[1]);
314 }
315
316 static inline coord magnitude2(vector v)
317 {
318     return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
319 }
320
321 static inline coord magnitude(vector v)
322 {
323     return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
324 }
325
326 static inline void normalize(vector v)
327 {
328     coord mag = 1.0/sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
329
330     v[0] *= mag;
331     v[1] *= mag;
332     v[2] *= mag;
333 }
334
335 static inline void normalize_to(vector v, coord m)
336 {
337     coord mag = 1.0/sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])/m;
338
339     v[0] *= mag;
340     v[1] *= mag;
341     v[2] *= mag;
342 }
343
344 static inline void midpoint(vector v1, vector v2, vector v)
345 {
346     vector_init(v,
347                 v1[0] + 0.5 * (v2[0] - v1[0]),
348                 v1[1] + 0.5 * (v2[1] - v1[1]),
349                 v1[2] + 0.5 * (v2[2] - v1[2]));
350 }
351
352 static inline hedge *partner(hedge *h) {
353     if(!h->e)
354         return NULL;
355     if(h == h->e->left) {
356         return h->e->right;
357     }
358     else if(h == h->e->right) {
359         return h->e->left;
360     }
361     else {
362 /*      _DEBUG("Inconsistent edge detected. Presumably, this is a bug. Exiting.\n", NULL); */
363         exit(-1);
364     }
365 }
366
367 vertex *vertex_new(solid *s, vector v)
368 {
369     vertex *vtx = (vertex*)malloc(sizeof(vertex));
370
371     if(!vtx)
372         return NULL;
373     vtx->s = s;
374     vtx->next = s->vertices;
375     s->vertices = vtx;
376     vector_copy(vtx->v, v);
377     vector_init(vtx->f, 0, 0, 0);
378     vector_init(vtx->vel, 0, 0, 0);
379     return vtx;
380 }
381
382 /* insert a new halfedge after hafter. this is low-level,
383  * i.e. it is a helper for the split_* functions, which
384  * maintain the consistency of the solid.
385  */
386 hedge *hedge_new(hedge *hafter, vertex *vtx)
387 {
388     hedge *h = (hedge*)malloc(sizeof(hedge));
389     
390     if(!h) {
391 /*      _DEBUG("Out of memory in hedge_new()\n",NULL); */
392         return NULL;
393     }
394     h->f = hafter->f;
395     h->vtx = vtx;
396     h->e = NULL;
397     h->prev = hafter;
398     h->next = hafter->next;
399     hafter->next = h;
400     h->next->prev = h;
401     return h;
402 }
403
404 edge *edge_new(solid *s)
405 {
406     edge *e = (edge*)malloc(sizeof(edge));
407     if(!e) {
408 /*      _DEBUG("Out of memory in edge_new()\n",NULL);*/
409         exit(-1);
410     }
411     e->next = s->edges;
412     s->edges = e;
413     e-> s = s;
414     e->left = e->right = NULL;
415     return e;
416 }
417
418 face *face_new(solid *s, hedge *h)
419 {
420     face *f = (face*)malloc(sizeof(face));
421     if(!f) {
422 /*      _DEBUG("Out of memory in face_new()",NULL);*/
423         exit(-1);
424     }
425     f->s = s;
426     f->start = h;
427     f->next = s->faces;
428     s->faces = f;
429     return f;
430 }
431
432 /* split vertex vtx, creating a new edge after v on f
433  * that goes to a new vertex at v, adjoining whatever
434  * face is on the other side of the halfedge attached to
435  * v on f. 
436  * Assumptions:
437  *    there are at least 2 faces.
438  *    partner(h)->next->vtx == vtx
439  * Post-assumptions:
440  *    the new halfedge will be inserted AFTER the indicated
441  *    halfedge. This means that f->start is guaranteed not to
442  *    change.
443  *    the vertex returned will have h==<the new halfedge>.
444  */
445
446 vertex *vertex_split(hedge *h, vector v)
447 {
448     hedge *h2, *hn1, *hn2;
449     vertex *vtxn;
450     edge *en;
451     face *f1, *f2;
452
453     f1 = h->f;
454     h2 = partner(h);
455     f2 = h2->f;
456     
457     vtxn = vertex_new(f1->s, v);
458     hn1 = hedge_new(h, vtxn);
459     vtxn->h = hn1;
460     hn2 = hedge_new(h2, vtxn);
461     hn2->e = h->e;
462
463     if(h2->e->left == h2)
464         h2->e->left = hn2;
465     else
466         h2->e->right = hn2;
467
468     en = edge_new(f1->s);
469     en->left = hn1;
470     en->right = h2;
471     hn1->e = en;
472     h2->e = en;
473     return vtxn;
474 }
475
476 face *face_split(face *f, hedge *h1, hedge *h2)
477 {
478     hedge *hn1, *hn2, *tmp;
479     edge *en;
480     face *fn;
481
482     if(h1->f != f || h2->f != f) {
483 /*      _DEBUG("Whoah, cap'n, yer usin' a bad halfedge!\n",NULL);*/
484         exit(-1);
485     }
486     if(h1 == h2) {
487 /*      _DEBUG("Trying to split a face at a single vertex\n",NULL);*/
488         exit(-1);
489     }
490     /* close the loops */
491     h1->prev->next = h2;
492     h2->prev->next = h1;
493     tmp = h1->prev;
494     h1->prev = h2->prev;
495     h2->prev = tmp;
496     /* insert halfedges & create edge */
497     hn1 = hedge_new(h2->prev, h1->vtx);
498     hn2 = hedge_new(h1->prev, h2->vtx);
499     en = edge_new(f->s);
500     en->left = hn1;
501     en->right = hn2;
502     hn1->e = en;
503     hn2->e = en;
504
505     /* make the new face, first find out which hedge is contained
506     * in the original face, then start the new face at the other */
507     tmp = f->start;
508     while(tmp != h1 && tmp != h2)
509         tmp = tmp->next;
510     tmp = (tmp == h1) ? h2 : h1 ;
511     fn = face_new(f->s, tmp);
512     do {
513         tmp->f = fn;
514         tmp = tmp->next;
515     } while(tmp != fn->start);
516     fn->color = f->color;
517     return fn;
518 }
519
520 solid *solid_new(vector where) 
521 {
522     solid *s = (solid*)malloc(sizeof(solid));
523     face *f1, *f2;
524     edge *e;
525     vertex *vtx;
526     hedge *h1,*h2;
527
528     s->faces = NULL;
529     s->edges = NULL;
530     s->vertices = NULL;
531
532     h1 = (hedge*)malloc(sizeof(hedge));
533     h2 = (hedge*)malloc(sizeof(hedge));
534     h1->next = h1->prev = h1;
535     h2->next = h2->prev = h2;
536
537     vtx = vertex_new(s, where);
538     vtx->h = h1;
539     h1->vtx = vtx;
540     h2->vtx = vtx;
541
542     e = edge_new(s);
543     e->left = h1;
544     e->right = h2;
545     h1->e = e;
546     h2->e = e;
547
548     f1 = face_new(s, h1);
549     f2 = face_new(s, h2);
550     h1->f = f1;
551     h2->f = f2;
552
553     return s;
554 }
555
556 /* This is all the code directly related to constructing the jigglypuff */
557 void face_tessel2(face *f)
558 {
559     hedge *h1=f->start->prev, *h2=f->start->next;
560     
561     if(h1->next == h1)
562         return;
563     while(h2 != h1 && h2->next != h1) {
564         f = face_split(f, h1, h2);
565         h1 = f->start;
566         h2 = f->start->next->next;
567     }
568 }
569
570 /* This will only work with solids composed entirely of
571  * triangular faces. It first add a vertex to the middle
572  * of each edge, then walks the faces, connecting the
573  * dots.
574  * I'm abusing the fact that new faces and edges are always
575  * added at the head of the list. If that ever changes,
576  * this is borked. 
577  */
578 void solid_tesselate(solid *s) 
579 {
580     edge *e = s->edges;
581     face *f = s->faces;
582     
583     while(e) {
584         vector v;
585         midpoint(e->left->vtx->v, e->right->vtx->v, v);
586         vertex_split(e->left, v);
587         e = e->next;
588     }
589     while(f) {
590         face_tessel2(f);
591         f=f->next;
592     }
593 }
594                 
595 void solid_spherify(solid * s, coord size) 
596 {
597     vertex *vtx = s->vertices;
598
599     while(vtx) {
600         normalize_to(vtx->v, size);
601         vtx = vtx->next;
602     }
603 }
604
605 solid *tetrahedron(jigglystruct *js) 
606 {
607     solid *s;
608     vertex *vtx;
609     vector v;
610     hedge *h;
611     face *f;
612     int i;
613
614     vector_init(v, 1, 1, 1);
615     s = solid_new(v);
616     vector_init(v, -1, -1, 1);
617     h = s->faces->start;
618     vtx = vertex_split(h, v);
619     vector_init(v, -1, 1, -1);
620     vtx = vertex_split(vtx->h, v);
621     h = vtx->h;
622     f = face_split(s->faces, h, h->prev);
623     vector_init(v, 1, -1, -1);
624     vertex_split(f->start, v);
625     f = s->faces->next->next;
626     h = f->start;
627     face_split(f, h, h->next->next);
628
629     if(js->color_style == COLOR_STYLE_FLOWERBOX) {
630         f = s->faces;
631         for(i=0; i<4; i++) {
632             f->color = flowerbox_colors[i];
633             f = f->next;
634         }
635     }       
636
637     return s;
638 }
639
640 solid *tesselated_tetrahedron(coord size, int iter, jigglystruct *js) {
641     solid *s = tetrahedron(js);
642     int i;
643
644     for(i=0; i<iter; i++) {
645         solid_tesselate(s);
646     }
647     return s;
648 }
649
650 static void clownbarf_colorize(solid *s) {
651     face *f = s->faces;
652     while(f) {
653         f->color = clownbarf_colors[random() % CLOWNBARF_NCOLORS];
654         f = f->next;
655     }
656 }
657
658 /* Here be the rendering code */
659
660 static inline void vertex_calcnormal(vertex *vtx, jigglystruct *js)
661 {
662     hedge *start = vtx->h, *h=start;
663     
664     vector_init(vtx->n, 0, 0, 0);
665     do {
666         vector u, v, norm;
667         vector_sub(h->prev->vtx->v, vtx->v, u);
668         vector_sub(h->next->vtx->v, vtx->v, v);
669         cross(u, v, norm);
670         vector_add_to(vtx->n, norm);
671         h = partner(h)->next;
672     } while(h != start);
673     if(!js->spooky)
674         normalize(vtx->n);
675     else
676         vector_scale(vtx->n, js->spooky);
677 }
678
679 static inline void vertex_render(vertex *vtx, jigglystruct *js)
680 {
681     glNormal3fv(vtx->n);
682     glVertex3fv(vtx->v);
683 }
684
685 /* This can be optimized somewhat due to the fact that all
686  * the faces are triangles. I haven't actually tested to
687  * see what the cost is of calling glBegin/glEnd for each
688  * triangle.
689  */
690 static inline void face_render(face *f, jigglystruct *js)
691 {
692     hedge *h1, *h2, *hend;
693
694     h1 = f->start;
695     hend = h1->prev;
696     h2 = h1->next;
697     
698     if(js->color_style == COLOR_STYLE_FLOWERBOX ||
699         js->color_style == COLOR_STYLE_CLOWNBARF)
700         glColor4fv(f->color);
701     glBegin(GL_TRIANGLES);
702     while(h1 != hend && h2 !=hend) {
703         vertex_render(h1->vtx, js);
704         vertex_render(h2->vtx, js);
705         vertex_render(hend->vtx, js);
706         h1 = h2;
707         h2 = h1->next;
708     }
709     glEnd();
710 }
711
712 void jigglypuff_render(jigglystruct *js) 
713 {
714     face *f = js->shape->faces;
715     vertex *vtx = js->shape->vertices;
716
717     while(vtx) {
718         vertex_calcnormal(vtx, js);
719         vtx = vtx->next;
720     }
721     while(f) {
722         face_render(f, js);
723         f=f->next;
724     }
725 }
726
727 /* This is the jiggling code */
728
729 /* stable distance when subdivs == 4 */
730 #define STABLE_DISTANCE 0.088388347648
731
732 void update_shape(jigglystruct *js)
733 {
734     vertex *vtx = js->shape->vertices;
735     edge *e = js->shape->edges;
736     vector zero;
737
738     vector_init(zero, 0, 0, 0);
739
740     /* sum all the vertex-vertex forces */
741     while(e) {
742         vector f;
743         coord mag;
744         vector_sub(e->left->vtx->v, e->right->vtx->v, f);
745         mag = js->stable_distance - magnitude(f);
746         vector_scale(f, mag);
747         vector_add_to(e->left->vtx->f, f);
748         vector_sub(zero, f, f);
749         vector_add_to(e->right->vtx->f, f);
750         e = e->next;
751     }
752
753     /* scale back the v-v force and add the spherical force
754      * then add the result to the vertex velocity, damping
755      * if necessary. Finally, move the vertex */
756     while(vtx) {
757         coord mag;
758         vector to_sphere;
759         vector_scale(vtx->f, js->hold_strength);
760         vector_copy(to_sphere, vtx->v);
761         mag = 1 - magnitude(to_sphere);
762         vector_scale(to_sphere, mag * js->spherify_strength);
763         vector_add_to(vtx->f, to_sphere);
764         vector_add_to(vtx->vel, vtx->f);
765         vector_init(vtx->f, 0, 0, 0);
766         mag = magnitude2(vtx->vel);
767         if(mag > js->damping_velocity)
768             vector_scale(vtx->vel, js->damping_factor);
769         vector_add_to(vtx->v, vtx->vel);
770         vtx = vtx->next;
771     }
772 }
773
774 /* These are the various initialization routines */
775
776 void init_texture(ModeInfo *mi)
777 {
778     XImage *img = xpm_to_ximage(mi->dpy, mi->xgwa.visual,
779                                mi->xgwa.colormap, jigglymap_xpm);
780
781     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
782                  img->width, img->height, 0, GL_RGBA,
783                  GL_UNSIGNED_BYTE, img->data);
784
785     XDestroyImage(img);
786 }
787
788 void setup_opengl(ModeInfo *mi, jigglystruct *js)
789 {
790     const GLfloat lpos0[4] = {-12, 8, 12, 0};
791     const GLfloat lpos1[4] = {7, -5, 0, 0};
792     const GLfloat lcol0[4] = {0.7f, 0.7f, 0.65f, 1};
793     const GLfloat lcol1[4] = {0.3f, 0.2f, 0.1f, 1};
794     const GLfloat scolor[4]= {0.9f, 0.9f, 0.9f, 0.5f};
795
796     glDrawBuffer(GL_BACK);
797     glClearColor(0, 0, 0, 0);
798     glShadeModel(GL_SMOOTH);
799     glEnable(GL_DEPTH_TEST);
800
801     if(js->do_wireframe) {
802         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
803     }
804     else {
805         glCullFace(GL_BACK);
806         glFrontFace(GL_CW);
807         glEnable(GL_CULL_FACE);
808     }
809
810     if(js->color_style != COLOR_STYLE_CHROME) {
811         glEnable(GL_LIGHTING);
812         glEnable(GL_LIGHT0);
813         glEnable(GL_LIGHT1);
814         
815         glLightfv(GL_LIGHT0, GL_POSITION, lpos0);
816         glLightfv(GL_LIGHT1, GL_POSITION, lpos1);
817         glLightfv(GL_LIGHT0, GL_DIFFUSE, lcol0);
818         glLightfv(GL_LIGHT1, GL_DIFFUSE, lcol1);
819
820         glEnable(GL_COLOR_MATERIAL);
821         glColor4fv(js->jiggly_color);
822
823         glMaterialfv(GL_FRONT, GL_SPECULAR, scolor);
824         glMateriali(GL_FRONT, GL_SHININESS, js->shininess);
825     }
826     else { /* chrome */
827         init_texture(mi);
828         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
829         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
830         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
831         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
832         glEnable(GL_TEXTURE_GEN_S);
833         glEnable(GL_TEXTURE_GEN_T);
834         glEnable(GL_TEXTURE_2D);
835         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
836         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
837         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
838     }
839 }
840
841 int parse_color(jigglystruct *js)
842 {
843     unsigned int r, g, b;
844     if(!strcmp(color, "clownbarf")) {
845         js->color_style = COLOR_STYLE_CLOWNBARF;
846         return 1;
847     }
848     else if(!strcmp(color, "flowerbox")) {
849         js->color_style = COLOR_STYLE_FLOWERBOX;
850         return 1;
851     }
852     else if(!strcmp(color, "chrome")) {
853         js->color_style = COLOR_STYLE_CHROME;
854         return 1;
855     }
856     else if(!strcmp(color, "cycle")) {
857         js->color_style = COLOR_STYLE_CYCLE;
858         js->jiggly_color[0] = ((float)random()) / REAL_RAND_MAX * 0.7 + 0.3;
859         js->jiggly_color[1] = ((float)random()) / REAL_RAND_MAX * 0.7 + 0.3;
860         js->jiggly_color[2] = ((float)random()) / REAL_RAND_MAX * 0.7 + 0.3;
861         js->jiggly_color[3] = 1.0f;
862         js->color_dir[0] = ((float)random()) / REAL_RAND_MAX / 100.0;
863         js->color_dir[1] = ((float)random()) / REAL_RAND_MAX / 100.0;
864         js->color_dir[2] = ((float)random()) / REAL_RAND_MAX / 100.0;
865         return 1;
866     }
867     else
868         js->color_style = 0;
869     if(strlen(color) != 7)
870         return 0;
871     if(sscanf(color,"#%02x%02x%02x", &r, &g, &b) != 3) {
872         return 0;
873     }
874     js->jiggly_color[0] = ((float)r)/255;
875     js->jiggly_color[1] = ((float)g)/255;
876     js->jiggly_color[2] = ((float)b)/255;
877     js->jiggly_color[3] = 1.0f;
878
879     return 1;
880 }
881
882 void randomize_parameters(jigglystruct *js) {
883     do_tetrahedron = random() & 1;
884     js->do_wireframe = !(random() & 3);
885     js->color_style = random() % 5;
886     if(js->color_style == COLOR_STYLE_NORMAL
887         || js->color_style == COLOR_STYLE_CYCLE) {
888         js->jiggly_color[0] = ((float)random()) / REAL_RAND_MAX * 0.5 + 0.5;
889         js->jiggly_color[1] = ((float)random()) / REAL_RAND_MAX * 0.5 + 0.5;
890         js->jiggly_color[2] = ((float)random()) / REAL_RAND_MAX * 0.5 + 0.5;
891         js->jiggly_color[3] = 1.0f;
892         if(js->color_style == COLOR_STYLE_CYCLE) {
893             js->color_dir[0] = ((float)random()) / REAL_RAND_MAX / 100.0;
894             js->color_dir[1] = ((float)random()) / REAL_RAND_MAX / 100.0;
895             js->color_dir[2] = ((float)random()) / REAL_RAND_MAX / 100.0;
896         }
897     }
898     if((js->color_style != COLOR_STYLE_CHROME) && (random() & 1))
899         js->spooky = (random() % 6) + 4;
900     else 
901         js->spooky = 0;
902     js->shininess = random() % 200;
903     speed = (random() % 700) + 50;
904     /* It' kind of dull if this is too high when it starts as a sphere */
905     spherism = do_tetrahedron ? (random() % 500) + 20 : (random() % 100) + 10;
906     hold = (random() % 800) + 100;
907     distance = (random() % 500) + 100;
908     damping = (random() % 800) + 50;
909 }    
910
911 void calculate_parameters(jigglystruct *js, int subdivs) {
912     /* try to compensate for the inherent instability at
913      * low complexity. */
914     float dist_factor = (subdivs == 6) ? 2 : (subdivs == 5) ? 1 : 0.5;
915
916     js->stable_distance = ((float)distance / 500.0) 
917         * (STABLE_DISTANCE / dist_factor);
918     js->hold_strength = (float)hold / 10000.0;
919     js->spherify_strength = (float)spherism / 10000.0;
920     js->damping_velocity = (float)damping / 100000.0;
921     js->damping_factor = 
922         0.001/max(js->hold_strength, js->spherify_strength);
923
924     js->speed = (float)speed / 1000.0;
925 }
926
927 /* The screenhack related functions begin here */
928
929 Bool jigglypuff_handle_event(ModeInfo *mi, XEvent *event)
930 {
931     jigglystruct *js = &jss[MI_SCREEN(mi)];
932     
933     if(event->xany.type == ButtonPress &&
934        event->xbutton.button & Button1) {
935         js->button_down = 1;
936         gltrackball_start(js->trackball, event->xbutton.x, event->xbutton.y,
937                           MI_WIDTH(mi), MI_HEIGHT(mi));
938         return True;
939     }
940     else if(event->xany.type == ButtonRelease &&
941             event->xbutton.button & Button1) {
942         js->button_down = 0;
943         return True;
944     }
945     else if(event->xany.type == MotionNotify && js->button_down) {
946         gltrackball_track(js->trackball, event->xmotion.x, event->xmotion.y,
947                           MI_WIDTH(mi), MI_HEIGHT(mi));
948         return True;
949     }
950     return False;
951 }
952
953 void reshape_jigglypuff(ModeInfo *mi, int width, int height)
954 {
955     GLfloat aspect = (GLfloat)width / (GLfloat)height;
956
957     glViewport(0, 0, width, height);
958     glMatrixMode(GL_PROJECTION);
959     glLoadIdentity();
960     glFrustum(-0.5*aspect, 0.5*aspect, -0.5, 0.5, 1, 20);
961 /*    glTranslatef(0, 0, -10);*/
962 }
963
964 void draw_jigglypuff(ModeInfo *mi)
965 {
966     jigglystruct *js = &jss[MI_SCREEN(mi)];
967     
968     glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(js->glx_context));
969     
970     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
971
972     glMatrixMode(GL_MODELVIEW);
973     glLoadIdentity();
974     glTranslatef(0,0,-10);
975
976     glRotatef(js->angle, sin(js->axis), cos(js->axis), -sin(js->axis));
977     glTranslatef(0, 0, 5);
978     if(!(js->button_down)) {
979         if((js->angle += js->speed) >= 360.0f ) {
980             js->angle -= 360.0f;
981         }
982         if((js->axis+=0.01f) >= 2*M_PI ) {
983             js->axis -= 2*M_PI;
984         }
985     }
986     gltrackball_rotate(js->trackball);
987
988     if(js->color_style == COLOR_STYLE_CYCLE) {
989         int i;
990         vector_add(js->jiggly_color, js->color_dir, js->jiggly_color);
991         
992         for(i=0; i<3; i++) {
993             if(js->jiggly_color[i] > 1.0 || js->jiggly_color[i] < 0.3) {
994                 js->color_dir[i] = (-js->color_dir[i]);
995                 js->jiggly_color[i] += js->color_dir[i];
996             }
997         }
998         glColor4fv(js->jiggly_color);
999     }
1000     
1001     jigglypuff_render(js);
1002     if(MI_IS_FPS(mi))
1003         do_fps(mi);
1004     glFinish();
1005     update_shape(js);
1006     glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
1007 }
1008
1009 void init_jigglypuff(ModeInfo *mi)
1010 {
1011     jigglystruct *js;
1012     int subdivs;
1013
1014     if(!jss) {
1015         jss = (jigglystruct*)
1016             calloc(MI_NUM_SCREENS(mi), sizeof(jigglystruct));
1017         if(!jss) {
1018             fprintf(stderr, "%s: No..memory...must...abort..\n", progname);
1019             exit(1);
1020         }
1021     }
1022
1023     js = &jss[MI_SCREEN(mi)];
1024
1025     js->do_wireframe = MI_IS_WIREFRAME(mi);
1026
1027     js->shininess = shininess;
1028
1029     subdivs = (complexity==1) ? 4 : (complexity==2) ? 5
1030         : (complexity==3) ? 6 : 5;
1031
1032     js->spooky = spooky << (subdivs-3);
1033
1034     if(!parse_color(js)) {
1035         fprintf(stderr, "%s: Bad color specification: '%s'.\n", progname, color);
1036         exit(-1);
1037     }
1038     
1039     if(random_parms)
1040         randomize_parameters(js);
1041
1042     js->shape = tesselated_tetrahedron(1, subdivs, js);
1043
1044     if(!do_tetrahedron)
1045         solid_spherify(js->shape, 1);
1046
1047     if(js->color_style == COLOR_STYLE_CLOWNBARF)
1048         clownbarf_colorize(js->shape);
1049
1050     calculate_parameters(js, subdivs);
1051
1052     if((js->glx_context = init_GL(mi)) != NULL) {
1053         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(js->glx_context));
1054         setup_opengl(mi, js);
1055         reshape_jigglypuff(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1056     }
1057     else {
1058         MI_CLEARWINDOW(mi);
1059     }
1060     js->trackball = gltrackball_init();
1061 /*    _DEBUG("distance : %f\nhold : %f\nspherify : %f\ndamping : %f\ndfact : %f\n",
1062            js->stable_distance, js->hold_strength, js->spherify_strength,
1063            js->damping_velocity, js->damping_factor);
1064     _DEBUG("wire : %d\nspooky : %d\nstyle : %d\nshininess : %d\n",
1065            js->do_wireframe, js->spooky, js->color_style, js->shininess);*/
1066 }
1067
1068 /* This is the end of the file */
1069
1070 #endif /* USE_GL */
1071
1072
1073
1074
1075
1076
1077