http://www.mirrorservice.org/sites/master.us.finkmirrors.net/distfiles/md5/fa43fdd68d...
[xscreensaver] / hacks / glx / jigglypuff.c
1 /* jigglypuff - the least colorful screensaver you'll ever see
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  * This could either use more options, or less options and
18  * more randomness.
19  *
20  * Apologies to anyone who thought they were getting a Pokemon
21  * out of this.
22  *
23  * Of course, if you modify it to do something interesting and/or
24  * funny, I'd appreciate receiving a copy.
25  */
26
27 #include <X11/Intrinsic.h>
28
29 #ifdef STANDALONE
30 # define PROGCLASS        "Jigglypuff"
31 # define HACK_INIT        init_jigglypuff
32 # define HACK_DRAW        draw_jigglypuff
33 # define HACK_RESHAPE     reshape_jigglypuff
34 # define jigglypuff_opts  xlockmore_opts
35 # define DEFAULTS         "*random: True\n" \
36                           "*delay: 20000\n" \
37                           "*showFPS: False\n" \
38                           "wireframe: False\n"
39
40 # include "xlockmore.h"
41 #else
42 # include "xlock.h"
43 #endif
44
45 #ifdef HAVE_CONFIG_H
46 # include "config.h"
47 #endif
48
49 #ifdef USE_GL
50 #include <GL/gl.h>
51
52 #ifndef max
53 #define max(a,b) (((a)>(b))?(a):(b))
54 #define min(a,b) (((a)<(b))?(a):(b))
55 #endif
56
57 static int spherism;
58 static int hold;
59 static int distance;
60 static int damping;
61
62 static int do_wireframe;
63 static int do_tetrahedron;
64 static int do_spooky;
65 static int random_parms;
66
67 typedef struct solid solid;
68
69 typedef struct {
70     float stable_distance;
71     float hold_strength;
72     float spherify_strength;
73     float damping_velocity;
74     float damping_factor;
75
76     int do_wireframe;
77     int do_spooky;
78
79     solid *shape;
80     float angle;
81     float axis;
82
83     GLXContext *glx_context;
84 }jigglystruct;
85
86 static jigglystruct *jss = NULL;
87
88 static XrmOptionDescRec opts[] = {
89     {"-random", ".Jigglypuff.random", XrmoptionNoArg, (caddr_t)"true"},
90     {"+random", ".Jigglypuff.random", XrmoptionNoArg, (caddr_t)"false"},
91     {"-tetra", ".Jigglypuff.tetra", XrmoptionNoArg, (caddr_t)"true"},
92     {"+tetra", ".Jigglypuff.tetra", XrmoptionNoArg, (caddr_t)"false"},
93     {"-spooky", ".Jigglypuff.spooky", XrmoptionNoArg, (caddr_t)"true"},
94     {"+spooky", ".Jigglypuff.spooky", XrmoptionNoArg, (caddr_t)"false"},
95     {"-spherism", ".Jigglypuff.spherism", XrmoptionSepArg, "500"},
96     {"-hold", ".Jigglypuff.hold", XrmoptionSepArg, "500"},
97     {"-distance", "Jigglypuff.distance", XrmoptionSepArg, "500"},
98     {"-damping", "Jigglypuff.damping", XrmoptionSepArg, "50"}
99 };
100
101 static argtype vars[] = {
102     {(caddr_t*)&random_parms, "random", "Random", "False", t_Bool},
103     {(caddr_t*)&do_tetrahedron, "tetra", "Tetra", "True", t_Bool},
104     {(caddr_t*)&do_spooky, "spooky", "Spooky", "False", t_Bool},
105     {(caddr_t*)&spherism, "spherism", "Spherism", "100", t_Int},
106     {(caddr_t*)&hold, "hold", "Hold", "600", t_Int},
107     {(caddr_t*)&distance, "distance", "Distance", "500", t_Int},
108     {(caddr_t*)&damping, "damping", "Damping", "50", t_Int}
109 };
110
111 #undef countof
112 #define countof(x) ((int)(sizeof(x)/sizeof(*(x))))
113
114 ModeSpecOpt jigglypuff_opts = {countof(opts), opts, countof(vars), vars, NULL};
115
116 #ifdef DEBUG
117 #define _DEBUG(msg, args...) do { \
118     fprintf(stderr, "%s : %d : " msg ,__FILE__,__LINE__ ,##args); \
119 } while(0)
120 #else
121 #define _DEBUG(msg, args...)
122 #endif
123
124 typedef struct face face;
125 typedef struct edge edge;
126 typedef struct hedge hedge;
127 typedef struct vertex vertex;
128 typedef GLfloat coord;
129 typedef coord vector[3];
130
131 struct solid {
132     face *faces;
133     edge *edges;
134     vertex *vertices;
135 };
136
137 struct face {
138     solid *s;
139     hedge *start;
140     face *next;
141 };
142
143 struct edge {
144     solid *s;
145     hedge *left;
146     hedge *right;
147     edge *next;
148 };
149
150 struct hedge {
151     face *f;
152     edge *e;
153     vertex *vtx;
154     hedge *next;
155     hedge *prev;
156 };
157
158 struct vertex {
159     solid *s;
160     hedge *h;
161     vector v;
162     vector n;
163     vector f;
164     vector vel;
165     vertex *next;
166 };
167
168 static void vector_init(vector v, coord x, coord y, coord z)
169 {
170     v[0] = x;
171     v[1] = y;
172     v[2] = z;
173 }    
174
175 static void vector_copy(vector d, vector s)
176 {
177     d[0] = s[0];
178     d[1] = s[1];
179     d[2] = s[2];
180 }
181
182 #if 0
183 static void vector_add(vector v1, vector v2, vector v)
184 {
185     vector_init(v, v1[0]+v2[0], v1[1]+v2[1], v1[2]+v2[2]);
186 }
187 #endif
188
189 static void vector_add_to(vector v1, vector v2)
190 {
191     v1[0] += v2[0];
192     v1[1] += v2[1];
193     v1[2] += v2[2];
194 }
195
196 static void vector_sub(vector v1, vector v2, vector v)
197 {
198     vector_init(v, v1[0]-v2[0], v1[1]-v2[1], v1[2]-v2[2]);
199 }
200
201 static void vector_scale(vector v, coord s)
202 {
203     v[0] *= s;
204     v[1] *= s;
205     v[2] *= s;
206 }
207
208 #if 0
209 static coord dot(vector v1, vector v2)
210 {
211     return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
212 }
213 #endif
214
215 static void cross(vector v1, vector v2, vector v)
216 {
217     vector_init(v,
218                 v1[1]*v2[2] - v2[1]*v1[2],
219                 v1[2]*v2[0] - v2[2]*v1[0],
220                 v1[0]*v2[1] - v2[0]*v1[1]);
221 }
222
223 static coord magnitude2(vector v)
224 {
225     return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
226 }
227
228 static coord magnitude(vector v)
229 {
230     return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
231 }
232
233 static void normalize(vector v)
234 {
235     coord mag = 1.0/sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
236
237     v[0] *= mag;
238     v[1] *= mag;
239     v[2] *= mag;
240 }
241
242 static void normalize_to(vector v, coord m)
243 {
244     coord mag = 1.0/sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])/m;
245
246     v[0] *= mag;
247     v[1] *= mag;
248     v[2] *= mag;
249 }
250
251 static void midpoint(vector v1, vector v2, vector v)
252 {
253     vector_init(v,
254                 v1[0] + 0.5 * (v2[0] - v1[0]),
255                 v1[1] + 0.5 * (v2[1] - v1[1]),
256                 v1[2] + 0.5 * (v2[2] - v1[2]));
257 }
258
259 static hedge *partner(hedge *h) {
260     if(!h->e)
261         return NULL;
262     if(h == h->e->left) {
263         return h->e->right;
264     }
265     else if(h == h->e->right) {
266         return h->e->left;
267     }
268     else {
269         _DEBUG("Holy shit Batman! this edge is fucked up!\n");
270         exit(-1);
271     }
272 }
273
274 static vertex *vertex_new(solid *s, vector v)
275 {
276     vertex *vtx = (vertex*)malloc(sizeof(vertex));
277
278     if(!vtx)
279         return NULL;
280     vtx->s = s;
281     vtx->next = s->vertices;
282     s->vertices = vtx;
283     vector_copy(vtx->v, v);
284     vector_init(vtx->f, 0, 0, 0);
285     vector_init(vtx->vel, 0, 0, 0);
286     return vtx;
287 }
288
289 /* insert a new halfedge after hafter. this is low-level,
290  * i.e. it is a helper for the split_* functions, which
291  * maintain the consistency of the solid.
292  */
293 static hedge *hedge_new(hedge *hafter, vertex *vtx)
294 {
295     hedge *h = (hedge*)malloc(sizeof(hedge));
296     
297     if(!h) {
298         _DEBUG("Out of memory in hedge_new()\n");
299         return NULL;
300     }
301     h->f = hafter->f;
302     h->vtx = vtx;
303     h->e = NULL;
304     h->prev = hafter;
305     h->next = hafter->next;
306     hafter->next = h;
307     h->next->prev = h;
308     return h;
309 }
310
311 static edge *edge_new(solid *s)
312 {
313     edge *e = (edge*)malloc(sizeof(edge));
314     if(!e) {
315         _DEBUG("Out of memory in edge_new()\n");
316         exit(-1);
317     }
318     e->next = s->edges;
319     s->edges = e;
320     e-> s = s;
321     e->left = e->right = NULL;
322     return e;
323 }
324
325 static face *face_new(solid *s, hedge *h)
326 {
327     face *f = (face*)malloc(sizeof(face));
328     if(!f) {
329         _DEBUG("Out of memory in face_new()");
330         exit(-1);
331     }
332     f->s = s;
333     f->start = h;
334     f->next = s->faces;
335     s->faces = f;
336     return f;
337 }
338
339 /* split vertex vtx, creating a new edge after v on f
340  * that goes to a new vertex at v, adjoining whatever
341  * face is on the other side of the halfedge attached to
342  * v on f. 
343  * Assumptions:
344  *    there are at least 2 faces.
345  *    partner(h)->next->vtx == vtx
346  * Post-assumptions:
347  *    the new halfedge will be inserted _before_ the
348  *    halfedge owning vtx in f.
349  *    THIS IS WRONG. FIX ME!!!
350  * New Deal - the Invariants
351  *    the new halfedge will be inserted AFTER the indicated
352  *    halfedge. This means that f->start is guaranteed not to
353  *    change.
354  * Also, the vertex returned will have h==<the new halfedge>.
355  */
356
357 static vertex *vertex_split(hedge *h, vector v)
358 {
359     hedge *h2, *hn1, *hn2;
360     vertex *vtxn;
361     edge *en;
362     face *f1, *f2;
363
364     f1 = h->f;
365     h2 = partner(h);
366     f2 = h2->f;
367
368     vtxn = vertex_new(f1->s, v);
369     hn1 = hedge_new(h, vtxn);
370     vtxn->h = hn1;
371     hn2 = hedge_new(h2, vtxn);
372     hn2->e = h->e;
373
374     if(h2->e->left == h2)
375         h2->e->left = hn2;
376     else
377         h2->e->right = hn2;
378
379     en = edge_new(f1->s);
380     en->left = hn1;
381     en->right = h2;
382     hn1->e = en;
383     h2->e = en;
384     return vtxn;
385 }
386
387 static face *face_split(face *f, hedge *h1, hedge *h2)
388 {
389     hedge *hn1, *hn2, *tmp;
390     edge *en;
391     face *fn;
392
393     if(h1->f != f || h2->f != f) {
394         _DEBUG("Whoah, cap'n, yer usin' a bad halfedge!\n");
395         exit(-1);
396     }
397     if(h1 == h2) {
398         _DEBUG("Trying to split a face at a single vertex\n");
399         exit(-1);
400     }
401     /* close the loops */
402     h1->prev->next = h2;
403     h2->prev->next = h1;
404     tmp = h1->prev;
405     h1->prev = h2->prev;
406     h2->prev = tmp;
407     /* insert halfedges & create edge */
408     hn1 = hedge_new(h2->prev, h1->vtx);
409     hn2 = hedge_new(h1->prev, h2->vtx);
410     en = edge_new(f->s);
411     en->left = hn1;
412     en->right = hn2;
413     hn1->e = en;
414     hn2->e = en;
415
416     /* make the new face, first find out which hedge is contained
417     * in the original face, then start the new face at the other */
418     tmp = f->start;
419     while(tmp != h1 && tmp != h2)
420         tmp = tmp->next;
421     tmp = (tmp == h1) ? h2 : h1 ;
422     fn = face_new(f->s, tmp);
423     do {
424         tmp->f = fn;
425         tmp = tmp->next;
426     } while(tmp != fn->start);
427     return fn;
428 }
429
430 static solid *solid_new(vector where) 
431 {
432     solid *s = (solid*)malloc(sizeof(solid));
433     face *f1, *f2;
434     edge *e;
435     vertex *vtx;
436     hedge *h1,*h2;
437
438     s->faces = NULL;
439     s->edges = NULL;
440     s->vertices = NULL;
441
442     h1 = (hedge*)malloc(sizeof(hedge));
443     h2 = (hedge*)malloc(sizeof(hedge));
444     h1->next = h1->prev = h1;
445     h2->next = h2->prev = h2;
446
447     vtx = vertex_new(s, where);
448     vtx->h = h1;
449     h1->vtx = vtx;
450     h2->vtx = vtx;
451
452     e = edge_new(s);
453     e->left = h1;
454     e->right = h2;
455     h1->e = e;
456     h2->e = e;
457
458     f1 = face_new(s, h1);
459     f2 = face_new(s, h2);
460     h1->f = f1;
461     h2->f = f2;
462
463     return s;
464 }
465
466 static solid *tetra(void) 
467 {
468     solid *s;
469     vertex *vtx;
470     vector v;
471     hedge *h;
472     face *f;
473
474     vector_init(v, 1, 1, 1);
475     s = solid_new(v);
476     vector_init(v, -1, -1, 1);
477     h = s->faces->start;
478     vtx = vertex_split(h, v);
479     vector_init(v, -1, 1, -1);
480     vtx = vertex_split(vtx->h, v);
481     h = vtx->h;
482     f = face_split(s->faces, h, h->prev);
483     vector_init(v, 1, -1, -1);
484     vertex_split(f->start, v);
485     f = s->faces->next->next;
486     h = f->start;
487     face_split(f, h, h->next->next);
488
489     return s;
490 }
491
492 static void face_tessel2(face *f)
493 {
494     hedge *h1=f->start->prev, *h2=f->start->next;
495     
496     if(h1->next == h1)
497         return;
498     while(h2 != h1 && h2->next != h1) {
499         f = face_split(f, h1, h2);
500         h1 = f->start;
501         h2 = f->start->next->next;
502     }
503 }
504
505 /* This will only work with solids composed entirely of
506  * triangular faces. It first add a vertex to the middle
507  * of each edge, then walks the faces, connecting the
508  * dots.
509  * I'm abusing the fact that new faces and edges are always
510  * added at the head of the list. If that ever changes,
511  * this is borked. 
512  */
513 static void solid_tesselate(solid *s) 
514 {
515     edge *e = s->edges;
516     face *f = s->faces;
517     
518     while(e) {
519         vector v;
520         midpoint(e->left->vtx->v, e->right->vtx->v, v);
521         vertex_split(e->left, v);
522         e = e->next;
523     }
524     while(f) {
525         face_tessel2(f);
526         f=f->next;
527     }
528 }
529                 
530 static void solid_spherify(solid * s, coord size) 
531 {
532     vertex *vtx = s->vertices;
533
534     while(vtx) {
535         normalize_to(vtx->v, size);
536         vtx = vtx->next;
537     }
538 }
539
540 static solid *tesselated_tetrahedron(coord size, int iter) 
541 {
542     solid *s = tetra();
543     int i;
544
545     for(i=0; i<iter; i++) {
546         solid_tesselate(s);
547     }
548     return s;
549 }
550
551 static void vertex_calcnormal(vertex *vtx, int spooky)
552 {
553     hedge *start = vtx->h, *h=start;
554     
555     vector_init(vtx->n, 0, 0, 0);
556     do {
557         vector u, v, norm;
558         vector_sub(h->prev->vtx->v, vtx->v, u);
559         vector_sub(h->next->vtx->v, vtx->v, v);
560         cross(u, v, norm);
561         vector_add_to(vtx->n, norm);
562         h = partner(h)->next;
563     } while(h != start);
564     if(!spooky)
565         normalize(vtx->n);
566     else
567         vector_scale(vtx->n, 15);
568 }
569
570 static void vertex_render(vertex *vtx)
571 {
572     glNormal3fv(vtx->n);
573     glVertex3fv(vtx->v);
574 }
575
576 static void face_render(face *f)
577 {
578     hedge *h1, *h2, *hend;
579
580     h1 = f->start;
581     hend = h1->prev;
582     h2 = h1->next;
583     
584     glBegin(GL_TRIANGLES);
585     while(h1 != hend && h2 !=hend) {
586         vertex_render(h1->vtx);
587         vertex_render(h2->vtx);
588         vertex_render(hend->vtx);
589         h1 = h2;
590         h2 = h1->next;
591     }
592     glEnd();
593 }
594
595 static void jigglypuff_render(jigglystruct *js) 
596 {
597     face *f = js->shape->faces;
598     vertex *vtx = js->shape->vertices;
599
600     while(vtx) {
601         vertex_calcnormal(vtx, js->do_spooky);
602         vtx = vtx->next;
603     }
604     while(f) {
605         face_render(f);
606         f=f->next;
607     }
608 }
609
610 void calculate_parameters(jigglystruct *js) {
611     js->stable_distance = (float)distance / 10000.0;
612     js->hold_strength = (float)hold / 1000.0;
613     js->spherify_strength = (float)spherism / 10000.0;
614     js->damping_velocity = (float)damping / 100000.0;
615     js->damping_factor = 
616         min(0.1, 1.0/max(js->hold_strength, js->spherify_strength));
617 }
618
619 void randomize_parameters(void) {
620     do_tetrahedron = !(random() & 1);
621     do_spooky = !(random() & 3);
622     do_wireframe = !(random() & 3);
623     spherism = random() % 1000;
624     hold = random() % 1000;
625     distance = random() % 1000;
626     damping = random() % 1000;
627 }    
628
629 void update_shape(jigglystruct *js) {
630     vertex *vtx = js->shape->vertices;
631     edge *e = js->shape->edges;
632     vector zero;
633
634     vector_init(zero, 0, 0, 0);
635
636     while(e) {
637         vector f;
638         coord mag;
639         vector_sub(e->left->vtx->v, e->right->vtx->v, f);
640         mag = js->stable_distance - magnitude(f);
641         vector_scale(f, mag);
642         vector_add_to(e->left->vtx->f, f);
643         vector_sub(zero, f, f);
644         vector_add_to(e->right->vtx->f, f);
645         e = e->next;
646     }
647     while(vtx) {
648         coord mag;
649         vector to_sphere;
650         vector_scale(vtx->f, js->hold_strength);
651         vector_copy(to_sphere, vtx->v);
652         mag = 1 - magnitude(to_sphere);
653         vector_scale(to_sphere, mag * js->spherify_strength);
654         vector_add_to(vtx->f, to_sphere);
655         vector_add_to(vtx->vel, vtx->f);
656         vector_init(vtx->f, 0, 0, 0);
657         mag = magnitude2(vtx->vel);
658         if(mag > js->damping_velocity)
659             vector_scale(vtx->vel, js->damping_factor);
660         vector_add_to(vtx->v, vtx->vel);
661         vtx = vtx->next;
662     }
663 }
664
665 void draw_jigglypuff(ModeInfo *mi)
666 {
667     jigglystruct *js = &jss[MI_SCREEN(mi)];
668
669     glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(js->glx_context));
670     
671     glDrawBuffer(GL_BACK);
672     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
673
674     glMatrixMode(GL_MODELVIEW);
675     glLoadIdentity();
676
677     glRotatef(js->angle, sin(js->axis), cos(js->axis), -sin(js->axis));
678     glTranslatef(0,0,5);
679
680     if((js->angle+=0.1) >= 360.0f ) {
681         js->angle -= 360.0f;
682     }
683     if((js->axis+=0.01f) >= 2*M_PI ) {
684         js->axis -= 2*M_PI;
685     }
686     jigglypuff_render(js);
687     if(mi->fps_p)
688         do_fps(mi);
689     glFinish();
690     update_shape(js);
691     glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
692 }
693
694 void reshape_jigglypuff(ModeInfo *mi, int width, int height)
695 {
696     GLfloat aspect = (GLfloat)width / (GLfloat)height;
697
698     glViewport(0, 0, width, height);
699     glMatrixMode(GL_PROJECTION);
700     glLoadIdentity();
701     glFrustum(-0.5*aspect, 0.5*aspect, -0.5, 0.5, 1, 20);
702     glTranslatef(0,0,-10);
703 }
704
705 static void setup_opengl(ModeInfo *mi, jigglystruct *js)
706 {
707     const GLfloat lpos0[4] = {-12, 8, 12, 0};
708     const GLfloat lpos1[4] = {7, -5, 0, 0};
709     const GLfloat lcol0[4] = {0.7, 0.7, 0.65, 1};
710     const GLfloat lcol1[4] = {0.3, 0.2, 0.1, 1};
711     const GLfloat color1[4]={1, 1, 1, 0.5};
712     const GLfloat color2[4]={0.9, 0.9, 0.9, 0.5};
713
714     glShadeModel(GL_SMOOTH);
715
716     if(js->do_wireframe) {
717         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
718     }
719     else {
720         glCullFace(GL_BACK);
721         glFrontFace(GL_CW);
722         glEnable(GL_CULL_FACE);
723     }
724
725     glEnable(GL_DEPTH_TEST);
726
727     glEnable(GL_LIGHTING);
728     glEnable(GL_LIGHT0);
729     glEnable(GL_LIGHT1);
730
731     glLightfv(GL_LIGHT0, GL_POSITION, lpos0);
732     glLightfv(GL_LIGHT1, GL_POSITION, lpos1);
733     glLightfv(GL_LIGHT0, GL_DIFFUSE, lcol0);
734     glLightfv(GL_LIGHT1, GL_DIFFUSE, lcol1);
735
736     glClearColor(0, 0, 0, 0);
737
738     glMaterialfv(GL_FRONT, GL_DIFFUSE, color1);
739     glMaterialfv(GL_FRONT, GL_SPECULAR, color2);
740     glMateriali(GL_FRONT, GL_SHININESS, 100);
741 }
742
743 void init_jigglypuff(ModeInfo *mi)
744 {
745     jigglystruct *js;
746
747     if(!jss) {
748         jss = (jigglystruct*)
749             calloc(MI_NUM_SCREENS(mi), sizeof(jigglystruct));
750         if(!jss) {
751             fprintf(stderr, "%s: No..memory...must...abort..\n", progname);
752             exit(1);
753         }
754     }
755     js = &jss[MI_SCREEN(mi)];
756     js->do_wireframe = MI_IS_WIREFRAME(mi);
757     
758     if(random_parms)
759         randomize_parameters();
760     if((js->glx_context = init_GL(mi)) != NULL) {
761         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(js->glx_context));
762         setup_opengl(mi, js);
763     }
764     js->shape = tesselated_tetrahedron(1, 5);
765     if(!do_tetrahedron)
766         solid_spherify(js->shape, 1);
767     calculate_parameters(js);
768     js->do_spooky = do_spooky;
769 }
770
771 #endif /* USE_GL */