From http://www.jwz.org/xscreensaver/xscreensaver-5.37.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   MI_INIT (mi, bps, NULL);
230
231 # ifdef HAVE_JWZGLES
232   dbuf_p = True;
233 # endif
234
235   bp = &bps[MI_SCREEN(mi)];
236
237   bp->glx_context = init_GL(mi);
238
239   if (MI_COUNT(mi) <= 0) MI_COUNT(mi) = 1;
240
241   bp->trackball = gltrackball_init (True);
242   bp->subcubes = (subcube *) calloc (MI_COUNT(mi), sizeof(subcube));
243   for (i = 0; i < MI_COUNT(mi); i++)
244     {
245       double wander_speed, spin_speed, spin_accel;
246
247       if (i == 0)
248         {
249           wander_speed = 0.05 * speed;
250           spin_speed   = 10.0 * speed;
251           spin_accel   = 4.0  * speed;
252         }
253       else
254         {
255           wander_speed = 0;
256           spin_speed   = 4.0 * speed;
257           spin_accel   = 2.0 * speed;
258         }
259
260       bp->subcubes[i].rot = make_rotator (do_spin ? spin_speed : 0,
261                                           do_spin ? spin_speed : 0,
262                                           do_spin ? spin_speed : 0,
263                                           spin_accel,
264                                           do_wander ? wander_speed : 0,
265                                           True);
266     }
267
268   bp->colors = 0;
269   new_cube_colors (mi);
270
271   reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
272
273   if (!wire)
274     {
275       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
276       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
277       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
278       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
279
280       glEnable(GL_LIGHTING);
281       glEnable(GL_LIGHT0);
282       glEnable(GL_DEPTH_TEST);
283       glEnable(GL_CULL_FACE);
284
285       glLightfv(GL_LIGHT0, GL_POSITION, pos);
286       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
287       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
288       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
289     }
290
291   bp->cube_list = glGenLists (1);
292   glNewList (bp->cube_list, GL_COMPILE);
293   draw_faces (mi);
294   glEndList ();
295
296   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
297 }
298
299
300 ENTRYPOINT void
301 draw_cube (ModeInfo *mi)
302 {
303   cube_configuration *bp = &bps[MI_SCREEN(mi)];
304   Display *dpy = MI_DISPLAY(mi);
305   Window window = MI_WINDOW(mi);
306   int wire = MI_IS_WIREFRAME(mi);
307   int i;
308   double x, y, z;
309
310   if (!bp->glx_context)
311     return;
312
313   glDrawBuffer(dbuf_p ? GL_BACK : GL_FRONT);
314
315   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
316
317   glShadeModel(GL_SMOOTH);
318
319   glEnable(GL_DEPTH_TEST);
320   glEnable(GL_NORMALIZE);
321   glEnable(GL_CULL_FACE);
322
323   if (bp->clear_p)   /* we're in "no vapor trails" mode */
324     {
325       glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
326       if (! (random() % (int) (25 / speed)))
327         bp->clear_p = False;
328     }
329   else
330     {
331       if (! (random() % (int) (200 / speed)))
332         {
333           bp->clear_p = True;
334           new_cube_colors (mi);
335         }
336     }
337
338   glPushMatrix ();
339
340   glScalef(1.1, 1.1, 1.1);
341
342   mi->polygon_count = 0;
343
344   get_position (bp->subcubes[0].rot, &x, &y, &z, !bp->button_down_p);
345   glTranslatef((x - 0.5) * 15,
346                (y - 0.5) * 15,
347                (z - 0.5) * 30);
348   gltrackball_rotate (bp->trackball);
349
350   glScalef (4.0, 4.0, 4.0);
351
352   for (i = 0; i < MI_COUNT(mi); i++)
353     {
354       GLfloat bcolor[4] = {0.0, 0.0, 0.0, 1.0};
355       GLfloat bspec[4]  = {1.0, 1.0, 1.0, 1.0};
356       GLfloat bshiny    = 128.0;
357
358       glPushMatrix();
359
360       if (i != 0)  /* N+1 cubes rotate relative to cube 0 */
361         {
362           get_rotation (bp->subcubes[0].rot, &x, &y, &z, False);
363           glRotatef (x * 360, 1.0, 0.0, 0.0);
364           glRotatef (y * 360, 0.0, 1.0, 0.0);
365           glRotatef (z * 360, 0.0, 0.0, 1.0);
366         }
367
368       get_rotation (bp->subcubes[i].rot, &x, &y, &z, !bp->button_down_p);
369       glRotatef (x * 360, 1.0, 0.0, 0.0);
370       glRotatef (y * 360, 0.0, 1.0, 0.0);
371       glRotatef (z * 360, 0.0, 0.0, 1.0);
372
373       bcolor[0] = bp->colors[bp->subcubes[i].ccolor].red   / 65536.0;
374       bcolor[1] = bp->colors[bp->subcubes[i].ccolor].green / 65536.0;
375       bcolor[2] = bp->colors[bp->subcubes[i].ccolor].blue  / 65536.0;
376       bp->subcubes[i].ccolor++;
377       if (bp->subcubes[i].ccolor >= bp->ncolors)
378         bp->subcubes[i].ccolor = 0;
379
380       if (wire)
381         glColor3f (bcolor[0], bcolor[1], bcolor[2]);
382       else
383         {
384           glMaterialfv (GL_FRONT, GL_SPECULAR,            bspec);
385           glMateriali  (GL_FRONT, GL_SHININESS,           bshiny);
386           glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
387         }
388
389       glCallList (bp->cube_list);
390       mi->polygon_count += (4 * 2 * 6);
391
392       glPopMatrix();
393     }
394
395   glPopMatrix ();
396
397   if (mi->fps_p) do_fps (mi);
398   glFinish();
399
400   if (dbuf_p)
401     glXSwapBuffers(dpy, window);
402 }
403
404 XSCREENSAVER_MODULE_2 ("CubeStorm", cubestorm, cube)
405
406 #endif /* USE_GL */