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