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