1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* glplanet --- 3D rotating planet, e.g., Earth. */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)plate.c 4.07 97/11/24 xlockmore";
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.
23 * 8-Oct-98: jwz@jwz.org Made the 512x512x1 xearth image be built in.
24 * Made it possible to load XPM or XBM files.
25 * Made the planet bounce and roll around.
27 * 8-Oct-98: Released initial version of "glplanet"
28 * (David Konerding, dek@cgl.ucsf.edu)
32 * 3) better earth image
33 * 4) "exploding" planet mode-- the surface will expand and explode
34 * 5) Fix bug with annoying triangles moving on surface
37 * For even more spectacular results, grab the images from the "SSysten"
38 * package (http://www.msu.edu/user/kamelkev/) and do this:
40 * cd ssystem-1.4/hires/
42 * djpeg $f | ppmquant 254 | ppmtoxpm > /tmp/$f:r.xpm
53 * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
54 * otherwise caddr_t is not defined correctly
57 #include <X11/Intrinsic.h>
60 # define PROGCLASS "Planet"
61 # define HACK_INIT init_planet
62 # define HACK_DRAW draw_planet
63 # define planet_opts xlockmore_opts
64 #define DEFAULTS "*delay: 15000 \n" \
68 "*wireframe: False \n" \
71 "*stipple: False \n" \
72 "*image: BUILTIN \n" \
73 "*imageForeground: Green \n" \
74 "*imageBackground: Blue \n"
76 # include "xlockmore.h" /* from the xscreensaver distribution */
77 #else /* !STANDALONE */
78 # include "xlock.h" /* from the xlockmore distribution */
79 #endif /* !STANDALONE */
81 #ifdef USE_GL /* whole file */
85 # ifndef PIXEL_ALREADY_TYPEDEFED
86 # define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */
92 # include <X11/Xmu/Drawing.h>
94 # include <Xmu/Drawing.h>
101 #define DEF_ROTATE "True"
102 #define DEF_ROLL "True"
103 #define DEF_BOUNCE "True"
104 #define DEF_TEXTURE "True"
105 #define DEF_LIGHT "True"
106 #define DEF_STIPPLE "False"
107 #define DEF_IMAGE "BUILTIN"
110 #define countof(x) (sizeof((x))/sizeof((*x)))
112 static int do_rotate;
114 static int do_bounce;
115 static int do_texture;
117 static int do_stipple;
118 static char *which_image;
119 static XrmOptionDescRec opts[] = {
120 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, (caddr_t) "true" },
121 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, (caddr_t) "false" },
122 {"-roll", ".glplanet.roll", XrmoptionNoArg, (caddr_t) "true" },
123 {"+roll", ".glplanet.roll", XrmoptionNoArg, (caddr_t) "false" },
124 {"-bounce", ".glplanet.bounce", XrmoptionNoArg, (caddr_t) "true" },
125 {"+bounce", ".glplanet.bounce", XrmoptionNoArg, (caddr_t) "false" },
126 {"-texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "true" },
127 {"+texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "false" },
128 {"-light", ".glplanet.light", XrmoptionNoArg, (caddr_t) "true" },
129 {"+light", ".glplanet.light", XrmoptionNoArg, (caddr_t) "false" },
130 {"-stipple", ".glplanet.stipple", XrmoptionNoArg, (caddr_t) "true" },
131 {"+stipple", ".glplanet.stipple", XrmoptionNoArg, (caddr_t) "false" },
132 {"-image", ".glplanet.image", XrmoptionSepArg, (caddr_t) 0 },
135 static argtype vars[] = {
136 {(caddr_t *) &do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
137 {(caddr_t *) &do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
138 {(caddr_t *) &do_bounce, "bounce", "Bounce", DEF_BOUNCE, t_Bool},
139 {(caddr_t *) &do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
140 {(caddr_t *) &do_light, "light", "Light", DEF_LIGHT, t_Bool},
141 {(caddr_t *) &do_stipple, "stipple", "Stipple", DEF_STIPPLE, t_Bool},
142 {(caddr_t *) &which_image, "image", "Image", DEF_IMAGE, t_String},
145 ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
148 ModStruct planet_description =
149 {"planet", "init_planet", "draw_planet", "release_planet",
150 "draw_planet", "init_planet", NULL, &planet_opts,
151 1000, 1, 2, 1, 4, 1.0, "",
152 "Animates texture mapped sphere (planet)", 0, NULL};
155 #include "../images/earth.xbm"
156 #include "xpm-ximage.h"
160 * slices and stacks are used in the sphere parameterization routine.
161 * more slices and stacks will increase the quality of the sphere,
162 * at the expense of rendering speed
167 #define NUM_PLATES (STACKS * (SLICES+1))
169 /* radius of the sphere- fairly arbitrary */
175 * structure for holding the data for an individual plate.
176 * RotationRate, Angle, Vector, Translation and Color
177 * are not currently used, but may be used in the future
180 GLfloat RotationRate;
183 GLfloat Translation[3];
188 /* structure for holding the planet data */
190 plate plates[NUM_PLATES];
191 GLXContext *glx_context;
197 GLfloat dtx, dty, dtz;
198 GLfloat xpos, ypos, zpos;
200 GLfloat box_width, box_height, box_depth;
205 static planetstruct *planets = NULL;
208 /* Set up and enable texturing on our object */
210 setup_xbm_texture (char *bits, int width, int height,
211 XColor *fgc, XColor *bgc)
213 unsigned int fg = (((fgc->red >> 8) << 16) |
214 ((fgc->green >> 8) << 8) |
216 unsigned int bg = (((bgc->red >> 8) << 16) |
217 ((bgc->green >> 8) << 8) |
220 unsigned char *data = (unsigned char *)
221 malloc ((width * height * 24) / 8);
222 unsigned char *out = data;
225 for (y = 0; y < height; y++)
226 for (x = 0; x < width; x++)
228 unsigned char byte = bits [(y * (width / 8) + (x / 8))];
229 unsigned char bit = (byte & (1 << (x % 8))) >> (x % 8);
230 unsigned int word = (bit ? bg : fg);
231 *out++ = (word & 0xFF0000) >> 16;
232 *out++ = (word & 0x00FF00) >> 8;
233 *out++ = (word & 0x0000FF);
236 glEnable(GL_TEXTURE_2D);
237 glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0,
238 GL_RGB, GL_UNSIGNED_BYTE, data);
240 /* setup parameters for texturing */
241 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
242 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
243 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
244 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
245 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
246 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
251 setup_file_texture (ModeInfo *mi, char *filename)
253 Display *dpy = mi->dpy;
254 Visual *visual = mi->xgwa.visual;
255 Colormap cmap = mi->xgwa.colormap;
260 int result = XpmReadFileToData (filename, &xpm_data);
264 XImage *image = xpm_to_ximage (dpy, visual, cmap, xpm_data);
266 glEnable(GL_TEXTURE_2D);
267 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
268 image->width, image->height, 0,
269 GL_RGBA, GL_UNSIGNED_BYTE, image->data);
271 /* setup parameters for texturing */
272 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
273 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
275 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
276 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
277 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
278 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
279 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
285 fprintf (stderr, "%s: file %s doesn't exist.\n", progname, filename);
290 /* Fall through and try it as an XBM. */
294 fprintf (stderr, "%s: XPM: out of memory\n", progname);
299 fprintf (stderr, "%s: XPM: unknown error code %d\n", progname, result);
304 #endif /* HAVE_XPM */
308 planetstruct *gp = &planets[MI_SCREEN(mi)];
309 unsigned int width = 0;
310 unsigned int height = 0;
311 unsigned char *data = 0;
313 int status = XmuReadBitmapDataFromFile (filename, &width, &height, &data,
315 if (status != Success)
318 fprintf (stderr, "%s: not an XPM file: %s\n", progname, filename);
320 fprintf (stderr, "%s: not an XBM file: %s\n", progname, filename);
324 setup_xbm_texture (data, width, height, &gp->fg, &gp->bg);
329 fprintf (stderr, "%s: not an XPM file: %s\n", progname, filename);
331 fprintf (stderr, "%s: your vendor doesn't ship the standard Xmu library.\n",
333 fprintf (stderr, "%s: we can't load XBM files without it.\n",progname);
340 setup_texture(ModeInfo * mi)
342 planetstruct *gp = &planets[MI_SCREEN(mi)];
345 !strcmp(which_image, "BUILTIN"))
346 setup_xbm_texture (earth_bits, earth_width, earth_height,
349 setup_file_texture (mi, which_image);
353 /* Set up and enable lighting */
358 glEnable(GL_DEPTH_TEST);
359 glEnable(GL_AUTO_NORMAL);
360 glEnable(GL_NORMALIZE);
361 glShadeModel(GL_SMOOTH);
363 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
364 glEnable(GL_LINE_SMOOTH);
367 glEnable(GL_LIGHTING);
370 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
371 glEnable(GL_COLOR_MATERIAL);
376 /* a stipple pattern */
377 static GLubyte halftone[] =
379 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
380 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
381 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
382 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
383 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
384 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
385 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
386 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
387 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
388 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
389 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
390 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
391 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
392 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
393 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
394 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
397 /* Set up and enable stippling */
401 glEnable(GL_POLYGON_STIPPLE);
402 glPolygonStipple(halftone);
406 /* Set up and enable face culling so we don't see the inside of the sphere */
410 glEnable(GL_CULL_FACE);
415 /* Function for determining points on the surface of the sphere */
416 void ParametricSphere(float theta, float rho, float *vector)
418 vector[0] = -sin(theta) * sin(rho);
419 vector[1] = cos(theta) * sin(rho);
420 vector[2] = cos(rho);
425 /* Initialization function for screen saver */
429 Bool wire = MI_IS_WIREFRAME(mi);
430 planetstruct *gp = &planets[MI_SCREEN(mi)];
431 int i, j, list, dllist;
432 int stacks=STACKS, slices=SLICES;
443 /* turn on various options we like */
453 dllist=glGenLists(NUM_PLATES);
455 drho = M_PI / stacks;
456 dtheta = 2.0 * M_PI / slices;
463 * Generate a huge sphere with quadrilaterals.
464 * Each quad is stored in its own display list; this is so we can
465 * move the quads around later (not yet done).
466 * Quad vertices are determined using a parametric sphere function.
467 * For fun, you could generate practically any parameteric surface and
468 * map an image onto it.
472 for(i=0; i<stacks; i++) {
475 for(j=0; j<slices+1; j++) {
478 gp->plates[i].Translation[0] = 0.;
479 gp->plates[i].Translation[1] = 0.;
480 gp->plates[i].Translation[2] = 0.;
482 gp->plates[i].RotationRate = 0.;
483 gp->plates[i].Angle[0] = 0.;
484 gp->plates[i].Angle[1] = 0.;
485 gp->plates[i].Angle[2] = 0.;
486 gp->plates[i].Angle[3] = 0.;
488 gp->plates[i].Color[0] = 1.;
489 gp->plates[i].Color[1] = 1.;
490 gp->plates[i].Color[2] = 1.;
492 gp->plates[list].platelist = dllist+list;
493 glNewList(gp->plates[list].platelist, GL_COMPILE);
494 glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
496 glColor3f(gp->plates[i].Color[0], gp->plates[i].Color[1], gp->plates[i].Color[2]);
499 ParametricSphere(theta, rho, vector);
501 glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
503 glTexCoord2f(s,t+dt);
504 ParametricSphere(theta, rho+drho, vector);
506 glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
508 glTexCoord2f(s+ds,t+dt);
509 ParametricSphere(theta + dtheta, rho+drho, vector);
511 glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
513 glTexCoord2f(s+ds, t);
514 ParametricSphere(theta + dtheta, rho, vector);
516 glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
535 planetstruct *gp = &planets[MI_SCREEN(mi)];
537 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
539 for (i=0; i < NUM_PLATES; i++) {
541 /* currently, the angle and translation are 0, but later this can
542 * help us move the surface around */
544 glRotatef(gp->plates[i].Angle[0],
545 gp->plates[i].Angle[1],
546 gp->plates[i].Angle[2],
547 gp->plates[i].Angle[3]);
548 glTranslatef(gp->plates[i].Translation[0],
549 gp->plates[i].Translation[1],
550 gp->plates[i].Translation[2]);
552 glCallList(gp->plates[i].platelist);
555 gp->plates[i].Angle[0] += gp->plates[i].RotationRate;
561 #define RANDSIGN() ((random() & 1) ? 1 : -1)
564 pick_velocity (ModeInfo * mi)
566 planetstruct *gp = &planets[MI_SCREEN(mi)];
568 gp->box_width = 15.0;
569 gp->box_height = 15.0;
570 gp->box_depth = 60.0;
576 gp->dtx = (frand(0.4) + frand(0.3)) * RANDSIGN();
577 gp->dty = (frand(0.4) + frand(0.3)) * RANDSIGN();
578 gp->dtz = (frand(5.0) + frand(5.0)); /* the sun sets in the west */
580 gp->dx = (frand(0.2) + frand(0.2)) * RANDSIGN();
581 gp->dy = (frand(0.2) + frand(0.2)) * RANDSIGN();
582 gp->dz = (frand(0.2) + frand(0.2)) * RANDSIGN();
587 rotate_and_move (ModeInfo * mi)
589 planetstruct *gp = &planets[MI_SCREEN(mi)];
594 while (gp->tx < 0) gp->tx += 360;
595 while (gp->tx > 360) gp->tx -= 360;
598 while (gp->ty < 0) gp->ty += 360;
599 while (gp->ty > 360) gp->ty -= 360;
605 while (gp->tz < 0) gp->tz += 360;
606 while (gp->tz > 360) gp->tz -= 360;
611 /* Move in the direction we had been moving in. */
617 if (gp->xpos > gp->box_depth)
618 gp->xpos = gp->box_depth, gp->dx = -gp->dx;
619 else if (gp->xpos < 0)
620 gp->xpos = 0, gp->dx = -gp->dx;
622 if (gp->ypos > gp->box_width/2)
623 gp->ypos = gp->box_width/2, gp->dy = -gp->dy;
624 else if (gp->ypos < -gp->box_width/2)
625 gp->ypos = -gp->box_width/2, gp->dy = -gp->dy;
627 if (gp->zpos > gp->box_height/2)
628 gp->zpos = gp->box_height/2, gp->dz = -gp->dz;
629 else if (gp->zpos < -gp->box_height/2)
630 gp->zpos = -gp->box_height/2, gp->dz = -gp->dz;
635 /* Standard reshape function */
638 reshape(int width, int height)
641 GLfloat h = (GLfloat) height / (GLfloat) width;
644 light[1] = (int) (((random() % 3) & 0xFF) - 1);
645 light[2] = (int) (((random() % 3) & 0xFF) - 1);
648 glViewport(0, 0, (GLint) width, (GLint) height);
649 glMatrixMode(GL_PROJECTION);
651 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
652 glMatrixMode(GL_MODELVIEW);
654 glTranslatef(0.0, 0.0, -DIST);
655 /* some messiness for orienting the earth normally */
658 glLightfv(GL_LIGHT0, GL_POSITION, light);
659 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
665 init_planet(ModeInfo * mi)
667 int screen = MI_SCREEN(mi);
671 if (planets == NULL) {
672 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
673 sizeof (planetstruct))) == NULL)
676 gp = &planets[screen];
681 char *f = get_string_resource("imageForeground", "Foreground");
682 char *b = get_string_resource("imageBackground", "Background");
684 if (!f) f = strdup("white");
685 if (!b) b = strdup("black");
687 for (s = f + strlen(f)-1; s > f; s--)
688 if (*s == ' ' || *s == '\t')
690 for (s = b + strlen(b)-1; s > b; s--)
691 if (*s == ' ' || *s == '\t')
694 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
696 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
699 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
701 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
710 gp->window = MI_WINDOW(mi);
711 if ((gp->glx_context = init_GL(mi)) != NULL) {
712 reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
720 draw_planet(ModeInfo * mi)
722 planetstruct *gp = &planets[MI_SCREEN(mi)];
723 Display *display = MI_DISPLAY(mi);
724 Window window = MI_WINDOW(mi);
726 if (!gp->glx_context)
729 glDrawBuffer(GL_BACK);
731 glXMakeCurrent(display, window, *(gp->glx_context));
735 glTranslatef(gp->xpos, gp->ypos, gp->zpos);
736 glRotatef(gp->tx, 1, 0, 0);
737 glRotatef(gp->ty, 0, 1, 0);
738 glRotatef(gp->tz, 0, 0, 1);
744 glXSwapBuffers(display, window);
746 rotate_and_move (mi);
750 release_planet(ModeInfo * mi)
753 if (planets != NULL) {
756 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
757 planetstruct *gp = &planets[screen];
759 if (gp->glx_context) {
760 /* Display lists MUST be freed while their glXContext is current. */
761 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
763 for (i=0; i < NUM_PLATES; i++) {
764 if (glIsList(gp->plates[i].platelist))
765 glDeleteLists(gp->plates[i].platelist, 1);
769 (void) free((void *) planets);