From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / rubikblocks.c
1 /* rubikblocks, Copyright (c) 2009 Vasek Potocek <vasek.potocek@post.cz>
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 /* RubikBlocks - a Rubik's Mirror Blocks puzzle introduced in 2008.
13  * No mirrors in this version, though, hence the altered name.
14  */
15
16 /* TODO:
17  * add reflection to the faces
18  */
19
20 #define DEFAULTS   "*delay:         20000         \n" \
21                    "*showFPS:       False         \n" \
22                    "*wireframe:     False         \n" \
23                    "*suppressRotationAnimation: True\n" \
24
25 # define refresh_rubikblocks 0
26 #include "xlockmore.h"
27 #include "rotator.h"
28 #include "gltrackball.h"
29
30 #ifdef USE_GL
31
32 #define DEF_SPIN        "True"
33 #define DEF_WANDER      "True"
34 #define DEF_TEXTURE     "True"
35 #define DEF_RANDOMIZE   "False"
36 #define DEF_SPINSPEED   "0.1"
37 #define DEF_ROTSPEED    "3.0"
38 #define DEF_WANDERSPEED "0.005"
39 #define DEF_WAIT        "40.0"
40 #define DEF_CUBESIZE    "1.0"
41
42 #define SHUFFLE 100
43
44 #define TEX_WIDTH  64
45 #define TEX_HEIGHT 64
46 #define BORDER     5
47 #define BORDER2    (BORDER*BORDER)
48
49 #undef countof
50 #define countof(x) (sizeof((x))/sizeof((*x)))
51
52 #define rnd01() ((int)(random()%2))
53
54 /*************************************************************************/
55
56 static Bool spin, wander, rndstart, tex;
57 static float spinspeed, tspeed, wspeed, twait, size;
58
59 static argtype vars[] = {
60   { &spin,      "spin",        "Spin",        DEF_SPIN,        t_Bool},
61   { &wander,    "wander",      "Wander",      DEF_WANDER,      t_Bool},
62   { &rndstart,  "randomize",   "Randomize",   DEF_RANDOMIZE,   t_Bool},
63   { &tex,       "texture",     "Texture",     DEF_TEXTURE,     t_Bool},
64   { &spinspeed, "spinspeed",   "SpinSpeed",   DEF_SPINSPEED,   t_Float},
65   { &tspeed,    "rotspeed",    "RotSpeed",    DEF_ROTSPEED,    t_Float},
66   { &wspeed,    "wanderspeed", "WanderSpeed", DEF_WANDERSPEED, t_Float},
67   { &twait,     "wait",        "Wait",        DEF_WAIT,        t_Float},
68   { &size,      "cubesize",    "CubeSize",    DEF_CUBESIZE,    t_Float},
69 };
70
71 static XrmOptionDescRec opts[] = {
72   { "-spin",        ".spin",        XrmoptionNoArg,  "True" },
73   { "+spin",        ".spin",        XrmoptionNoArg,  "False" },
74   { "-wander",      ".wander",      XrmoptionNoArg,  "True" },
75   { "+wander",      ".wander",      XrmoptionNoArg,  "False" },
76   { "-randomize",   ".randomize",   XrmoptionNoArg,  "True" },
77   { "+randomize",   ".randomize",   XrmoptionNoArg,  "False" },
78   { "-texture",     ".texture",     XrmoptionNoArg,  "True" },
79   { "+texture",     ".texture",     XrmoptionNoArg,  "False" },
80   { "-spinspeed",   ".spinspeed",   XrmoptionSepArg, 0 },
81   { "-wanderspeed", ".wanderspeed", XrmoptionSepArg, 0 },
82   { "-rotspeed",    ".rotspeed",    XrmoptionSepArg, 0 },
83   { "-wait",        ".wait",        XrmoptionSepArg, 0 },
84   { "-cubesize",    ".cubesize",    XrmoptionSepArg, 0 },
85 };
86
87 ENTRYPOINT ModeSpecOpt rubikblocks_opts = {countof(opts), opts, countof(vars), vars, NULL};
88
89 #ifdef USE_MODULES
90 ModStruct   rubikblocks_description =
91 { "rubikblocks", "init_rubikblocks", "draw_rubikblocks", "release_rubikblocks",
92   "draw_rubikblocks", "change_rubikblocks", NULL, &rubikblocks_opts,
93   25000, 1, 1, 1, 1.0, 4, "",
94   "Shows randomly shuffling Rubik's Mirror Blocks puzzle", 0, NULL
95 };
96 #endif
97
98 typedef struct {
99   float         pos[3]; /* _original_ position */
100   float         qr[4];  /* quaternion of rotation */
101   Bool          act;    /* flag if it is undergoing the current rotation */
102 } piece_t;
103
104 typedef struct {
105   GLXContext    *glx_context;
106   rotator       *rot;
107   trackball_state *trackball;
108   GLfloat       ratio;
109   Bool          button_down;
110
111   Bool          pause;          /* pause between two rotations */
112   float         qfram[4];       /* quaternion describing the rotation in one anim. frame */
113   GLfloat       t, tmax;        /* rotation clock */
114   piece_t       pieces[27];     /* type and tilt of all the pieces */
115
116   unsigned char texture[TEX_HEIGHT][TEX_WIDTH];
117   GLuint        list_base;
118   Bool          wire;
119 } rubikblocks_conf;
120
121 static rubikblocks_conf *rubikblocks = NULL;
122
123 static const GLfloat shininess = 20.0;
124 static const GLfloat ambient[] = {0.0, 0.0, 0.0, 1.0};
125 static const GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0};
126 static const GLfloat position0[] = {1.0, 1.0, 1.0, 0.0};
127 static const GLfloat position1[] = {-1.0, -1.0, 1.0, 0.0};
128 static const GLfloat lmodel_ambient[] = {0.1, 0.1, 0.1, 1.0};
129 static const GLfloat material_ambient[] = {0.7, 0.7, 0.7, 1.0};
130 static const GLfloat material_diffuse[] = {0.7, 0.7, 0.7, 1.0};
131 static const GLfloat material_specular[] = {0.2, 0.2, 0.2, 1.0};
132
133 /*************************************************************************/
134
135 /* Multiplies two quaternions, src*dest, and stores the result in dest. */
136 static void
137 mult_quat(float src[4], float dest[4])
138 {
139   float r, i, j, k;
140   r = src[0]*dest[0] - src[1]*dest[1] - src[2]*dest[2] - src[3]*dest[3];
141   i = src[0]*dest[1] + src[1]*dest[0] + src[2]*dest[3] - src[3]*dest[2];
142   j = src[0]*dest[2] + src[2]*dest[0] + src[3]*dest[1] - src[1]*dest[3];
143   k = src[0]*dest[3] + src[3]*dest[0] + src[1]*dest[2] - src[2]*dest[1];
144   dest[0] = r;
145   dest[1] = i;
146   dest[2] = j;
147   dest[3] = k;
148 }
149
150 /* Sets the 'act' flag for pieces which will undergo the rotation. */
151 static void
152 flag_pieces(piece_t pieces[27], int axis, int side)
153 {
154   int i, j;
155   float q[4];
156   for(i = 0; i < 27; i++)
157   {
158     q[0] = 0;
159     q[1] = pieces[i].pos[0];
160     q[2] = pieces[i].pos[1];
161     q[3] = pieces[i].pos[2];
162     mult_quat(pieces[i].qr, q);
163     for(j = 1; j < 4; j++)
164       q[j] = -q[j];
165     mult_quat(pieces[i].qr, q);
166     for(j = 1; j < 4; j++)
167       q[j] = -q[j];
168     if(fabs(q[axis] - side) < 0.1)
169       pieces[i].act = True;
170     else
171       pieces[i].act = False;
172   }
173 }
174
175 /* "Rounds" the value to the nearest from the set {0, +-1/2, +-1/sqrt(2), +-1}.
176  * It is guaranteed to be pretty close to one when this function is called. */
177 static float 
178 settle_value(float v) 
179 {
180   if(v > 0.9) return 1;
181   else if(v < -0.9) return -1;
182   else if(v > 0.6) return M_SQRT1_2;
183   else if(v < -0.6) return -M_SQRT1_2;
184   else if(v > 0.4) return 0.5;
185   else if(v < -0.4) return -0.5;
186   else return 0;
187 }
188
189 static void 
190 randomize(rubikblocks_conf *cp) 
191 {
192   int axis, side;
193   int i, j;
194   for(i = 0; i < SHUFFLE; i++)
195   {
196     axis = (random()%3)+1;
197     side = rnd01()*2-1;
198     flag_pieces(cp->pieces, axis, side);
199     for(j = 1; j < 4; j++)
200       cp->qfram[j] = 0;
201     cp->qfram[0] = M_SQRT1_2;
202     cp->qfram[axis] = M_SQRT1_2;
203     for(j = 0; j < 27; j++)
204     {
205       if(cp->pieces[j].act)
206         mult_quat(cp->qfram, cp->pieces[j].qr);
207     }
208   }
209 }
210
211 static void 
212 finish(rubikblocks_conf *cp) 
213 {
214   static int axis = 1;
215   int side, angle;
216   int i, j;
217   if(cp->pause)
218   {
219     switch(axis) 
220     {
221       case 1:
222         axis = rnd01()+2;
223         break;
224       case 2:
225         axis = 2*rnd01()+1;
226         break;
227       default:
228         axis = rnd01()+1;
229     }
230     side = rnd01()*2-1;
231     angle = rnd01()+1;
232     flag_pieces(cp->pieces, axis, side);
233     cp->pause = False;
234     cp->tmax = 90.0*angle;
235     for(i = 1; i < 4; i++)
236       cp->qfram[i] = 0;
237     cp->qfram[0] = cos(tspeed*M_PI/360);
238     cp->qfram[axis] = sin((rnd01()*2-1)*tspeed*M_PI/360);
239   }
240   else
241   {
242     for(i = 0; i < 27; i++)
243     {
244       for(j = 0; j < 4; j++)
245       {
246         cp->pieces[i].qr[j] = settle_value(cp->pieces[i].qr[j]);
247       }
248     }
249     cp->pause = True;
250     cp->tmax = twait;
251   }
252   cp->t = 0;
253 }
254
255 static Bool 
256 draw_main(ModeInfo *mi, rubikblocks_conf *cp) 
257 {
258   int i;
259   double x, y, z;
260
261   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
262   glLoadIdentity();
263
264   get_position(cp->rot, &x, &y, &z, !cp->button_down);
265   glTranslatef((x-0.5)*6, (y-0.5)*6, -20);
266
267   gltrackball_rotate(cp->trackball);
268
269   get_rotation(cp->rot, &x, &y, &z, !cp->button_down);
270   glRotatef(x*360, 1, 0, 0);
271   glRotatef(y*360, 0, 1, 0);
272   glRotatef(z*360, 0, 0, 1);
273   glScalef(size, size, size);
274
275 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
276   {
277     GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
278     int o = (int) current_device_rotation();
279     if (o != 0 && o != 180 && o != -180)
280       glScalef (1/h, 1/h, 1/h);
281   }
282 # endif
283
284   if(cp->wire) glColor3f(0.7, 0.7, 0.7);
285   if(!cp->pause)
286     for(i = 0; i < 27; i++)
287       if(cp->pieces[i].act)
288         mult_quat(cp->qfram, cp->pieces[i].qr);
289   for(i = 0; i < 27; i++) 
290   {
291     glPushMatrix();
292     if(fabs(cp->pieces[i].qr[0]) < 1)
293       glRotatef(360/M_PI*acos(cp->pieces[i].qr[0]),
294           cp->pieces[i].qr[1], cp->pieces[i].qr[2], cp->pieces[i].qr[3]);
295     glCallList(cp->list_base + i);
296     glPopMatrix();
297   }
298   if((cp->t += tspeed) > cp->tmax) finish(cp);
299   return True;
300 }
301
302 static void 
303 draw_horz_line(rubikblocks_conf *cp, int x1, int x2, int y) 
304 {
305   int x, y0 = y, w;
306   if(y < BORDER) y = -y;
307   else y = -BORDER;
308   for(; y < BORDER; y++) {
309     if(y0+y >= TEX_HEIGHT) break;
310     w = y*y*255/BORDER2;
311     for(x = x1; x <= x2; x++)
312       if(cp->texture[y0+y][x]>w) cp->texture[y0+y][x] = w;
313   }
314 }
315
316 static void 
317 draw_vert_line(rubikblocks_conf *cp, int x, int y1, int y2) 
318 {
319   int x0 = x, y, w;
320   if(x<BORDER) x = -x;
321   else x = -BORDER;
322   for(; x < BORDER; x++) {
323     if(x0+x >= TEX_WIDTH) break;
324     w = x*x*255/BORDER2;
325     for(y = y1; y <= y2; y++)
326       if(cp->texture[y][x0+x]>w) cp->texture[y][x0+x] = w;
327   }
328 }
329
330 static void 
331 make_texture(rubikblocks_conf *cp) 
332 {
333   int x, y;
334   for(y = 0; y < TEX_HEIGHT; y++)
335     for(x = 0; x < TEX_WIDTH; x++)
336       cp->texture[y][x] = 255;
337   draw_horz_line(cp, 0, TEX_WIDTH-1, 0);
338   draw_horz_line(cp, 0, TEX_WIDTH-1, TEX_HEIGHT-1);
339   draw_vert_line(cp, 0, 0, TEX_HEIGHT-1);
340   draw_vert_line(cp, TEX_WIDTH-1, 0, TEX_HEIGHT-1);
341 }
342
343 /* These simple transforms make the actual shape of the pieces. The parameters
344  * A, B and C affect the excentricity of the pieces in each direction. */
345 static float 
346 fx(float x)
347 {
348   const float A = 0.5;
349   if(x > 1.4) return 1.5 - A;
350   else if(x < -1.4) return -1.5 - A;
351   else return x;
352 }
353
354 static float 
355 fy(float y)
356 {
357   const float B = 0.25;
358   if(y > 1.4) return 1.5 - B;
359   else if(y < -1.4) return -1.5 - B;
360   else return y;
361 }
362
363 static float 
364 fz(float z)
365 {
366   const float C = 0.0;
367   if(z > 1.4) return 1.5 - C;
368   else if(z < -1.4) return -1.5 - C;
369   else return z;
370 }
371
372 static void 
373 init_lists(rubikblocks_conf *cp)
374 {
375   GLuint base;
376   int i;
377   float x, y, z;
378   base = cp->list_base = glGenLists(27);
379   for(i = 0; i < 27; i++)
380   {
381     x = cp->pieces[i].pos[0];
382     y = cp->pieces[i].pos[1];
383     z = cp->pieces[i].pos[2];
384     glNewList(base+i, GL_COMPILE);
385     glBegin(GL_QUAD_STRIP);
386     glNormal3f(1, 0, 0);
387     glTexCoord2f(0, 0);
388     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
389     glTexCoord2f(0, 1);
390     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
391     glTexCoord2f(1, 0);
392     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z+0.5));
393     glTexCoord2f(1, 1);
394     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z+0.5));
395     glNormal3f(0, 0, 1);
396     glTexCoord2f(0, 0);
397     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z+0.5));
398     glTexCoord2f(0, 1);
399     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z+0.5));
400     glNormal3f(-1, 0, 0);
401     glTexCoord2f(1, 0);
402     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z-0.5));
403     glTexCoord2f(1, 1);
404     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z-0.5));
405     glNormal3f(0, 0, -1);
406     glTexCoord2f(0, 0);
407     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
408     glTexCoord2f(0, 1);
409     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
410     glEnd();
411     glBegin(GL_QUADS);
412     glNormal3f(0, 1, 0);
413     glTexCoord2f(0, 0);
414     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z+0.5));
415     glTexCoord2f(0, 1);
416     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
417     glTexCoord2f(1, 1);
418     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z-0.5));
419     glTexCoord2f(1, 0);
420     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z+0.5));
421     glNormal3f(0, -1, 0);
422     glTexCoord2f(0, 0);
423     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
424     glTexCoord2f(0, 1);
425     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z+0.5));
426     glTexCoord2f(1, 1);
427     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z+0.5));
428     glTexCoord2f(1, 0);
429     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z-0.5));
430     glEnd();
431     glEndList();
432   }
433 }
434
435 /* It looks terrible... FIXME: any other ideas, maybe some anisotropic filtering? */
436 /*#define MIPMAP*/
437
438 static void 
439 init_gl(ModeInfo *mi) 
440 {
441   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
442 #ifdef MIPMAP
443   int status;
444 #endif
445   cp->wire = MI_IS_WIREFRAME(mi);
446
447 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
448   cp->wire = 0;
449 # endif
450
451   if(MI_IS_MONO(mi))
452     tex = False;
453   if(cp->wire) {
454     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
455     return;
456   }
457
458   glClearDepth(1.0);
459   glDrawBuffer(GL_BACK);
460   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
461   glShadeModel(GL_FLAT);
462   glDepthFunc(GL_LESS);
463   glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
464   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
465   glLightfv(GL_LIGHT0, GL_POSITION, position0);
466   glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
467   glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
468   glLightfv(GL_LIGHT1, GL_POSITION, position1);
469   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
470   glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
471   glEnable(GL_DEPTH_TEST);
472   glEnable(GL_LIGHT0);
473   glEnable(GL_LIGHT1);
474   glEnable(GL_LIGHTING);
475   glEnable(GL_NORMALIZE);
476   glEnable(GL_COLOR_MATERIAL);
477   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
478   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
479   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
480   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
481   if (!tex) return;
482   glEnable(GL_TEXTURE_2D);
483 #ifdef MIPMAP
484   clear_gl_error();
485   status = gluBuild2DMipmaps(GL_TEXTURE_2D, 1, TEX_WIDTH, TEX_HEIGHT,
486       GL_LUMINANCE, GL_UNSIGNED_BYTE, cp->texture);
487   if (status) {
488     const char *s = (char *)gluErrorString(status);
489     fprintf (stderr, "%s: error mipmapping texture: %s\n", progname, (s?s:"(unknown)"));
490     exit (1);
491   }
492   check_gl_error("mipmapping");
493 #else    
494   glTexImage2D(GL_TEXTURE_2D, 0, 1, TEX_WIDTH, TEX_HEIGHT,
495       0, GL_LUMINANCE, GL_UNSIGNED_BYTE, cp->texture);
496 #endif  
497   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
498   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
499   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
500   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
501 #ifdef MIPMAP
502   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
503 #else
504   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
505 #endif
506 }
507
508 static void 
509 init_cp(rubikblocks_conf *cp) 
510 {
511   int i, j, k, m;
512
513   cp->pause = True;
514   cp->t = 0.0;
515   cp->tmax = twait;
516
517   for(i = -1, m = 0; i <= 1; i++)
518     for(j = -1; j <= 1; j++)
519       for(k = -1; k <= 1; k++)
520       {
521         cp->pieces[m].pos[0] = k;
522         cp->pieces[m].pos[1] = j;
523         cp->pieces[m].pos[2] = i;
524         cp->pieces[m].qr[0] = 1;
525         cp->pieces[m].qr[1] = 0;
526         cp->pieces[m].qr[2] = 0;
527         cp->pieces[m].qr[3] = 0;
528         m++;
529       }
530
531   cp->rot = make_rotator(spin?spinspeed:0, spin?spinspeed:0, spin?spinspeed:0,
532       0.1, wander?wspeed:0, True);
533   cp->trackball = gltrackball_init(True);
534
535   if(rndstart) randomize(cp);
536 }
537
538 /*************************************************************************/
539
540 ENTRYPOINT void 
541 reshape_rubikblocks(ModeInfo *mi, int width, int height) 
542 {
543   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
544   if(!height) height = 1;
545   cp->ratio = (GLfloat)width/(GLfloat)height;
546   glViewport(0, 0, (GLint) width, (GLint) height);
547   glMatrixMode(GL_PROJECTION);
548   glLoadIdentity();
549   gluPerspective(30.0, cp->ratio, 1.0, 100.0);
550   glMatrixMode(GL_MODELVIEW);
551   glClear(GL_COLOR_BUFFER_BIT);
552 }
553
554 ENTRYPOINT void 
555 release_rubikblocks(ModeInfo *mi) 
556 {
557   if (rubikblocks != NULL) 
558   {
559     int screen;
560     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) 
561     {
562       rubikblocks_conf *cp = &rubikblocks[screen];
563       if (cp->glx_context) {
564         cp->glx_context = NULL;
565       }
566     }
567     free((void *)rubikblocks);
568     rubikblocks = NULL;
569   }
570   FreeAllGL(mi);
571 }
572
573 ENTRYPOINT void 
574 init_rubikblocks(ModeInfo *mi) 
575 {
576   rubikblocks_conf *cp;
577   if(!rubikblocks) 
578   {
579     rubikblocks = (rubikblocks_conf *)calloc(MI_NUM_SCREENS(mi), sizeof(rubikblocks_conf));
580     if(!rubikblocks) return;
581   }
582   cp = &rubikblocks[MI_SCREEN(mi)];
583
584   if(tex)
585     make_texture(cp);
586
587   if ((cp->glx_context = init_GL(mi)) != NULL) 
588   {
589     init_gl(mi);
590     init_cp(cp);
591     init_lists(cp);
592     reshape_rubikblocks(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
593   }
594   else 
595   {
596     MI_CLEARWINDOW(mi);
597   }
598 }
599
600 ENTRYPOINT void 
601 draw_rubikblocks(ModeInfo * mi) 
602 {
603   Display *display = MI_DISPLAY(mi);
604   Window window = MI_WINDOW(mi);
605   rubikblocks_conf *cp;
606   if (!rubikblocks) return;
607   cp = &rubikblocks[MI_SCREEN(mi)];
608   MI_IS_DRAWN(mi) = True;
609   if (!cp->glx_context) return;
610   mi->polygon_count = 0;
611   glXMakeCurrent(display, window, *(cp->glx_context));
612   if (!draw_main(mi, cp)) 
613   {
614     release_rubikblocks(mi);
615     return;
616   }
617   if (MI_IS_FPS(mi)) do_fps (mi);
618   glFlush();
619   glXSwapBuffers(display, window);
620 }
621
622 #ifndef STANDALONE
623 ENTRYPOINT void 
624 change_rubikblocks(ModeInfo * mi) 
625 {
626   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
627   if (!cp->glx_context) return;
628   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(cp->glx_context));
629   init_gl(mi);
630 }
631 #endif /* !STANDALONE */
632
633 ENTRYPOINT Bool
634 rubikblocks_handle_event (ModeInfo *mi, XEvent *event)
635 {
636   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
637
638   if (gltrackball_event_handler (event, cp->trackball,
639                                  MI_WIDTH (mi), MI_HEIGHT (mi),
640                                  &cp->button_down))
641     return True;
642
643   return False;
644 }
645
646
647 XSCREENSAVER_MODULE ("RubikBlocks", rubikblocks)
648
649 #endif