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