From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / sballs.c
1 /* sballs --- balls spinning like crazy in GL */
2
3 #if 0
4 static const char sccsid[] = "@(#)sballs.c      5.02 2001/03/10 xlockmore";
5 #endif
6
7 /* Copyright (c) E. Lassauge, 2001. */
8
9 /*
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * The original code for this mode was written by 
23  * Mustata Bogdan (LoneRunner) <lonerunner@planetquake.com>
24  * and can be found at http://www.cfxweb.net/lonerunner/
25  *
26  * Eric Lassauge  (November-07-2000) <lassauge@users.sourceforge.net>
27  *                                  http://lassauge.free.fr/linux.html
28  *
29  * REVISION HISTORY:
30  *
31  * E.Lassauge - 03-Oct-2001:
32  *      - minor bugfixes - get ready for xscreensaver
33  * E.Lassauge - 09-Mar-2001:
34  *      - get rid of my framerate options to use showfps
35  * E.Lassauge - 28-Nov-2000:
36  *      - add handling of polyhedrons (like in ico)
37  *      - modified release part to add freeing of GL objects
38  * E.Lassauge - 14-Nov-2000:
39  *      - use new common xpm_to_ximage function
40  *
41  */
42
43 #ifdef STANDALONE       /* xscreensaver mode */
44 #define DEFAULTS        "*delay:        30000 \n" \
45                         "*size:             0 \n" \
46                         "*cycles:           4 \n" \
47                         "*showFPS:      False \n" \
48                         "*wireframe:    False \n" \
49
50 # define refresh_sballs 0
51 # define release_sballs 0
52 #define MODE_sballs
53 #include "xlockmore.h"          /* from the xscreensaver distribution */
54 #include "gltrackball.h"
55 #else                           /* !STANDALONE */
56 #include "xlock.h"              /* from the xlockmore distribution */
57 #include "visgl.h"
58 #endif                          /* !STANDALONE */
59
60 #ifdef MODE_sballs
61
62 #define MINSIZE         32      /* minimal viewport size */
63 #define FRAME           50      /* frame count interval */
64 #define MAX_OBJ         8       /* number of 3D objects */
65
66 #if defined( USE_XPM ) || defined( USE_XPMINC ) || defined( STANDALONE )
67 /* USE_XPM & USE_XPMINC in xlock mode ; HAVE_XPM in xscreensaver mode */
68 # include "xpm-ximage.h"
69 # define I_HAVE_XPM
70
71 # ifdef STANDALONE
72
73 #  ifdef __GNUC__
74    __extension__ /* don't warn about "string length is greater than the length
75                     ISO C89 compilers are required to support" when including
76                     the following XPM file... */
77 #  endif
78 #  include "../images/sball.xpm"
79 #  ifdef __GNUC__
80    __extension__
81 #  endif
82 #  include "../images/sball-bg.xpm"
83 # else /* !STANDALONE */
84 #  include "pixmaps/sball.xpm"
85 #  include "pixmaps/sball-bg.xpm"
86 # endif /* !STANDALONE */
87 #endif /* HAVE_XPM */
88
89 /* Manage option vars */
90 #define DEF_TEXTURE     "True"
91 #define DEF_OBJECT      "0"
92 static Bool do_texture;
93 static int  object;
94 static int  spheres;
95
96 static XrmOptionDescRec opts[] = {
97     {"-texture", ".sballs.texture", XrmoptionNoArg, "on"},
98     {"+texture", ".sballs.texture", XrmoptionNoArg, "off"},
99     {"-object", ".sballs.object", XrmoptionSepArg, 0},
100
101 };
102
103 static argtype vars[] = {
104     {&do_texture,    "texture",    "Texture",    DEF_TEXTURE,    t_Bool},
105     {&object,        "object",     "Object",     DEF_OBJECT,     t_Int},
106
107 };
108
109 static OptionStruct desc[] = {
110     /*{"-count spheres", "set number of spheres"},*/
111     /*{"-cycles speed", "set ball speed value"},*/
112     {"-/+texture", "turn on/off texturing"},
113     {"-object num", "number of the 3D object (0 means random)"},
114 };
115
116 ENTRYPOINT ModeSpecOpt sballs_opts =
117  { sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc };
118
119 #ifdef USE_MODULES
120 ModStruct sballs_description =
121     { "sballs", "init_sballs", "draw_sballs", NULL,
122     "draw_sballs", "change_sballs", (char *) NULL, &sballs_opts,
123     /*
124     delay,count,cycles,size,ncolors,sat
125      */
126     10000, 0, 10, 400, 64, 1.0, "",
127     "balls spinning like crazy in GL", 0, NULL
128 };
129 #endif /* USE_MODULES */
130
131 /* misc types and defines */
132 #define vinit(a,i,j,k) {\
133   (a)[0]=i;\
134   (a)[1]=j;\
135   (a)[2]=k;\
136 }
137 typedef float vec_t;
138 typedef vec_t vec3_t[3];
139
140 #define MAX_BALLS       20
141
142 /* the mode struct, contains all per screen variables */
143 typedef struct {
144     GLint WIDTH, HEIGHT;        /* display dimensions */
145     GLXContext *glx_context;
146
147
148     XImage *btexture;           /* back texture image bits */
149     XImage *ftexture;           /* face texture image bits */
150     GLuint backid;              /* back texture id: GL world */
151     GLuint faceid;              /* face texture id: GL world */
152
153     vec3_t eye;
154     vec3_t rotm;
155     int speed;
156     float radius[MAX_BALLS];
157
158     trackball_state *trackball;
159     Bool button_down_p;
160
161 } sballsstruct;
162
163 /* array of sballsstruct indexed by screen number */
164 static sballsstruct *sballs = (sballsstruct *) NULL;
165
166 /* lights */
167 static const float LightAmbient[]=   { 1.0f, 1.0f, 1.0f, 1.0f };
168 static const float LightDiffuse[]=   { 1.0f, 1.0f, 1.0f, 1.0f };
169 static const float LightPosition[]=  { 0.0f, 0.0f, 4.0f, 1.0f };
170
171 /* structure of the polyhedras */
172 typedef struct {
173         const char *longname;   /* long name of object */
174         const char *shortname;  /* short name of object */
175         int         numverts;   /* number of vertices */
176         float       radius;     /* radius */
177         vec3_t      v[MAX_BALLS];/* the vertices */
178 } Polyinfo;
179
180 static const Polyinfo polygons[] =
181 {
182
183 /* 0: objtetra - structure values for tetrahedron */
184         {
185                 "tetrahedron", "tetra",         /* long and short names */
186                 4,                              /* number of vertices */
187                 0.8,
188                 {                               /* vertices (x,y,z) */
189                         /* all points must be within radius 2 of the origin */
190 #define T 1.0
191                         {T, T, T},
192                         {T, -T, -T},
193                         {-T, T, -T},
194                         {-T, -T, T},
195 #undef T
196                 }
197         },
198
199 /* 1: objcube - structure values for cube */
200
201         {
202                 "hexahedron", "cube",           /* long and short names */
203                 8,                              /* number of vertices, edges, and faces */
204                 0.6,
205                 {                               /* vertices (x,y,z) */
206                         /* all points must be within radius 2 of the origin */
207 #define T 1.0
208                         {T, T, T},
209                         {T, T, -T},
210                         {T, -T, -T},
211                         {T, -T, T},
212                         {-T, T, T},
213                         {-T, T, -T},
214                         {-T, -T, -T},
215                         {-T, -T, T},
216 #undef T
217                 }
218         },
219
220 /* 2: objocta - structure values for octahedron */
221
222         {
223                 "octahedron", "octa",   /* long and short names */
224                 6,                      /* number of vertices */
225                 0.6,
226                 {                       /* vertices (x,y,z) */
227                         /* all points must be within radius 2 of the origin */
228 #define T 1.5
229                         {T, 0, 0},
230                         {-T, 0, 0},
231                         {0, T, 0},
232                         {0, -T, 0},
233                         {0, 0, T},
234                         {0, 0, -T},
235 #undef T
236                 }
237         },
238 /* 3: objdodec - structure values for dodecahedron */
239
240         {
241                 "dodecahedron", "dodeca",       /* long and short names */
242                 20,                             /* number of vertices */
243                 0.35,
244                 {                               /* vertices (x,y,z) */
245                         /* all points must be within radius 2 of the origin */
246                         {0.000000, 0.500000, 1.000000},
247                         {0.000000, -0.500000, 1.000000},
248                         {0.000000, -0.500000, -1.000000},
249                         {0.000000, 0.500000, -1.000000},
250                         {1.000000, 0.000000, 0.500000},
251                         {-1.000000, 0.000000, 0.500000},
252                         {-1.000000, 0.000000, -0.500000},
253                         {1.000000, 0.000000, -0.500000},
254                         {0.500000, 1.000000, 0.000000},
255                         {-0.500000, 1.000000, 0.000000},
256                         {-0.500000, -1.000000, 0.000000},
257                         {0.500000, -1.000000, 0.000000},
258                         {0.750000, 0.750000, 0.750000},
259                         {-0.750000, 0.750000, 0.750000},
260                         {-0.750000, -0.750000, 0.750000},
261                         {0.750000, -0.750000, 0.750000},
262                         {0.750000, -0.750000, -0.750000},
263                         {0.750000, 0.750000, -0.750000},
264                         {-0.750000, 0.750000, -0.750000},
265                         {-0.750000, -0.750000, -0.750000},
266                 }
267         },
268
269 /* 4: objicosa - structure values for icosahedron */
270
271         {
272                 "icosahedron", "icosa",         /* long and short names */
273                 12,                             /* number of vertices */
274                 0.4,
275                 {                               /* vertices (x,y,z) */
276                         /* all points must be within radius 2 of the origin */
277                         {0.00000000, 0.00000000, -0.95105650},
278                         {0.00000000, 0.85065080, -0.42532537},
279                         {0.80901698, 0.26286556, -0.42532537},
280                         {0.50000000, -0.68819095, -0.42532537},
281                         {-0.50000000, -0.68819095, -0.42532537},
282                         {-0.80901698, 0.26286556, -0.42532537},
283                         {0.50000000, 0.68819095, 0.42532537},
284                         {0.80901698, -0.26286556, 0.42532537},
285                         {0.00000000, -0.85065080, 0.42532537},
286                         {-0.80901698, -0.26286556, 0.42532537},
287                         {-0.50000000, 0.68819095, 0.42532537},
288                         {0.00000000, 0.00000000, 0.95105650}
289                 }
290         },
291
292 /* 5: objplane - structure values for plane */
293
294         {
295                 "plane", "plane",       /* long and short names */
296                 4,                      /* number of vertices */
297                 0.7,
298                 {                       /* vertices (x,y,z) */
299                         /* all points must be within radius 2 of the origin */
300 #define T 1.1
301                         {T, 0, 0},
302                         {-T, 0, 0},
303                         {0, T, 0},
304                         {0, -T, 0},
305 #undef T
306                 }
307         },
308
309 /* 6: objpyr - structure values for pyramid */
310
311         {
312                 "pyramid", "pyramid",   /* long and short names */
313                 5,                      /* number of vertices */ 
314                 0.5,
315                 {                       /* vertices (x,y,z) */
316                         /* all points must be within radius 1 of the origin */
317 #define T 1.0
318                         {T, 0, 0},
319                         {-T, 0, 0},
320                         {0, T, 0},
321                         {0, -T, 0},
322                         {0, 0, T},
323 #undef T
324                 }
325         },
326
327 /* 7: objstar - structure values for octahedron star (stellated octahedron?) */
328         {
329                 "star", "star", /* long and short names */
330                 8,              /* number of vertices */
331                 0.7,
332                 {               /* vertices (x,y,z) */
333                         /* all points must be within radius 1 of the origin */
334 #define T 0.9
335                         {T, T, T},
336                         {T, -T, -T},
337                         {-T, T, -T},
338                         {-T, -T, T},
339                         {-T, -T, -T},
340                         {-T, T, T},
341                         {T, -T, T},
342                         {T, T, -T},
343 #undef T
344                 }
345         },
346
347 };
348
349
350
351
352 /*
353  *-----------------------------------------------------------------------------
354  *-----------------------------------------------------------------------------
355  *    Misc funcs.
356  *-----------------------------------------------------------------------------
357  *-----------------------------------------------------------------------------
358  */
359
360
361 /* initialise textures */
362 static void inittextures(ModeInfo * mi)
363 {
364     sballsstruct *sb = &sballs[MI_SCREEN(mi)];
365
366 #if defined( I_HAVE_XPM )
367     if (do_texture) {
368
369         glGenTextures(1, &sb->backid);
370 #ifdef HAVE_GLBINDTEXTURE
371         glBindTexture(GL_TEXTURE_2D, sb->backid);
372 #endif /* HAVE_GLBINDTEXTURE */
373
374         sb->btexture = xpm_to_ximage(MI_DISPLAY(mi),
375                                      MI_VISUAL(mi),
376                                      MI_COLORMAP(mi),
377                                      sball_bg);
378         if (!(sb->btexture)) {
379             (void) fprintf(stderr, "Error reading the background texture.\n");
380             glDeleteTextures(1, &sb->backid);
381             do_texture = False;
382             sb->faceid = 0;       /* default textures */
383             sb->backid = 0;
384             return;
385         }
386
387         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
388         clear_gl_error();
389         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
390                      sb->btexture->width, sb->btexture->height, 0,
391                      GL_RGBA,
392                      /* GL_UNSIGNED_BYTE, */
393                      GL_UNSIGNED_INT_8_8_8_8_REV,
394                      sb->btexture->data);
395         check_gl_error("texture");
396
397         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
398         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
399
400         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
401         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
402
403         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
404
405         glGenTextures(1, &sb->faceid);
406 #ifdef HAVE_GLBINDTEXTURE
407         glBindTexture(GL_TEXTURE_2D, sb->faceid);
408 #endif /* HAVE_GLBINDTEXTURE */
409
410         sb->ftexture = xpm_to_ximage(MI_DISPLAY(mi),
411                                      MI_VISUAL(mi),
412                                      MI_COLORMAP(mi),
413                                      sball);
414         if (!(sb->ftexture)) {
415             (void) fprintf(stderr, "Error reading the face texture.\n");
416             glDeleteTextures(1, &sb->faceid);
417             sb->faceid = 0;
418             return;
419         }
420
421         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
422         clear_gl_error();
423         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
424                      sb->ftexture->width, sb->ftexture->height, 0,
425                      GL_RGBA,
426                      /* GL_UNSIGNED_BYTE, */
427                      GL_UNSIGNED_INT_8_8_8_8_REV,
428                      sb->ftexture->data);
429         check_gl_error("texture");
430
431         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
432         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
433
434         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
435         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
436
437         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
438     }
439     else
440     {
441         sb->faceid = 0;       /* default textures */
442         sb->backid = 0;
443     }
444 #else /* !I_HAVE_XPM */
445   do_texture = False;
446   sb->faceid = 0;       /* default textures */
447   sb->backid = 0;
448 #endif /* !I_HAVE_XPM */
449 }
450
451 static void drawSphere(ModeInfo * mi,int sphere_num)
452 {
453   sballsstruct *sb = &sballs[MI_SCREEN(mi)];
454   float x = polygons[object].v[sphere_num][0];
455   float y = polygons[object].v[sphere_num][1];
456   float z = polygons[object].v[sphere_num][2];
457   int numMajor = 15;
458   int numMinor = 30;
459   float radius = sb->radius[sphere_num];
460   double majorStep = (M_PI / numMajor);
461   double minorStep = (2.0 * M_PI / numMinor);
462   int i, j;
463
464   glPushMatrix();
465   glTranslatef(x, y, z);
466
467   glColor4f(1, 1, 1, 1);
468   for (i = 0; i < numMajor; ++i)
469   {
470     double a = i * majorStep;
471     double b = a + majorStep;
472     double r0 = radius * sin(a);
473     double r1 = radius * sin(b);
474     GLfloat z0 = radius * cos(a);
475     GLfloat z1 = radius * cos(b);
476
477     glBegin(MI_IS_WIREFRAME(mi) ? GL_LINE_STRIP: GL_TRIANGLE_STRIP);
478     for (j = 0; j <= numMinor; ++j)
479         {
480       double c = j * minorStep;
481       GLfloat x = cos(c);
482       GLfloat y = sin(c);
483
484       glNormal3f((x * r0) / radius, (y * r0) / radius, z0 / radius);
485       glTexCoord2f(j / (GLfloat) numMinor, i / (GLfloat) numMajor);
486       glVertex3f(x * r0, y * r0, z0);
487
488       glNormal3f((x * r1) / radius, (y * r1) / radius, z1 / radius);
489       glTexCoord2f(j / (GLfloat) numMinor, (i + 1) / (GLfloat) numMajor);
490       glVertex3f(x * r1, y * r1, z1);
491
492       mi->polygon_count++;
493     }
494     glEnd();
495   }
496
497   glPopMatrix();
498 }
499
500 /*
501  *-----------------------------------------------------------------------------
502  *-----------------------------------------------------------------------------
503  *    GL funcs.
504  *-----------------------------------------------------------------------------
505  *-----------------------------------------------------------------------------
506  */
507
508 #ifndef STANDALONE
509 static void Reshape(ModeInfo * mi)
510 #else
511 ENTRYPOINT void reshape_sballs(ModeInfo * mi, int width, int height)
512 #endif
513 {
514
515     sballsstruct *sb = &sballs[MI_SCREEN(mi)];
516     int size = MI_SIZE(mi);
517
518     /* Viewport is specified size if size >= MINSIZE && size < screensize */
519     if (size <= 1) {
520         sb->WIDTH = MI_WIDTH(mi);
521         sb->HEIGHT = MI_HEIGHT(mi);
522     } else if (size < MINSIZE) {
523         sb->WIDTH = MINSIZE;
524         sb->HEIGHT = MINSIZE;
525     } else {
526         sb->WIDTH = (size > MI_WIDTH(mi)) ? MI_WIDTH(mi) : size;
527         sb->HEIGHT = (size > MI_HEIGHT(mi)) ? MI_HEIGHT(mi) : size;
528     }
529     glViewport((MI_WIDTH(mi) - sb->WIDTH) / 2, (MI_HEIGHT(mi) - sb->HEIGHT) / 2, sb->WIDTH, sb->HEIGHT);
530     glMatrixMode(GL_PROJECTION);
531     glLoadIdentity();
532     gluPerspective(55.0, (float)sb->WIDTH / (float) sb->HEIGHT, 1.0, 300.0);
533
534     glMatrixMode(GL_MODELVIEW);
535
536 }
537
538 static void Draw(ModeInfo * mi)
539 {
540     sballsstruct *sb = &sballs[MI_SCREEN(mi)];
541     int sphere;
542
543     mi->polygon_count = 0;
544
545     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
546
547     glPushMatrix();
548     glEnable(GL_DEPTH_TEST);
549
550     /* move eyes */
551     glTranslatef (-sb->eye[0], -sb->eye[1], -sb->eye[2]);
552
553     /* draw background */
554     if (do_texture)
555     {
556        glEnable(GL_LIGHTING);
557        glEnable(GL_TEXTURE_2D);
558        glColor3f(1, 1, 1);
559 #ifdef HAVE_GLBINDTEXTURE
560        glBindTexture(GL_TEXTURE_2D, sb->backid);
561 #endif /* HAVE_GLBINDTEXTURE */
562     }
563     else
564     {
565        glColor3f(0, 0, 0);
566     }
567     glBegin(GL_QUAD_STRIP);
568 #ifndef HAVE_MOBILE
569     /* Letterbox the background image */
570     glNormal3f(0, 0, 1); glTexCoord2f(0,0); glVertex3f(8, 4.1, -4);
571     glNormal3f(0, 0, 1); glTexCoord2f(0,1); glVertex3f(8, -4.1, -4);
572     glNormal3f(0, 0, 1); glTexCoord2f(1,0); glVertex3f(-8, 4.1, -4);
573     glNormal3f(0, 0, 1); glTexCoord2f(1,1); glVertex3f(-8, -4.1, -4);
574 #else
575     /* Fill the iPhone screen. Letterboxing looks dumb there. */
576     glNormal3f(0, 0, 1); glTexCoord2f(0,0); glVertex3f(4, 5.2, -4);
577     glNormal3f(0, 0, 1); glTexCoord2f(0,1); glVertex3f(4, -5.2, -4);
578     glNormal3f(0, 0, 1); glTexCoord2f(1,0); glVertex3f(-4, 5.2, -4);
579     glNormal3f(0, 0, 1); glTexCoord2f(1,1); glVertex3f(-4, -5.2, -4);
580 #endif
581     glEnd();
582     mi->polygon_count++;
583
584     gltrackball_rotate (sb->trackball);
585
586     /* rotate the balls */
587     glRotatef(sb->rotm[0], 1.0f, 0.0f, 0.0f);
588     glRotatef(sb->rotm[1], 0.0f, 1.0f, 0.0f);
589     glRotatef(sb->rotm[2], 0.0f, 0.0f, 1.0f);
590
591     if (! sb->button_down_p) {
592       sb->rotm[0] += sb->speed;
593       sb->rotm[1] += -(sb->speed);
594       sb->rotm[2] += 0;
595     }
596
597     /* draw the balls */
598     if (do_texture)
599 #ifdef HAVE_GLBINDTEXTURE
600        glBindTexture(GL_TEXTURE_2D, sb->faceid);
601     else
602 #endif /* HAVE_GLBINDTEXTURE */
603        glEnable(GL_LIGHTING);
604     for (sphere=0;sphere<spheres;sphere++)
605     {
606         drawSphere(mi,sphere);
607     }
608
609     glDisable(GL_TEXTURE_2D);
610     glDisable(GL_DEPTH_TEST);
611     glDisable(GL_LIGHTING);
612
613     /* manage framerate display */
614     if (MI_IS_FPS(mi)) do_fps (mi);
615     glPopMatrix();
616 }
617
618
619 static void Init(ModeInfo * mi)
620 {
621     sballsstruct *sb = &sballs[MI_SCREEN(mi)];
622     int i;
623
624     /* Default settings */
625     if (MI_IS_WIREFRAME(mi))
626         do_texture = False;
627     if (do_texture)
628         inittextures(mi);
629     else
630     {
631         sb->btexture = (XImage*) NULL;
632         sb->ftexture = (XImage*) NULL;
633     }
634
635     vinit(sb->eye   ,0.0f, 0.0f, 6.0f);
636     vinit(sb->rotm  ,0.0f, 0.0f, 0.0f);
637     sb->speed = MI_CYCLES(mi);
638
639     /* initialise object number */
640     if ((object == 0) || (object > MAX_OBJ))
641       object = NRAND(MAX_OBJ-1)+1;
642     object--;
643
644     /* initialise sphere number */
645     spheres = MI_COUNT(mi);
646     if (MI_COUNT(mi) > polygons[object].numverts)
647         spheres = polygons[object].numverts;
648     if (MI_COUNT(mi) < 1)
649         spheres = polygons[object].numverts;
650     /* initialise sphere radius */
651     for(i=0; i < spheres;i++)
652     {
653 #if RANDOM_RADIUS
654         sb->radius[i] = ((float) LRAND() / (float) MAXRAND);
655         if (sb->radius[i] < 0.3)
656             sb->radius[i] = 0.3;
657         if (sb->radius[i] > 0.7)
658             sb->radius[i] = 0.7;
659 #else
660         sb->radius[i] = polygons[object].radius;
661 #endif
662     }
663
664     if (MI_IS_DEBUG(mi)) {
665         (void) fprintf(stderr,
666                        "%s:\n\tobject=%s\n\tspheres=%d\n\tspeed=%d\n\ttexture=%s\n",
667                        MI_NAME(mi),
668                        polygons[object].shortname,
669                        spheres,
670                        (int) MI_CYCLES(mi),
671                        do_texture ? "on" : "off"
672                         );
673     }
674
675         glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
676         glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
677         glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);
678         glEnable(GL_LIGHT1);
679 }
680
681 /*
682  *-----------------------------------------------------------------------------
683  *-----------------------------------------------------------------------------
684  *    Xlock hooks.
685  *-----------------------------------------------------------------------------
686  *-----------------------------------------------------------------------------
687  */
688
689 static void free_sballs(ModeInfo * mi);
690
691 /*
692  *-----------------------------------------------------------------------------
693  *    Initialize sballs.  Called each time the window changes.
694  *-----------------------------------------------------------------------------
695  */
696
697 ENTRYPOINT void init_sballs(ModeInfo * mi)
698 {
699     sballsstruct *sb;
700
701     MI_INIT(mi, sballs, free_sballs);
702     sb = &sballs[MI_SCREEN(mi)];
703
704     sb->trackball = gltrackball_init (True);
705
706     if ((sb->glx_context = init_GL(mi)) != NULL) {
707
708 #ifndef STANDALONE
709         Reshape(mi); /* xlock mode */
710 #else
711         reshape_sballs(mi,MI_WIDTH(mi),MI_HEIGHT(mi)); /* xscreensaver mode */
712 #endif
713         glDrawBuffer(GL_BACK);
714         Init(mi);
715
716     } else {
717         MI_CLEARWINDOW(mi);
718     }
719 }
720
721 /*
722  *-----------------------------------------------------------------------------
723  *    Called by the mainline code periodically to update the display.
724  *-----------------------------------------------------------------------------
725  */
726 ENTRYPOINT void draw_sballs(ModeInfo * mi)
727 {
728     Display *display = MI_DISPLAY(mi);
729     Window window = MI_WINDOW(mi);
730     sballsstruct *sb;
731
732     if (sballs == NULL)
733             return;
734     sb = &sballs[MI_SCREEN(mi)];
735
736     MI_IS_DRAWN(mi) = True;
737     if (!sb->glx_context)
738         return;
739
740     glXMakeCurrent(display, window, *(sb->glx_context));
741     Draw(mi);
742 #ifndef STANDALONE
743     Reshape(mi); /* xlock mode */
744 #else
745     reshape_sballs(mi,MI_WIDTH(mi),MI_HEIGHT(mi)); /* xscreensaver mode */
746 #endif
747
748     glFinish();
749     glXSwapBuffers(display, window);
750 }
751
752
753 /*
754  *-----------------------------------------------------------------------------
755  *    The display is being taken away from us.  Free up malloc'ed
756  *      memory and X resources that we've alloc'ed.
757  *-----------------------------------------------------------------------------
758  */
759
760 static void free_sballs(ModeInfo * mi)
761 {
762     sballsstruct *sb = &sballs[MI_SCREEN(mi)];
763     if (sb->glx_context)
764     {
765         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sb->glx_context));
766         if (sb->btexture)
767         {
768             glDeleteTextures(1,&sb->backid);
769             XDestroyImage(sb->btexture);
770         }
771         if (sb->ftexture)
772         {
773             glDeleteTextures(1,&sb->faceid);
774             XDestroyImage(sb->ftexture);
775         }
776     }
777 }
778
779 ENTRYPOINT Bool
780 sballs_handle_event (ModeInfo *mi, XEvent *event)
781 {
782   sballsstruct *sb = &sballs[MI_SCREEN(mi)];
783
784   if (gltrackball_event_handler (event, sb->trackball,
785                                  MI_WIDTH (mi), MI_HEIGHT (mi),
786                                  &sb->button_down_p))
787     return True;
788
789   return False;
790 }
791
792
793 #ifndef STANDALONE
794 ENTRYPOINT void change_sballs(ModeInfo * mi)
795 {
796     sballsstruct *sb;
797
798     if (sballs == NULL)
799             return;
800     sb = &sballs[MI_SCREEN(mi)];
801
802     if (!sb->glx_context)
803         return;
804
805     /* initialise object number */
806     if ((object == 0) || (object > MAX_OBJ))
807       object = NRAND(MAX_OBJ-1)+1;
808     object--;
809
810     /* correct sphere number */
811     spheres = MI_COUNT(mi);
812     if (MI_COUNT(mi) > polygons[object].numverts)
813         spheres = polygons[object].numverts;
814     if (MI_COUNT(mi) < 1)
815         spheres = polygons[object].numverts;
816
817     if (MI_IS_DEBUG(mi)) {
818         (void) fprintf(stderr,
819                        "%s:\n\tobject=%s\n\tspheres=%d\n\tspeed=%d\n\ttexture=%s\n",
820                        MI_NAME(mi),
821                        polygons[object].shortname,
822                        spheres,
823                        (int) MI_CYCLES(mi),
824                        do_texture ? "on" : "off"
825                         );
826     }
827     glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sb->glx_context));
828
829 }
830 #endif /* !STANDALONE */
831
832 XSCREENSAVER_MODULE ("SBalls", sballs)
833
834 #endif /* MODE_sballs */