ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.21.tar.gz
[xscreensaver] / hacks / glx / cubenetic.c
1 /* cubenetic, Copyright (c) 2002-2004 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 #include <X11/Intrinsic.h>
13
14 extern XtAppContext app;
15
16 #define PROGCLASS       "Cubenetic"
17 #define HACK_INIT       init_cube
18 #define HACK_DRAW       draw_cube
19 #define HACK_RESHAPE    reshape_cube
20 #define HACK_HANDLE_EVENT cube_handle_event
21 #define EVENT_MASK      PointerMotionMask
22 #define ccs_opts        xlockmore_opts
23
24 #define DEF_SPIN        "XYZ"
25 #define DEF_WANDER      "True"
26 #define DEF_TEXTURE     "True"
27
28 #define DEF_WAVE_COUNT  "3"
29 #define DEF_WAVE_SPEED  "80"
30 #define DEF_WAVE_RADIUS "512"
31
32 #define DEFAULTS        "*delay:        20000       \n" \
33                         "*count:        5           \n" \
34                         "*showFPS:      False       \n" \
35                         "*wireframe:    False       \n" \
36
37 #undef countof
38 #define countof(x) (sizeof((x))/sizeof((*x)))
39
40 #include "xlockmore.h"
41 #include "colors.h"
42 #include "rotator.h"
43 #include "gltrackball.h"
44 #include <ctype.h>
45
46 #ifdef USE_GL /* whole file */
47
48 #include <GL/glu.h>
49
50 typedef struct {
51   int color;
52   GLfloat x, y, z;
53   GLfloat w, h, d;
54   int frame;
55   GLfloat dx, dy, dz;
56   GLfloat dw, dh, dd;
57 } cube;
58
59 typedef struct {
60   int x, y;
61   double xth, yth;
62 } wave_src;
63
64 typedef struct {
65   int nwaves;
66   int radius;
67   int speed;
68   wave_src *srcs;
69   int *heights;
70 } waves;
71
72
73 typedef struct {
74   GLXContext *glx_context;
75   rotator *rot;
76   trackball_state *trackball;
77   Bool button_down_p;
78
79   GLuint cube_list;
80   GLuint texture_id;
81   int ncubes;
82   cube *cubes;
83   waves *waves;
84
85   int texture_width, texture_height;
86   unsigned char *texture;
87
88   int ncolors;
89   XColor *cube_colors;
90   XColor *texture_colors;
91
92 } cube_configuration;
93
94 static cube_configuration *ccs = NULL;
95
96 static char *do_spin;
97 static Bool do_wander;
98 static Bool do_texture;
99
100 static int wave_count;
101 static int wave_speed;
102 static int wave_radius;
103 static int texture_size = 256;
104
105 static XrmOptionDescRec opts[] = {
106   { "-spin",   ".spin",   XrmoptionSepArg, 0 },
107   { "+spin",   ".spin",   XrmoptionNoArg, "" },
108   { "-wander", ".wander", XrmoptionNoArg, "True" },
109   { "+wander", ".wander", XrmoptionNoArg, "False" },
110   {"-texture", ".texture", XrmoptionNoArg, "true" },
111   {"+texture", ".texture", XrmoptionNoArg, "false" },
112   {"-waves",       ".waves",      XrmoptionSepArg, 0 },
113   {"-wave-speed",  ".waveSpeed",  XrmoptionSepArg, 0 },
114   {"-wave-radius", ".waveRadius", XrmoptionSepArg, 0 },
115 };
116
117 static argtype vars[] = {
118   {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_String},
119   {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
120   {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
121   {&wave_count, "waves",     "Waves",      DEF_WAVE_COUNT, t_Int},
122   {&wave_speed, "waveSpeed", "WaveSpeed",  DEF_WAVE_SPEED, t_Int},
123   {&wave_radius,"waveRadius","WaveRadius", DEF_WAVE_RADIUS,t_Int},
124 };
125
126 ModeSpecOpt ccs_opts = {countof(opts), opts, countof(vars), vars, NULL};
127
128
129 static void
130 unit_cube (Bool wire)
131 {
132   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);     /* front */
133   glNormal3f (0, 0, 1);
134   glTexCoord2f(1, 0); glVertex3f ( 0.5, -0.5,  0.5);
135   glTexCoord2f(0, 0); glVertex3f ( 0.5,  0.5,  0.5);
136   glTexCoord2f(0, 1); glVertex3f (-0.5,  0.5,  0.5);
137   glTexCoord2f(1, 1); glVertex3f (-0.5, -0.5,  0.5);
138   glEnd();
139
140   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);     /* back */
141   glNormal3f (0, 0, -1);
142   glTexCoord2f(0, 0); glVertex3f (-0.5, -0.5, -0.5);
143   glTexCoord2f(0, 1); glVertex3f (-0.5,  0.5, -0.5);
144   glTexCoord2f(1, 1); glVertex3f ( 0.5,  0.5, -0.5);
145   glTexCoord2f(1, 0); glVertex3f ( 0.5, -0.5, -0.5);
146   glEnd();
147
148   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);     /* left */
149   glNormal3f (-1, 0, 0);
150   glTexCoord2f(1, 1); glVertex3f (-0.5,  0.5,  0.5);
151   glTexCoord2f(1, 0); glVertex3f (-0.5,  0.5, -0.5);
152   glTexCoord2f(0, 0); glVertex3f (-0.5, -0.5, -0.5);
153   glTexCoord2f(0, 1); glVertex3f (-0.5, -0.5,  0.5);
154   glEnd();
155
156   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);     /* right */
157   glNormal3f (1, 0, 0);
158   glTexCoord2f(1, 1); glVertex3f ( 0.5, -0.5, -0.5);
159   glTexCoord2f(1, 0); glVertex3f ( 0.5,  0.5, -0.5);
160   glTexCoord2f(0, 0); glVertex3f ( 0.5,  0.5,  0.5);
161   glTexCoord2f(0, 1); glVertex3f ( 0.5, -0.5,  0.5);
162   glEnd();
163
164   if (wire) return;
165
166   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);     /* top */
167   glNormal3f (0, 1, 0);
168   glTexCoord2f(0, 0); glVertex3f ( 0.5,  0.5,  0.5);
169   glTexCoord2f(0, 1); glVertex3f ( 0.5,  0.5, -0.5);
170   glTexCoord2f(1, 1); glVertex3f (-0.5,  0.5, -0.5);
171   glTexCoord2f(1, 0); glVertex3f (-0.5,  0.5,  0.5);
172   glEnd();
173
174   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);     /* bottom */
175   glNormal3f (0, -1, 0);
176   glTexCoord2f(1, 0); glVertex3f (-0.5, -0.5,  0.5);
177   glTexCoord2f(0, 0); glVertex3f (-0.5, -0.5, -0.5);
178   glTexCoord2f(0, 1); glVertex3f ( 0.5, -0.5, -0.5);
179   glTexCoord2f(1, 1); glVertex3f ( 0.5, -0.5,  0.5);
180   glEnd();
181 }
182
183
184
185 /* Window management, etc
186  */
187 void
188 reshape_cube (ModeInfo *mi, int width, int height)
189 {
190   GLfloat h = (GLfloat) height / (GLfloat) width;
191
192   glViewport (0, 0, (GLint) width, (GLint) height);
193
194   glMatrixMode(GL_PROJECTION);
195   glLoadIdentity();
196
197   gluPerspective (30.0, 1/h, 1.0, 100.0);
198   glMatrixMode(GL_MODELVIEW);
199   glLoadIdentity();
200   gluLookAt( 0.0, 0.0, 30.0,
201              0.0, 0.0, 0.0,
202              0.0, 1.0, 0.0);
203
204   glClear(GL_COLOR_BUFFER_BIT);
205 }
206
207
208 \f
209 /* Waves.
210    Adapted from ../hacks/interference.c by Hannu Mallat.
211  */
212
213 static void
214 init_wave (ModeInfo *mi)
215 {
216   cube_configuration *cc = &ccs[MI_SCREEN(mi)];
217   waves *ww;
218   int i;
219   cc->waves = ww = (waves *) calloc (sizeof(*cc->waves), 1);
220   ww->nwaves = wave_count;
221   ww->radius = wave_radius;
222   ww->speed  = wave_speed;
223   ww->heights = (int *) calloc (sizeof(*ww->heights), ww->radius);
224   ww->srcs = (wave_src *) calloc (sizeof(*ww->srcs), ww->nwaves);
225
226   for (i = 0; i < ww->radius; i++)
227     {
228       float max = (cc->ncolors * (ww->radius - i) / (float) ww->radius);
229       ww->heights[i] = ((max + max * cos(i / 50.0)) / 2.0);
230     }
231
232   for (i = 0; i < ww->nwaves; i++)
233     {
234       ww->srcs[i].xth = frand(2.0) * M_PI;
235       ww->srcs[i].yth = frand(2.0) * M_PI;
236     }
237 }
238
239 static void
240 interference (ModeInfo *mi)
241 {
242   cube_configuration *cc = &ccs[MI_SCREEN(mi)];
243   waves *ww = cc->waves;
244   int x, y, i;
245
246   /* Move the wave origins around
247    */
248   for (i = 0; i < ww->nwaves; i++)
249     {
250       ww->srcs[i].xth += (ww->speed / 1000.0);
251       if (ww->srcs[i].xth > 2*M_PI)
252         ww->srcs[i].xth -= 2*M_PI;
253       ww->srcs[i].yth += (ww->speed / 1000.0);
254       if (ww->srcs[i].yth > 2*M_PI)
255         ww->srcs[i].yth -= 2*M_PI;
256
257       ww->srcs[i].x = (cc->texture_width/2 +
258                        (cos (ww->srcs[i].xth) *
259                         cc->texture_width / 2));
260       ww->srcs[i].y = (cc->texture_height/2 +
261                        (cos (ww->srcs[i].yth) *
262                         cc->texture_height / 2));
263     }
264
265   /* Compute the effect of the waves on each pixel,
266      and generate the output map.
267    */
268   for (y = 0; y < cc->texture_height; y++)
269     for (x = 0; x < cc->texture_width; x++)
270       {
271         int result = 0;
272         unsigned char *o;
273         for (i = 0; i < ww->nwaves; i++)
274           {
275             int dx = x - ww->srcs[i].x;
276             int dy = y - ww->srcs[i].y;
277             int dist = sqrt (dx*dx + dy*dy);
278             result += (dist > ww->radius ? 0 : ww->heights[dist]);
279           }
280         result %= cc->ncolors;
281
282         o = cc->texture + (((y * cc->texture_width) + x) << 2);
283         o[0] = (cc->texture_colors[result].red   >> 8);
284         o[1] = (cc->texture_colors[result].green >> 8);
285         o[2] = (cc->texture_colors[result].blue  >> 8);
286      /* o[3] = 0xFF; */
287       }
288 }
289
290 \f
291 /* Textures
292  */
293
294 static void
295 init_texture (ModeInfo *mi)
296 {
297   cube_configuration *cc = &ccs[MI_SCREEN(mi)];
298   int i;
299
300   glEnable(GL_TEXTURE_2D);
301
302   clear_gl_error();
303   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
304   glGenTextures (1, &cc->texture_id);
305   glBindTexture (GL_TEXTURE_2D, cc->texture_id);
306   check_gl_error("texture binding");
307
308   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
309   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
310   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
311   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
312   glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
313   glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
314   glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
315   check_gl_error("texture initialization");
316
317   cc->texture_width  = texture_size;
318   cc->texture_height = texture_size;
319
320   i = texture_size * texture_size * 4;
321   cc->texture = (unsigned char *) malloc (i);
322   memset (cc->texture, 0xFF, i);
323 }
324
325
326 static void
327 shuffle_texture (ModeInfo *mi)
328 {
329   cube_configuration *cc = &ccs[MI_SCREEN(mi)];
330   interference (mi);
331   clear_gl_error();
332   glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
333                 cc->texture_width, cc->texture_height, 0,
334                 GL_RGBA, GL_UNSIGNED_BYTE,
335                 cc->texture);
336   check_gl_error("texture");
337 }
338
339
340 Bool
341 cube_handle_event (ModeInfo *mi, XEvent *event)
342 {
343   cube_configuration *cc = &ccs[MI_SCREEN(mi)];
344
345   if (event->xany.type == ButtonPress &&
346       event->xbutton.button == Button1)
347     {
348       cc->button_down_p = True;
349       gltrackball_start (cc->trackball,
350                          event->xbutton.x, event->xbutton.y,
351                          MI_WIDTH (mi), MI_HEIGHT (mi));
352       return True;
353     }
354   else if (event->xany.type == ButtonRelease &&
355            event->xbutton.button == Button1)
356     {
357       cc->button_down_p = False;
358       return True;
359     }
360   else if (event->xany.type == ButtonPress &&
361            (event->xbutton.button == Button4 ||
362             event->xbutton.button == Button5))
363     {
364       gltrackball_mousewheel (cc->trackball, event->xbutton.button, 10,
365                               !!event->xbutton.state);
366       return True;
367     }
368   else if (event->xany.type == MotionNotify &&
369            cc->button_down_p)
370     {
371       gltrackball_track (cc->trackball,
372                          event->xmotion.x, event->xmotion.y,
373                          MI_WIDTH (mi), MI_HEIGHT (mi));
374       return True;
375     }
376
377   return False;
378 }
379
380
381 void 
382 init_cube (ModeInfo *mi)
383 {
384   int i;
385   cube_configuration *cc;
386   int wire = MI_IS_WIREFRAME(mi);
387
388   if (!ccs) {
389     ccs = (cube_configuration *)
390       calloc (MI_NUM_SCREENS(mi), sizeof (cube_configuration));
391     if (!ccs) {
392       fprintf(stderr, "%s: out of memory\n", progname);
393       exit(1);
394     }
395   }
396
397   cc = &ccs[MI_SCREEN(mi)];
398
399   if ((cc->glx_context = init_GL(mi)) != NULL) {
400     reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
401   }
402
403   if (!wire)
404     {
405       static GLfloat pos[4] = {1.0, 0.5, 1.0, 0.0};
406       static GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
407       static GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
408
409       glLightfv(GL_LIGHT0, GL_POSITION, pos);
410       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
411       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
412
413       glEnable(GL_LIGHTING);
414       glEnable(GL_LIGHT0);
415       glEnable(GL_DEPTH_TEST);
416       glEnable(GL_CULL_FACE);
417     }
418
419
420   {
421     Bool spinx=False, spiny=False, spinz=False;
422     double spin_speed   = 1.0;
423     double wander_speed = 0.05;
424
425     char *s = do_spin;
426     while (*s)
427       {
428         if      (*s == 'x' || *s == 'X') spinx = True;
429         else if (*s == 'y' || *s == 'Y') spiny = True;
430         else if (*s == 'z' || *s == 'Z') spinz = True;
431         else
432           {
433             fprintf (stderr,
434          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
435                      progname, do_spin);
436             exit (1);
437           }
438         s++;
439       }
440
441     cc->rot = make_rotator (spinx ? spin_speed : 0,
442                             spiny ? spin_speed : 0,
443                             spinz ? spin_speed : 0,
444                             1.0,
445                             do_wander ? wander_speed : 0,
446                             (spinx && spiny && spinz));
447     cc->trackball = gltrackball_init ();
448   }
449
450   cc->ncolors = 256;
451   cc->texture_colors = (XColor *) calloc(cc->ncolors, sizeof(XColor));
452   cc->cube_colors    = (XColor *) calloc(cc->ncolors, sizeof(XColor));
453
454   {
455     double H[3], S[3], V[3];
456     int shift = 60;
457     H[0] = frand(360.0); 
458     H[1] = ((H[0] + shift) < 360) ? (H[0]+shift) : (H[0] + shift - 360);
459     H[2] = ((H[1] + shift) < 360) ? (H[1]+shift) : (H[1] + shift - 360);
460     S[0] = S[1] = S[2] = 1.0;
461     V[0] = V[1] = V[2] = 1.0;
462     make_color_loop(0, 0,
463                     H[0], S[0], V[0], 
464                     H[1], S[1], V[1], 
465                     H[2], S[2], V[2], 
466                     cc->texture_colors, &cc->ncolors,
467                     False, False);
468
469     make_smooth_colormap (0, 0, 0,
470                           cc->cube_colors, &cc->ncolors, 
471                           False, 0, False);
472   }
473
474   cc->ncubes = MI_COUNT (mi);
475   cc->cubes = (cube *) calloc (sizeof(cube), cc->ncubes);
476   for (i = 0; i < cc->ncubes; i++)
477     {
478       cube *cube = &cc->cubes[i];
479       cube->color = random() % cc->ncolors;
480       cube->w = 1.0;
481       cube->h = 1.0;
482       cube->d = 1.0;
483       cube->dx = frand(0.1);
484       cube->dy = frand(0.1);
485       cube->dz = frand(0.1);
486       cube->dw = frand(0.1);
487       cube->dh = frand(0.1);
488       cube->dd = frand(0.1);
489     }
490
491   if (wire)
492     do_texture = False;
493     
494   if (do_texture)
495     {
496       init_texture (mi);
497       init_wave (mi);
498       shuffle_texture (mi);
499     }
500
501   cc->cube_list = glGenLists (1);
502   glNewList (cc->cube_list, GL_COMPILE);
503   unit_cube (wire);
504   glEndList ();
505 }
506
507
508 static void
509 shuffle_cubes (ModeInfo *mi)
510 {
511   cube_configuration *cc = &ccs[MI_SCREEN(mi)];
512   int i;
513   for (i = 0; i < cc->ncubes; i++)
514     {
515 #     define SINOID(SCALE,FRAME,SIZE) \
516         ((((1 + sin((FRAME * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
517
518       cube *cube = &cc->cubes[i];
519       cube->x = SINOID(cube->dx, cube->frame, 0.5);
520       cube->y = SINOID(cube->dy, cube->frame, 0.5);
521       cube->z = SINOID(cube->dz, cube->frame, 0.5);
522       cube->w = SINOID(cube->dw, cube->frame, 0.9) + 1.0;
523       cube->h = SINOID(cube->dh, cube->frame, 0.9) + 1.0;
524       cube->d = SINOID(cube->dd, cube->frame, 0.9) + 1.0;
525       cube->frame++;
526 #     undef SINOID
527     }
528 }
529
530
531 void
532 draw_cube (ModeInfo *mi)
533 {
534   cube_configuration *cc = &ccs[MI_SCREEN(mi)];
535   Display *dpy = MI_DISPLAY(mi);
536   Window window = MI_WINDOW(mi);
537   int i;
538
539   if (!cc->glx_context)
540     return;
541
542   glShadeModel(GL_FLAT);
543
544   glEnable(GL_DEPTH_TEST);
545   glEnable(GL_NORMALIZE);
546   glEnable(GL_CULL_FACE);
547
548   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
549
550   glPushMatrix ();
551
552   glScalef(1.1, 1.1, 1.1);
553
554   {
555     double x, y, z;
556     get_position (cc->rot, &x, &y, &z, !cc->button_down_p);
557     glTranslatef((x - 0.5) * 8,
558                  (y - 0.5) * 6,
559                  (z - 0.5) * 15);
560
561     gltrackball_rotate (cc->trackball);
562
563     get_rotation (cc->rot, &x, &y, &z, !cc->button_down_p);
564     glRotatef (x * 360, 1.0, 0.0, 0.0);
565     glRotatef (y * 360, 0.0, 1.0, 0.0);
566     glRotatef (z * 360, 0.0, 0.0, 1.0);
567   }
568
569   glScalef (2.5, 2.5, 2.5);
570
571   for (i = 0; i < cc->ncubes; i++)
572     {
573       cube *cube = &cc->cubes[i];
574       GLfloat color[4];
575       color[0] = cc->cube_colors[cube->color].red   / 65536.0;
576       color[1] = cc->cube_colors[cube->color].green / 65536.0;
577       color[2] = cc->cube_colors[cube->color].blue  / 65536.0;
578       color[3] = 1.0;
579       cube->color++;
580       if (cube->color >= cc->ncolors) cube->color = 0;
581
582       glPushMatrix ();
583       glTranslatef (cube->x, cube->y, cube->z);
584       glScalef (cube->w, cube->h, cube->d);
585       glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
586       glCallList (cc->cube_list);
587       glPopMatrix ();
588     }
589
590   shuffle_cubes (mi);
591   if (do_texture)
592     shuffle_texture (mi);
593
594   glPopMatrix();
595
596   if (mi->fps_p) do_fps (mi);
597   glFinish();
598
599   glXSwapBuffers(dpy, window);
600 }
601
602 #endif /* USE_GL */