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