1 /* sballs --- balls spinning like crazy in GL */
4 static const char sccsid[] = "@(#)sballs.c 5.02 2001/03/10 xlockmore";
7 /* Copyright (c) E. Lassauge, 2001. */
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.
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.
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/
26 * Eric Lassauge (November-07-2000) <lassauge@users.sourceforge.net>
27 * http://lassauge.free.fr/linux.html
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
43 #ifdef STANDALONE /* xscreensaver mode */
44 #define DEFAULTS "*delay: 30000 \n" \
47 "*showFPS: False \n" \
48 "*wireframe: False \n" \
50 # define refresh_sballs 0
52 #include "xlockmore.h" /* from the xscreensaver distribution */
53 #include "gltrackball.h"
54 #else /* !STANDALONE */
55 #include "xlock.h" /* from the xlockmore distribution */
57 #endif /* !STANDALONE */
61 #define MINSIZE 32 /* minimal viewport size */
62 #define FRAME 50 /* frame count interval */
63 #define MAX_OBJ 8 /* number of 3D objects */
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"
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... */
77 # include "../images/sball.xpm"
81 # include "../images/sball-bg.xpm"
82 # else /* !STANDALONE */
83 # include "pixmaps/sball.xpm"
84 # include "pixmaps/sball-bg.xpm"
85 # endif /* !STANDALONE */
88 /* Manage option vars */
89 #define DEF_TEXTURE "True"
90 #define DEF_OBJECT "0"
91 static Bool do_texture;
95 static XrmOptionDescRec opts[] = {
96 {"-texture", ".sballs.texture", XrmoptionNoArg, "on"},
97 {"+texture", ".sballs.texture", XrmoptionNoArg, "off"},
98 {"-object", ".sballs.object", XrmoptionSepArg, 0},
102 static argtype vars[] = {
103 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
104 {&object, "object", "Object", DEF_OBJECT, t_Int},
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)"},
115 ENTRYPOINT ModeSpecOpt sballs_opts =
116 { sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc };
119 ModStruct sballs_description =
120 { "sballs", "init_sballs", "draw_sballs", "release_sballs",
121 "draw_sballs", "change_sballs", (char *) NULL, &sballs_opts,
123 delay,count,cycles,size,ncolors,sat
125 10000, 0, 10, 400, 64, 1.0, "",
126 "balls spinning like crazy in GL", 0, NULL
128 #endif /* USE_MODULES */
130 /* misc types and defines */
131 #define vinit(a,i,j,k) {\
137 typedef vec_t vec3_t[3];
141 /* the mode struct, contains all per screen variables */
143 GLint WIDTH, HEIGHT; /* display dimensions */
144 GLXContext *glx_context;
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 */
155 float radius[MAX_BALLS];
157 trackball_state *trackball;
162 /* array of sballsstruct indexed by screen number */
163 static sballsstruct *sballs = (sballsstruct *) NULL;
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 };
170 /* structure of the polyhedras */
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 */
179 static const Polyinfo polygons[] =
182 /* 0: objtetra - structure values for tetrahedron */
184 "tetrahedron", "tetra", /* long and short names */
185 4, /* number of vertices */
187 { /* vertices (x,y,z) */
188 /* all points must be within radius 2 of the origin */
198 /* 1: objcube - structure values for cube */
201 "hexahedron", "cube", /* long and short names */
202 8, /* number of vertices, edges, and faces */
204 { /* vertices (x,y,z) */
205 /* all points must be within radius 2 of the origin */
219 /* 2: objocta - structure values for octahedron */
222 "octahedron", "octa", /* long and short names */
223 6, /* number of vertices */
225 { /* vertices (x,y,z) */
226 /* all points must be within radius 2 of the origin */
237 /* 3: objdodec - structure values for dodecahedron */
240 "dodecahedron", "dodeca", /* long and short names */
241 20, /* number of vertices */
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},
268 /* 4: objicosa - structure values for icosahedron */
271 "icosahedron", "icosa", /* long and short names */
272 12, /* number of vertices */
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}
291 /* 5: objplane - structure values for plane */
294 "plane", "plane", /* long and short names */
295 4, /* number of vertices */
297 { /* vertices (x,y,z) */
298 /* all points must be within radius 2 of the origin */
308 /* 6: objpyr - structure values for pyramid */
311 "pyramid", "pyramid", /* long and short names */
312 5, /* number of vertices */
314 { /* vertices (x,y,z) */
315 /* all points must be within radius 1 of the origin */
326 /* 7: objstar - structure values for octahedron star (stellated octahedron?) */
328 "star", "star", /* long and short names */
329 8, /* number of vertices */
331 { /* vertices (x,y,z) */
332 /* all points must be within radius 1 of the origin */
352 *-----------------------------------------------------------------------------
353 *-----------------------------------------------------------------------------
355 *-----------------------------------------------------------------------------
356 *-----------------------------------------------------------------------------
360 /* initialise textures */
361 static void inittextures(ModeInfo * mi)
363 sballsstruct *sb = &sballs[MI_SCREEN(mi)];
365 #if defined( I_HAVE_XPM )
368 glGenTextures(1, &sb->backid);
369 #ifdef HAVE_GLBINDTEXTURE
370 glBindTexture(GL_TEXTURE_2D, sb->backid);
371 #endif /* HAVE_GLBINDTEXTURE */
373 sb->btexture = xpm_to_ximage(MI_DISPLAY(mi),
377 if (!(sb->btexture)) {
378 (void) fprintf(stderr, "Error reading the background texture.\n");
379 glDeleteTextures(1, &sb->backid);
381 sb->faceid = 0; /* default textures */
386 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
388 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
389 sb->btexture->width, sb->btexture->height, 0,
391 /* GL_UNSIGNED_BYTE, */
392 GL_UNSIGNED_INT_8_8_8_8_REV,
394 check_gl_error("texture");
396 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
397 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
399 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
400 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
402 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
404 glGenTextures(1, &sb->faceid);
405 #ifdef HAVE_GLBINDTEXTURE
406 glBindTexture(GL_TEXTURE_2D, sb->faceid);
407 #endif /* HAVE_GLBINDTEXTURE */
409 sb->ftexture = xpm_to_ximage(MI_DISPLAY(mi),
413 if (!(sb->ftexture)) {
414 (void) fprintf(stderr, "Error reading the face texture.\n");
415 glDeleteTextures(1, &sb->faceid);
420 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
422 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
423 sb->ftexture->width, sb->ftexture->height, 0,
425 /* GL_UNSIGNED_BYTE, */
426 GL_UNSIGNED_INT_8_8_8_8_REV,
428 check_gl_error("texture");
430 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
431 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
433 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
434 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
436 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
440 sb->faceid = 0; /* default textures */
443 #else /* !I_HAVE_XPM */
445 sb->faceid = 0; /* default textures */
447 #endif /* !I_HAVE_XPM */
450 static void drawSphere(ModeInfo * mi,int sphere_num)
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];
458 float radius = sb->radius[sphere_num];
459 double majorStep = (M_PI / numMajor);
460 double minorStep = (2.0 * M_PI / numMinor);
464 glTranslatef(x, y, z);
466 glColor4f(1, 1, 1, 1);
467 for (i = 0; i < numMajor; ++i)
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);
476 glBegin(MI_IS_WIREFRAME(mi) ? GL_LINE_STRIP: GL_TRIANGLE_STRIP);
477 for (j = 0; j <= numMinor; ++j)
479 double c = j * minorStep;
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);
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);
500 *-----------------------------------------------------------------------------
501 *-----------------------------------------------------------------------------
503 *-----------------------------------------------------------------------------
504 *-----------------------------------------------------------------------------
508 static void Reshape(ModeInfo * mi)
510 ENTRYPOINT void reshape_sballs(ModeInfo * mi, int width, int height)
514 sballsstruct *sb = &sballs[MI_SCREEN(mi)];
515 int size = MI_SIZE(mi);
517 /* Viewport is specified size if size >= MINSIZE && size < screensize */
519 sb->WIDTH = MI_WIDTH(mi);
520 sb->HEIGHT = MI_HEIGHT(mi);
521 } else if (size < MINSIZE) {
523 sb->HEIGHT = MINSIZE;
525 sb->WIDTH = (size > MI_WIDTH(mi)) ? MI_WIDTH(mi) : size;
526 sb->HEIGHT = (size > MI_HEIGHT(mi)) ? MI_HEIGHT(mi) : size;
528 glViewport((MI_WIDTH(mi) - sb->WIDTH) / 2, (MI_HEIGHT(mi) - sb->HEIGHT) / 2, sb->WIDTH, sb->HEIGHT);
529 glMatrixMode(GL_PROJECTION);
531 gluPerspective(55.0, (float)sb->WIDTH / (float) sb->HEIGHT, 1.0, 300.0);
533 glMatrixMode(GL_MODELVIEW);
537 static void Draw(ModeInfo * mi)
539 sballsstruct *sb = &sballs[MI_SCREEN(mi)];
542 mi->polygon_count = 0;
544 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
547 glEnable(GL_DEPTH_TEST);
550 glTranslatef (-sb->eye[0], -sb->eye[1], -sb->eye[2]);
552 /* draw background */
555 glEnable(GL_LIGHTING);
556 glEnable(GL_TEXTURE_2D);
558 #ifdef HAVE_GLBINDTEXTURE
559 glBindTexture(GL_TEXTURE_2D, sb->backid);
560 #endif /* HAVE_GLBINDTEXTURE */
566 glBegin(GL_QUAD_STRIP);
568 /* Letterbox the background image */
569 glNormal3f(0, 0, 1); glTexCoord2f(0,0); glVertex3f(8, 4.1, -4);
570 glNormal3f(0, 0, 1); glTexCoord2f(0,1); glVertex3f(8, -4.1, -4);
571 glNormal3f(0, 0, 1); glTexCoord2f(1,0); glVertex3f(-8, 4.1, -4);
572 glNormal3f(0, 0, 1); glTexCoord2f(1,1); glVertex3f(-8, -4.1, -4);
574 /* Fill the iPhone screen. Letterboxing looks dumb there. */
575 glNormal3f(0, 0, 1); glTexCoord2f(0,0); glVertex3f(4, 5.2, -4);
576 glNormal3f(0, 0, 1); glTexCoord2f(0,1); glVertex3f(4, -5.2, -4);
577 glNormal3f(0, 0, 1); glTexCoord2f(1,0); glVertex3f(-4, 5.2, -4);
578 glNormal3f(0, 0, 1); glTexCoord2f(1,1); glVertex3f(-4, -5.2, -4);
583 gltrackball_rotate (sb->trackball);
585 /* rotate the balls */
586 glRotatef(sb->rotm[0], 1.0f, 0.0f, 0.0f);
587 glRotatef(sb->rotm[1], 0.0f, 1.0f, 0.0f);
588 glRotatef(sb->rotm[2], 0.0f, 0.0f, 1.0f);
590 if (! sb->button_down_p) {
591 sb->rotm[0] += sb->speed;
592 sb->rotm[1] += -(sb->speed);
598 #ifdef HAVE_GLBINDTEXTURE
599 glBindTexture(GL_TEXTURE_2D, sb->faceid);
600 #endif /* HAVE_GLBINDTEXTURE */
602 glEnable(GL_LIGHTING);
603 for (sphere=0;sphere<spheres;sphere++)
605 drawSphere(mi,sphere);
608 glDisable(GL_TEXTURE_2D);
609 glDisable(GL_DEPTH_TEST);
610 glDisable(GL_LIGHTING);
612 /* manage framerate display */
613 if (MI_IS_FPS(mi)) do_fps (mi);
618 static void Init(ModeInfo * mi)
620 sballsstruct *sb = &sballs[MI_SCREEN(mi)];
623 /* Default settings */
624 if (MI_IS_WIREFRAME(mi))
630 sb->btexture = (XImage*) NULL;
631 sb->ftexture = (XImage*) NULL;
634 vinit(sb->eye ,0.0f, 0.0f, 6.0f);
635 vinit(sb->rotm ,0.0f, 0.0f, 0.0f);
636 sb->speed = MI_CYCLES(mi);
638 /* initialise object number */
639 if ((object == 0) || (object > MAX_OBJ))
640 object = NRAND(MAX_OBJ-1)+1;
643 /* initialise sphere number */
644 spheres = MI_COUNT(mi);
645 if (MI_COUNT(mi) > polygons[object].numverts)
646 spheres = polygons[object].numverts;
647 if (MI_COUNT(mi) < 1)
648 spheres = polygons[object].numverts;
649 /* initialise sphere radius */
650 for(i=0; i < spheres;i++)
653 sb->radius[i] = ((float) LRAND() / (float) MAXRAND);
654 if (sb->radius[i] < 0.3)
656 if (sb->radius[i] > 0.7)
659 sb->radius[i] = polygons[object].radius;
663 if (MI_IS_DEBUG(mi)) {
664 (void) fprintf(stderr,
665 "%s:\n\tobject=%s\n\tspheres=%d\n\tspeed=%d\n\ttexture=%s\n",
667 polygons[object].shortname,
670 do_texture ? "on" : "off"
674 glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
675 glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
676 glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);
681 *-----------------------------------------------------------------------------
682 *-----------------------------------------------------------------------------
684 *-----------------------------------------------------------------------------
685 *-----------------------------------------------------------------------------
689 *-----------------------------------------------------------------------------
690 * Initialize sballs. Called each time the window changes.
691 *-----------------------------------------------------------------------------
694 ENTRYPOINT void init_sballs(ModeInfo * mi)
698 if (sballs == NULL) {
699 if ((sballs = (sballsstruct *) calloc(MI_NUM_SCREENS(mi),
700 sizeof(sballsstruct))) == NULL)
703 sb = &sballs[MI_SCREEN(mi)];
705 sb->trackball = gltrackball_init (True);
707 if ((sb->glx_context = init_GL(mi)) != NULL) {
710 Reshape(mi); /* xlock mode */
712 reshape_sballs(mi,MI_WIDTH(mi),MI_HEIGHT(mi)); /* xscreensaver mode */
714 glDrawBuffer(GL_BACK);
723 *-----------------------------------------------------------------------------
724 * Called by the mainline code periodically to update the display.
725 *-----------------------------------------------------------------------------
727 ENTRYPOINT void draw_sballs(ModeInfo * mi)
729 Display *display = MI_DISPLAY(mi);
730 Window window = MI_WINDOW(mi);
735 sb = &sballs[MI_SCREEN(mi)];
737 MI_IS_DRAWN(mi) = True;
738 if (!sb->glx_context)
741 glXMakeCurrent(display, window, *(sb->glx_context));
744 Reshape(mi); /* xlock mode */
746 reshape_sballs(mi,MI_WIDTH(mi),MI_HEIGHT(mi)); /* xscreensaver mode */
750 glXSwapBuffers(display, window);
755 *-----------------------------------------------------------------------------
756 * The display is being taken away from us. Free up malloc'ed
757 * memory and X resources that we've alloc'ed. Only called
758 * once, we must zap everything for every screen.
759 *-----------------------------------------------------------------------------
762 ENTRYPOINT void release_sballs(ModeInfo * mi)
766 if (sballs != NULL) {
767 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
768 sballsstruct *sb = &sballs[screen];
771 glDeleteTextures(1,&sb->backid);
772 XDestroyImage(sb->btexture);
776 glDeleteTextures(1,&sb->faceid);
777 XDestroyImage(sb->ftexture);
780 (void) free((void *) sballs);
781 sballs = (sballsstruct *) NULL;
787 sballs_handle_event (ModeInfo *mi, XEvent *event)
789 sballsstruct *sb = &sballs[MI_SCREEN(mi)];
791 if (gltrackball_event_handler (event, sb->trackball,
792 MI_WIDTH (mi), MI_HEIGHT (mi),
801 ENTRYPOINT void change_sballs(ModeInfo * mi)
807 sb = &sballs[MI_SCREEN(mi)];
809 if (!sb->glx_context)
812 /* initialise object number */
813 if ((object == 0) || (object > MAX_OBJ))
814 object = NRAND(MAX_OBJ-1)+1;
817 /* correct sphere number */
818 spheres = MI_COUNT(mi);
819 if (MI_COUNT(mi) > polygons[object].numverts)
820 spheres = polygons[object].numverts;
821 if (MI_COUNT(mi) < 1)
822 spheres = polygons[object].numverts;
824 if (MI_IS_DEBUG(mi)) {
825 (void) fprintf(stderr,
826 "%s:\n\tobject=%s\n\tspheres=%d\n\tspeed=%d\n\ttexture=%s\n",
828 polygons[object].shortname,
831 do_texture ? "on" : "off"
834 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sb->glx_context));
837 #endif /* !STANDALONE */
839 XSCREENSAVER_MODULE ("SBalls", sballs)
841 #endif /* MODE_sballs */