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