1 /* jigglypuff - the least colorful screensaver you'll ever see
3 * Copyright (c) 2003 Keith Macleod (kmacleod@primus.ca)
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
13 * Draws all varieties of obscene, spastic, puffy balls
14 * orbiting lazily about the screen. More of an accident
17 * This could either use more options, or less options and
20 * Apologies to anyone who thought they were getting a Pokemon
23 * Of course, if you modify it to do something interesting and/or
24 * funny, I'd appreciate receiving a copy.
27 #include <X11/Intrinsic.h>
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" \
40 # include "xlockmore.h"
53 #define max(a,b) (((a)>(b))?(a):(b))
54 #define min(a,b) (((a)<(b))?(a):(b))
62 static int do_wireframe;
63 static int do_tetrahedron;
65 static int random_parms;
67 typedef struct solid solid;
70 float stable_distance;
72 float spherify_strength;
73 float damping_velocity;
83 GLXContext *glx_context;
86 static jigglystruct *jss = NULL;
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"}
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}
112 #define countof(x) ((int)(sizeof(x)/sizeof(*(x))))
114 ModeSpecOpt jigglypuff_opts = {countof(opts), opts, countof(vars), vars, NULL};
117 #define _DEBUG(msg, args...) do { \
118 fprintf(stderr, "%s : %d : " msg ,__FILE__,__LINE__ ,##args); \
121 #define _DEBUG(msg, args...)
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];
168 static void vector_init(vector v, coord x, coord y, coord z)
175 static void vector_copy(vector d, vector s)
183 static void vector_add(vector v1, vector v2, vector v)
185 vector_init(v, v1[0]+v2[0], v1[1]+v2[1], v1[2]+v2[2]);
189 static void vector_add_to(vector v1, vector v2)
196 static void vector_sub(vector v1, vector v2, vector v)
198 vector_init(v, v1[0]-v2[0], v1[1]-v2[1], v1[2]-v2[2]);
201 static void vector_scale(vector v, coord s)
209 static coord dot(vector v1, vector v2)
211 return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
215 static void cross(vector v1, vector v2, vector 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]);
223 static coord magnitude2(vector v)
225 return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
228 static coord magnitude(vector v)
230 return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
233 static void normalize(vector v)
235 coord mag = 1.0/sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
242 static void normalize_to(vector v, coord m)
244 coord mag = 1.0/sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])/m;
251 static void midpoint(vector v1, vector v2, vector 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]));
259 static hedge *partner(hedge *h) {
262 if(h == h->e->left) {
265 else if(h == h->e->right) {
269 _DEBUG("Holy shit Batman! this edge is fucked up!\n");
274 static vertex *vertex_new(solid *s, vector v)
276 vertex *vtx = (vertex*)malloc(sizeof(vertex));
281 vtx->next = s->vertices;
283 vector_copy(vtx->v, v);
284 vector_init(vtx->f, 0, 0, 0);
285 vector_init(vtx->vel, 0, 0, 0);
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.
293 static hedge *hedge_new(hedge *hafter, vertex *vtx)
295 hedge *h = (hedge*)malloc(sizeof(hedge));
298 _DEBUG("Out of memory in hedge_new()\n");
305 h->next = hafter->next;
311 static edge *edge_new(solid *s)
313 edge *e = (edge*)malloc(sizeof(edge));
315 _DEBUG("Out of memory in edge_new()\n");
321 e->left = e->right = NULL;
325 static face *face_new(solid *s, hedge *h)
327 face *f = (face*)malloc(sizeof(face));
329 _DEBUG("Out of memory in face_new()");
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
344 * there are at least 2 faces.
345 * partner(h)->next->vtx == vtx
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
354 * Also, the vertex returned will have h==<the new halfedge>.
357 static vertex *vertex_split(hedge *h, vector v)
359 hedge *h2, *hn1, *hn2;
368 vtxn = vertex_new(f1->s, v);
369 hn1 = hedge_new(h, vtxn);
371 hn2 = hedge_new(h2, vtxn);
374 if(h2->e->left == h2)
379 en = edge_new(f1->s);
387 static face *face_split(face *f, hedge *h1, hedge *h2)
389 hedge *hn1, *hn2, *tmp;
393 if(h1->f != f || h2->f != f) {
394 _DEBUG("Whoah, cap'n, yer usin' a bad halfedge!\n");
398 _DEBUG("Trying to split a face at a single vertex\n");
401 /* close the loops */
407 /* insert halfedges & create edge */
408 hn1 = hedge_new(h2->prev, h1->vtx);
409 hn2 = hedge_new(h1->prev, h2->vtx);
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 */
419 while(tmp != h1 && tmp != h2)
421 tmp = (tmp == h1) ? h2 : h1 ;
422 fn = face_new(f->s, tmp);
426 } while(tmp != fn->start);
430 static solid *solid_new(vector where)
432 solid *s = (solid*)malloc(sizeof(solid));
442 h1 = (hedge*)malloc(sizeof(hedge));
443 h2 = (hedge*)malloc(sizeof(hedge));
444 h1->next = h1->prev = h1;
445 h2->next = h2->prev = h2;
447 vtx = vertex_new(s, where);
458 f1 = face_new(s, h1);
459 f2 = face_new(s, h2);
466 static solid *tetra(void)
474 vector_init(v, 1, 1, 1);
476 vector_init(v, -1, -1, 1);
478 vtx = vertex_split(h, v);
479 vector_init(v, -1, 1, -1);
480 vtx = vertex_split(vtx->h, v);
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;
487 face_split(f, h, h->next->next);
492 static void face_tessel2(face *f)
494 hedge *h1=f->start->prev, *h2=f->start->next;
498 while(h2 != h1 && h2->next != h1) {
499 f = face_split(f, h1, h2);
501 h2 = f->start->next->next;
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
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,
513 static void solid_tesselate(solid *s)
520 midpoint(e->left->vtx->v, e->right->vtx->v, v);
521 vertex_split(e->left, v);
530 static void solid_spherify(solid * s, coord size)
532 vertex *vtx = s->vertices;
535 normalize_to(vtx->v, size);
540 static solid *tesselated_tetrahedron(coord size, int iter)
545 for(i=0; i<iter; i++) {
551 static void vertex_calcnormal(vertex *vtx, int spooky)
553 hedge *start = vtx->h, *h=start;
555 vector_init(vtx->n, 0, 0, 0);
558 vector_sub(h->prev->vtx->v, vtx->v, u);
559 vector_sub(h->next->vtx->v, vtx->v, v);
561 vector_add_to(vtx->n, norm);
562 h = partner(h)->next;
567 vector_scale(vtx->n, 15);
570 static void vertex_render(vertex *vtx)
576 static void face_render(face *f)
578 hedge *h1, *h2, *hend;
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);
595 static void jigglypuff_render(jigglystruct *js)
597 face *f = js->shape->faces;
598 vertex *vtx = js->shape->vertices;
601 vertex_calcnormal(vtx, js->do_spooky);
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;
616 min(0.1, 1.0/max(js->hold_strength, js->spherify_strength));
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;
629 void update_shape(jigglystruct *js) {
630 vertex *vtx = js->shape->vertices;
631 edge *e = js->shape->edges;
634 vector_init(zero, 0, 0, 0);
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);
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);
665 void draw_jigglypuff(ModeInfo *mi)
667 jigglystruct *js = &jss[MI_SCREEN(mi)];
669 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(js->glx_context));
671 glDrawBuffer(GL_BACK);
672 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
674 glMatrixMode(GL_MODELVIEW);
677 glRotatef(js->angle, sin(js->axis), cos(js->axis), -sin(js->axis));
680 if((js->angle+=0.1) >= 360.0f ) {
683 if((js->axis+=0.01f) >= 2*M_PI ) {
686 jigglypuff_render(js);
691 glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
694 void reshape_jigglypuff(ModeInfo *mi, int width, int height)
696 GLfloat aspect = (GLfloat)width / (GLfloat)height;
698 glViewport(0, 0, width, height);
699 glMatrixMode(GL_PROJECTION);
701 glFrustum(-0.5*aspect, 0.5*aspect, -0.5, 0.5, 1, 20);
702 glTranslatef(0,0,-10);
705 static void setup_opengl(ModeInfo *mi, jigglystruct *js)
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};
714 glShadeModel(GL_SMOOTH);
716 if(js->do_wireframe) {
717 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
722 glEnable(GL_CULL_FACE);
725 glEnable(GL_DEPTH_TEST);
727 glEnable(GL_LIGHTING);
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);
736 glClearColor(0, 0, 0, 0);
738 glMaterialfv(GL_FRONT, GL_DIFFUSE, color1);
739 glMaterialfv(GL_FRONT, GL_SPECULAR, color2);
740 glMateriali(GL_FRONT, GL_SHININESS, 100);
743 void init_jigglypuff(ModeInfo *mi)
748 jss = (jigglystruct*)
749 calloc(MI_NUM_SCREENS(mi), sizeof(jigglystruct));
751 fprintf(stderr, "%s: No..memory...must...abort..\n", progname);
755 js = &jss[MI_SCREEN(mi)];
756 js->do_wireframe = MI_IS_WIREFRAME(mi);
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);
764 js->shape = tesselated_tetrahedron(1, 5);
766 solid_spherify(js->shape, 1);
767 calculate_parameters(js);
768 js->do_spooky = do_spooky;