From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / cubestorm.c
1 /* cubestorm, Copyright (c) 2003-2014 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 #define DEFAULTS        "*delay:        30000       \n" \
13                         "*count:      " DEF_COUNT   "\n" \
14                         "*showFPS:      False       \n" \
15                         "*fpsSolid:     True        \n" \
16                         "*wireframe:    False       \n" \
17                         "*suppressRotationAnimation: True\n" \
18
19
20 # define refresh_cube 0
21 # define release_cube 0
22 #undef countof
23 #define countof(x) (sizeof((x))/sizeof((*x)))
24
25 #include "xlockmore.h"
26 #include "colors.h"
27 #include "rotator.h"
28 #include "gltrackball.h"
29 #include <ctype.h>
30
31 #ifdef USE_GL /* whole file */
32
33 #define DEF_SPIN        "True"
34 #define DEF_WANDER      "True"
35 #define DEF_SPEED       "1.0"
36 #define DEF_THICKNESS   "0.06"
37 #define DEF_COUNT       "4"
38 #define DEF_DBUF        "False"
39
40 typedef struct {
41   rotator *rot;
42   int ccolor;
43 } subcube;
44
45 typedef struct {
46   GLXContext *glx_context;
47   trackball_state *trackball;
48   Bool button_down_p;
49   Bool clear_p;
50
51   GLuint cube_list;
52
53   int ncolors;
54   XColor *colors;
55
56   subcube *subcubes;
57
58 } cube_configuration;
59
60 static cube_configuration *bps = NULL;
61
62 static Bool do_spin;
63 static Bool do_wander;
64 static GLfloat speed;
65 static GLfloat thickness;
66 static Bool dbuf_p;
67
68 static XrmOptionDescRec opts[] = {
69   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
70   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
71   { "-wander", ".wander", XrmoptionNoArg, "True" },
72   { "+wander", ".wander", XrmoptionNoArg, "False" },
73   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
74   { "-db",     ".doubleBuffer", XrmoptionNoArg, "True"},
75   { "+db",     ".doubleBuffer", XrmoptionNoArg, "False"},
76   { "-thickness", ".thickness", XrmoptionSepArg, 0 },
77 };
78
79 static argtype vars[] = {
80   {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
81   {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
82   {&speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
83   {&thickness, "thickness", "Thickness",  DEF_THICKNESS,  t_Float},
84   {&dbuf_p, "doubleBuffer", "DoubleBuffer", DEF_DBUF, t_Bool},
85 };
86
87 ENTRYPOINT ModeSpecOpt cube_opts = {countof(opts), opts, countof(vars), vars, NULL};
88
89
90 static void
91 draw_face (ModeInfo *mi)
92 {
93   int wire = MI_IS_WIREFRAME(mi);
94
95   int i;
96   GLfloat t = thickness / 2;
97   GLfloat a = -0.5;
98   GLfloat b =  0.5;
99
100   if (t <= 0) t = 0.001;
101   else if (t > 0.5) t = 0.5;
102
103   glPushMatrix();
104   glFrontFace(GL_CW);
105
106   for (i = 0; i < 4; i++)
107     {
108       glNormal3f (0, 0, -1);
109       glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
110       glVertex3f (a,   a,   a);
111       glVertex3f (b,   a,   a);
112       glVertex3f (b-t, a+t, a);
113       glVertex3f (a+t, a+t, a);
114       glEnd();
115
116       glNormal3f (0, 1, 0);
117       glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
118       glVertex3f (b-t, a+t, a);
119       glVertex3f (b-t, a+t, a+t);
120       glVertex3f (a+t, a+t, a+t);
121       glVertex3f (a+t, a+t, a);
122       glEnd();
123
124       glRotatef(90, 0, 0, 1);
125     }
126   glPopMatrix();
127 }
128
129 static void
130 draw_faces (ModeInfo *mi)
131 {
132   glPushMatrix();
133   draw_face (mi);
134   glRotatef (90,  0, 1, 0); draw_face (mi);
135   glRotatef (90,  0, 1, 0); draw_face (mi);
136   glRotatef (90,  0, 1, 0); draw_face (mi);
137   glRotatef (90,  1, 0, 0); draw_face (mi);
138   glRotatef (180, 1, 0, 0); draw_face (mi);
139   glPopMatrix();
140 }
141
142
143 static void
144 new_cube_colors (ModeInfo *mi)
145 {
146   cube_configuration *bp = &bps[MI_SCREEN(mi)];
147   int i;
148   bp->ncolors = 128;
149   if (bp->colors) free (bp->colors);
150   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
151   make_smooth_colormap (0, 0, 0,
152                         bp->colors, &bp->ncolors,
153                         False, 0, False);
154   for (i = 0; i < MI_COUNT(mi); i++)
155     bp->subcubes[i].ccolor = random() % bp->ncolors;
156 }
157
158
159 /* Window management, etc
160  */
161 ENTRYPOINT void
162 reshape_cube (ModeInfo *mi, int width, int height)
163 {
164   GLfloat h = (GLfloat) height / (GLfloat) width;
165
166   glViewport (0, 0, (GLint) width, (GLint) height);
167
168   glMatrixMode(GL_PROJECTION);
169   glLoadIdentity();
170   gluPerspective (30.0, 1/h, 1.0, 100.0);
171
172   glMatrixMode(GL_MODELVIEW);
173   glLoadIdentity();
174   gluLookAt( 0.0, 0.0, 45.0,
175              0.0, 0.0, 0.0,
176              0.0, 1.0, 0.0);
177
178 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
179   {
180     int o = (int) current_device_rotation();
181     if (o != 0 && o != 180 && o != -180)
182       glScalef (1/h, 1/h, 1/h);
183   }
184 # endif
185
186   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
187 }
188
189
190 ENTRYPOINT Bool
191 cube_handle_event (ModeInfo *mi, XEvent *event)
192 {
193   cube_configuration *bp = &bps[MI_SCREEN(mi)];
194
195   if (gltrackball_event_handler (event, bp->trackball,
196                                  MI_WIDTH (mi), MI_HEIGHT (mi),
197                                  &bp->button_down_p))
198     return True;
199   else if (event->xany.type == KeyPress)
200     {
201       KeySym keysym;
202       char c = 0;
203       XLookupString (&event->xkey, &c, 1, &keysym, 0);
204       if (c == ' ')
205         {
206           glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
207           return True;
208         }
209       else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
210         goto DEF;
211     }
212   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
213     {
214     DEF:
215       new_cube_colors (mi);
216       return True;
217     }
218   return False;
219 }
220
221
222 ENTRYPOINT void 
223 init_cube (ModeInfo *mi)
224 {
225   cube_configuration *bp;
226   int wire = MI_IS_WIREFRAME(mi);
227   int i;
228
229   if (!bps) {
230     bps = (cube_configuration *)
231       calloc (MI_NUM_SCREENS(mi), sizeof (cube_configuration));
232     if (!bps) {
233       fprintf(stderr, "%s: out of memory\n", progname);
234       exit(1);
235     }
236   }
237
238 # ifdef HAVE_JWZGLES
239   dbuf_p = True;
240 # endif
241
242   bp = &bps[MI_SCREEN(mi)];
243
244   bp->glx_context = init_GL(mi);
245
246   if (MI_COUNT(mi) <= 0) MI_COUNT(mi) = 1;
247
248   bp->trackball = gltrackball_init (True);
249   bp->subcubes = (subcube *) calloc (MI_COUNT(mi), sizeof(subcube));
250   for (i = 0; i < MI_COUNT(mi); i++)
251     {
252       double wander_speed, spin_speed, spin_accel;
253
254       if (i == 0)
255         {
256           wander_speed = 0.05 * speed;
257           spin_speed   = 10.0 * speed;
258           spin_accel   = 4.0  * speed;
259         }
260       else
261         {
262           wander_speed = 0;
263           spin_speed   = 4.0 * speed;
264           spin_accel   = 2.0 * speed;
265         }
266
267       bp->subcubes[i].rot = make_rotator (do_spin ? spin_speed : 0,
268                                           do_spin ? spin_speed : 0,
269                                           do_spin ? spin_speed : 0,
270                                           spin_accel,
271                                           do_wander ? wander_speed : 0,
272                                           True);
273     }
274
275   bp->colors = 0;
276   new_cube_colors (mi);
277
278   reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
279
280   if (!wire)
281     {
282       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
283       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
284       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
285       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
286
287       glEnable(GL_LIGHTING);
288       glEnable(GL_LIGHT0);
289       glEnable(GL_DEPTH_TEST);
290       glEnable(GL_CULL_FACE);
291
292       glLightfv(GL_LIGHT0, GL_POSITION, pos);
293       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
294       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
295       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
296     }
297
298   bp->cube_list = glGenLists (1);
299   glNewList (bp->cube_list, GL_COMPILE);
300   draw_faces (mi);
301   glEndList ();
302
303   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
304 }
305
306
307 ENTRYPOINT void
308 draw_cube (ModeInfo *mi)
309 {
310   cube_configuration *bp = &bps[MI_SCREEN(mi)];
311   Display *dpy = MI_DISPLAY(mi);
312   Window window = MI_WINDOW(mi);
313   int wire = MI_IS_WIREFRAME(mi);
314   int i;
315   double x, y, z;
316
317   if (!bp->glx_context)
318     return;
319
320   glDrawBuffer(dbuf_p ? GL_BACK : GL_FRONT);
321
322   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
323
324   glShadeModel(GL_SMOOTH);
325
326   glEnable(GL_DEPTH_TEST);
327   glEnable(GL_NORMALIZE);
328   glEnable(GL_CULL_FACE);
329
330   if (bp->clear_p)   /* we're in "no vapor trails" mode */
331     {
332       glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
333       if (! (random() % (int) (25 / speed)))
334         bp->clear_p = False;
335     }
336   else
337     {
338       if (! (random() % (int) (200 / speed)))
339         {
340           bp->clear_p = True;
341           new_cube_colors (mi);
342         }
343     }
344
345   glPushMatrix ();
346
347   glScalef(1.1, 1.1, 1.1);
348
349   mi->polygon_count = 0;
350
351   get_position (bp->subcubes[0].rot, &x, &y, &z, !bp->button_down_p);
352   glTranslatef((x - 0.5) * 15,
353                (y - 0.5) * 15,
354                (z - 0.5) * 30);
355   gltrackball_rotate (bp->trackball);
356
357   glScalef (4.0, 4.0, 4.0);
358
359   for (i = 0; i < MI_COUNT(mi); i++)
360     {
361       GLfloat bcolor[4] = {0.0, 0.0, 0.0, 1.0};
362       GLfloat bspec[4]  = {1.0, 1.0, 1.0, 1.0};
363       GLfloat bshiny    = 128.0;
364
365       glPushMatrix();
366
367       if (i != 0)  /* N+1 cubes rotate relative to cube 0 */
368         {
369           get_rotation (bp->subcubes[0].rot, &x, &y, &z, False);
370           glRotatef (x * 360, 1.0, 0.0, 0.0);
371           glRotatef (y * 360, 0.0, 1.0, 0.0);
372           glRotatef (z * 360, 0.0, 0.0, 1.0);
373         }
374
375       get_rotation (bp->subcubes[i].rot, &x, &y, &z, !bp->button_down_p);
376       glRotatef (x * 360, 1.0, 0.0, 0.0);
377       glRotatef (y * 360, 0.0, 1.0, 0.0);
378       glRotatef (z * 360, 0.0, 0.0, 1.0);
379
380       bcolor[0] = bp->colors[bp->subcubes[i].ccolor].red   / 65536.0;
381       bcolor[1] = bp->colors[bp->subcubes[i].ccolor].green / 65536.0;
382       bcolor[2] = bp->colors[bp->subcubes[i].ccolor].blue  / 65536.0;
383       bp->subcubes[i].ccolor++;
384       if (bp->subcubes[i].ccolor >= bp->ncolors)
385         bp->subcubes[i].ccolor = 0;
386
387       if (wire)
388         glColor3f (bcolor[0], bcolor[1], bcolor[2]);
389       else
390         {
391           glMaterialfv (GL_FRONT, GL_SPECULAR,            bspec);
392           glMateriali  (GL_FRONT, GL_SHININESS,           bshiny);
393           glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
394         }
395
396       glCallList (bp->cube_list);
397       mi->polygon_count += (4 * 2 * 6);
398
399       glPopMatrix();
400     }
401
402   glPopMatrix ();
403
404   if (mi->fps_p) do_fps (mi);
405   glFinish();
406
407   if (dbuf_p)
408     glXSwapBuffers(dpy, window);
409 }
410
411 XSCREENSAVER_MODULE_2 ("CubeStorm", cubestorm, cube)
412
413 #endif /* USE_GL */