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 * 9-Oct-98: dek@cgl.ucsf.edu Added stars.
25 * 8-Oct-98: jwz@jwz.org Made the 512x512x1 xearth image be built in.
26 * Made it possible to load XPM or XBM files.
27 * Made the planet bounce and roll around.
29 * 8-Oct-98: Released initial version of "glplanet"
30 * (David Konerding, dek@cgl.ucsf.edu)
35 * For even more spectacular results, grab the images from the "SSysten"
36 * package (http://www.msu.edu/user/kamelkev/) and do this:
38 * cd ssystem-1.4/hires/
40 * djpeg $f | ppmquant 254 | ppmtoxpm > /tmp/$f:r.xpm
51 * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
52 * otherwise caddr_t is not defined correctly
55 #include <X11/Intrinsic.h>
58 # define PROGCLASS "Planet"
59 # define HACK_INIT init_planet
60 # define HACK_DRAW draw_planet
61 # define planet_opts xlockmore_opts
62 #define DEFAULTS "*delay: 15000 \n" \
66 "*wireframe: False \n" \
70 "*image: BUILTIN \n" \
71 "*imageForeground: Green \n" \
72 "*imageBackground: Blue \n"
74 # include "xlockmore.h" /* from the xscreensaver distribution */
75 #else /* !STANDALONE */
76 # include "xlock.h" /* from the xlockmore distribution */
77 #endif /* !STANDALONE */
79 #ifdef USE_GL /* whole file */
83 # ifndef PIXEL_ALREADY_TYPEDEFED
84 # define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */
90 # include <X11/Xmu/Drawing.h>
92 # include <Xmu/Drawing.h>
99 #define DEF_ROTATE "True"
100 #define DEF_ROLL "True"
101 #define DEF_BOUNCE "True"
102 #define DEF_TEXTURE "True"
103 #define DEF_STARS "True"
104 #define DEF_LIGHT "True"
105 #define DEF_IMAGE "BUILTIN"
108 #define countof(x) (sizeof((x))/sizeof((*x)))
110 static int do_rotate;
112 static int do_bounce;
113 static int do_texture;
116 static char *which_image;
117 static XrmOptionDescRec opts[] = {
118 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, (caddr_t) "true" },
119 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, (caddr_t) "false" },
120 {"-roll", ".glplanet.roll", XrmoptionNoArg, (caddr_t) "true" },
121 {"+roll", ".glplanet.roll", XrmoptionNoArg, (caddr_t) "false" },
122 {"-bounce", ".glplanet.bounce", XrmoptionNoArg, (caddr_t) "true" },
123 {"+bounce", ".glplanet.bounce", XrmoptionNoArg, (caddr_t) "false" },
124 {"-texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "true" },
125 {"+texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "false" },
126 {"-stars", ".glplanet.stars", XrmoptionNoArg, (caddr_t) "true" },
127 {"+stars", ".glplanet.stars", XrmoptionNoArg, (caddr_t) "false" },
128 {"-light", ".glplanet.light", XrmoptionNoArg, (caddr_t) "true" },
129 {"+light", ".glplanet.light", XrmoptionNoArg, (caddr_t) "false" },
130 {"-image", ".glplanet.image", XrmoptionSepArg, (caddr_t) 0 },
133 static argtype vars[] = {
134 {(caddr_t *) &do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
135 {(caddr_t *) &do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
136 {(caddr_t *) &do_bounce, "bounce", "Bounce", DEF_BOUNCE, t_Bool},
137 {(caddr_t *) &do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
138 {(caddr_t *) &do_stars, "stars", "Stars", DEF_STARS, t_Bool},
139 {(caddr_t *) &do_light, "light", "Light", DEF_LIGHT, t_Bool},
140 {(caddr_t *) &which_image, "image", "Image", DEF_IMAGE, t_String},
143 ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
146 ModStruct planet_description =
147 {"planet", "init_planet", "draw_planet", "release_planet",
148 "draw_planet", "init_planet", NULL, &planet_opts,
149 1000, 1, 2, 1, 4, 1.0, "",
150 "Animates texture mapped sphere (planet)", 0, NULL};
153 #include "../images/earth.xbm"
154 #include "xpm-ximage.h"
158 * slices and stacks are used in the sphere parameterization routine.
159 * more slices and stacks will increase the quality of the sphere,
160 * at the expense of rendering speed
163 #define NUM_STARS 1000
167 /* radius of the sphere- fairly arbitrary */
170 /* distance away from the sphere model */
175 /* structure for holding the planet data */
179 int screen_width, screen_height;
180 GLXContext *glx_context;
186 GLfloat dtx, dty, dtz;
187 GLfloat xpos, ypos, zpos;
189 GLfloat box_width, box_height, box_depth;
194 static planetstruct *planets = NULL;
198 normalize(GLfloat v[3])
200 GLfloat d = (GLfloat) sqrt((double) (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));
207 v[0] = v[1] = v[2] = 0;
212 /* Set up and enable texturing on our object */
214 setup_xbm_texture (char *bits, int width, int height,
215 XColor *fgc, XColor *bgc)
217 unsigned int fg = (((fgc->red >> 8) << 16) |
218 ((fgc->green >> 8) << 8) |
220 unsigned int bg = (((bgc->red >> 8) << 16) |
221 ((bgc->green >> 8) << 8) |
224 unsigned char *data = (unsigned char *)
225 malloc ((width * height * 24) / 8);
226 unsigned char *out = data;
229 for (y = 0; y < height; y++)
230 for (x = 0; x < width; x++)
232 unsigned char byte = bits [(y * (width / 8) + (x / 8))];
233 unsigned char bit = (byte & (1 << (x % 8))) >> (x % 8);
234 unsigned int word = (bit ? bg : fg);
235 *out++ = (word & 0xFF0000) >> 16;
236 *out++ = (word & 0x00FF00) >> 8;
237 *out++ = (word & 0x0000FF);
241 glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0,
242 GL_RGB, GL_UNSIGNED_BYTE, data);
243 check_gl_error("texture");
245 /* setup parameters for texturing */
246 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
247 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
248 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
249 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
250 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
251 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
256 setup_file_texture (ModeInfo *mi, char *filename)
258 Display *dpy = mi->dpy;
259 Visual *visual = mi->xgwa.visual;
260 Colormap cmap = mi->xgwa.colormap;
265 int result = XpmReadFileToData (filename, &xpm_data);
269 XImage *image = xpm_to_ximage (dpy, visual, cmap, xpm_data);
272 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
273 image->width, image->height, 0,
274 GL_RGBA, GL_UNSIGNED_BYTE, image->data);
275 check_gl_error("texture");
277 /* setup parameters for texturing */
278 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
279 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
281 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
282 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
283 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
284 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
285 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
291 fprintf (stderr, "%s: file %s doesn't exist.\n", progname, filename);
296 /* Fall through and try it as an XBM. */
300 fprintf (stderr, "%s: XPM: out of memory\n", progname);
305 fprintf (stderr, "%s: XPM: unknown error code %d\n", progname, result);
310 #endif /* HAVE_XPM */
314 planetstruct *gp = &planets[MI_SCREEN(mi)];
315 unsigned int width = 0;
316 unsigned int height = 0;
317 unsigned char *data = 0;
319 int status = XmuReadBitmapDataFromFile (filename, &width, &height, &data,
321 if (status != Success)
324 fprintf (stderr, "%s: not an XPM file: %s\n", progname, filename);
326 fprintf (stderr, "%s: not an XBM file: %s\n", progname, filename);
330 setup_xbm_texture ((char *) data, width, height, &gp->fg, &gp->bg);
335 fprintf (stderr, "%s: not an XPM file: %s\n", progname, filename);
337 fprintf (stderr, "%s: your vendor doesn't ship the standard Xmu library.\n",
339 fprintf (stderr, "%s: we can't load XBM files without it.\n",progname);
346 setup_texture(ModeInfo * mi)
348 planetstruct *gp = &planets[MI_SCREEN(mi)];
351 !strcmp(which_image, "BUILTIN"))
352 setup_xbm_texture (earth_bits, earth_width, earth_height,
355 setup_file_texture (mi, which_image);
359 /* Set up and enable lighting */
363 /* set a number of parameters which make the scene look much nicer */
365 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
366 glShadeModel(GL_SMOOTH);
370 /* Set up and enable face culling so we don't see the inside of the sphere */
374 glEnable(GL_CULL_FACE);
379 /* Function for determining points on the surface of the sphere */
380 static void inline ParametricSphere(float theta, float rho, GLfloat *vector)
382 vector[0] = -sin(theta) * sin(rho);
383 vector[1] = cos(theta) * sin(rho);
384 vector[2] = cos(rho);
387 vector[0] = -(1- cos(theta)) * cos(rho);
388 vector[1] = -(1- cos(theta)) * sin(rho);
389 vector[2] = -(sin(theta) + rho);
390 #endif /* DO_HELIX */
396 /* lame way to generate some random stars */
397 void generate_stars(int width, int height)
400 /* GLfloat size_range[2], size;*/
403 planetstruct *gp = &planets[MI_SCREEN(mi)];
405 /* glGetFloatv(GL_POINT_SIZE_RANGE, size_range); */
407 /* printf("size range: %f\t%f\n", size_range[0], size_range[1]); */
408 gp->starlist = glGenLists(1);
409 glNewList(gp->starlist, GL_COMPILE);
411 /* this hackery makes the viewport map one-to-one with Vertex arguments */
412 glMatrixMode(GL_PROJECTION);
414 glMatrixMode(GL_PROJECTION);
416 gluOrtho2D(0, width, 0, height);
417 glMatrixMode(GL_MODELVIEW);
420 /* disable depth testing for the stars, so they don't obscure the planet */
421 glDisable(GL_DEPTH_TEST);
422 glEnable(GL_POINT_SMOOTH);
424 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
427 for(i = 0 ; i < NUM_STARS ; i++)
429 /* size = ((random()%size_range[0])) * size_range[1]/2.; */
430 /* glPointSize(size); */
431 x = random() % width;
432 y = random() % height;
437 /* return to original PROJECT and MODELVIEW */
438 glMatrixMode(GL_PROJECTION);
440 glMatrixMode(GL_MODELVIEW);
447 /* Initialization function for screen saver */
451 Bool wire = MI_IS_WIREFRAME(mi);
452 planetstruct *gp = &planets[MI_SCREEN(mi)];
454 int stacks=STACKS, slices=SLICES;
460 GLfloat ds, dt, t, s;;
463 glEnable(GL_LINE_SMOOTH);
467 /* turn on various options we like */
476 glEnable(GL_POINT_SMOOTH);
477 generate_stars(MI_WIDTH(mi), MI_HEIGHT(mi));
482 * Generate a sphere with quadrilaterals.
483 * Quad vertices are determined using a parametric sphere function.
484 * For fun, you could generate practically any parameteric surface and
485 * map an image onto it.
488 drho = M_PI / stacks;
489 dtheta = 2.0 * M_PI / slices;
494 gp->platelist=glGenLists(1);
495 glNewList(gp->platelist, GL_COMPILE);
498 glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
501 for(i=0; i<stacks; i++) {
504 for(j=0; j<slices; j++) {
509 ParametricSphere(theta, rho, vector);
512 ParametricSphere(theta, rho, vector);
513 glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
515 glTexCoord2f(s,t+dt);
516 ParametricSphere(theta, rho+drho, vector);
519 ParametricSphere(theta, rho+drho, vector);
520 glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
522 glTexCoord2f(s+ds,t+dt);
523 ParametricSphere(theta + dtheta, rho+drho, vector);
526 ParametricSphere(theta + dtheta, rho+drho, vector);
527 glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
529 glTexCoord2f(s+ds, t);
530 ParametricSphere(theta + dtheta, rho, vector);
533 ParametricSphere(theta + dtheta, rho, vector);
534 glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
548 draw_sphere(ModeInfo * mi)
550 planetstruct *gp = &planets[MI_SCREEN(mi)];
552 glEnable(GL_DEPTH_TEST);
554 /* turn on the various attributes for making the sphere look nice */
556 glEnable(GL_TEXTURE_2D);
560 glEnable(GL_LIGHTING);
562 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
563 glEnable(GL_COLOR_MATERIAL);
566 glCallList(gp->platelist);
571 #define RANDSIGN() ((random() & 1) ? 1 : -1)
574 pick_velocity (ModeInfo * mi)
576 planetstruct *gp = &planets[MI_SCREEN(mi)];
578 gp->box_width = 15.0;
579 gp->box_height = 15.0;
586 gp->dtx = (frand(0.4) + frand(0.3)) * RANDSIGN();
587 gp->dty = (frand(0.4) + frand(0.3)) * RANDSIGN();
588 gp->dtz = (frand(5.0) + frand(5.0)); /* the sun sets in the west */
590 gp->dx = (frand(0.2) + frand(0.2)) * RANDSIGN();
591 gp->dy = (frand(0.2) + frand(0.2)) * RANDSIGN();
592 gp->dz = (frand(0.2) + frand(0.2)) * RANDSIGN();
597 rotate_and_move (ModeInfo * mi)
599 planetstruct *gp = &planets[MI_SCREEN(mi)];
604 while (gp->tx < 0) gp->tx += 360;
605 while (gp->tx > 360) gp->tx -= 360;
608 while (gp->ty < 0) gp->ty += 360;
609 while (gp->ty > 360) gp->ty -= 360;
615 while (gp->tz < 0) gp->tz += 360;
616 while (gp->tz > 360) gp->tz -= 360;
621 /* Move in the direction we had been moving in. */
627 if (gp->xpos > gp->box_depth)
628 gp->xpos = gp->box_depth, gp->dx = -gp->dx;
629 else if (gp->xpos < 0)
630 gp->xpos = 0, gp->dx = -gp->dx;
632 if (gp->ypos > gp->box_width/2)
633 gp->ypos = gp->box_width/2, gp->dy = -gp->dy;
634 else if (gp->ypos < -gp->box_width/2)
635 gp->ypos = -gp->box_width/2, gp->dy = -gp->dy;
637 if (gp->zpos > gp->box_height/2)
638 gp->zpos = gp->box_height/2, gp->dz = -gp->dz;
639 else if (gp->zpos < -gp->box_height/2)
640 gp->zpos = -gp->box_height/2, gp->dz = -gp->dz;
645 /* Standard reshape function */
647 reshape(int width, int height)
650 GLfloat h = (GLfloat) height / (GLfloat) width;
653 light[1] = (int) (((random() % 3) & 0xFF) - 1);
654 light[2] = (int) (((random() % 3) & 0xFF) - 1);
657 glViewport(0, 0, (GLint) width, (GLint) height);
658 glMatrixMode(GL_PROJECTION);
660 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
661 glMatrixMode(GL_MODELVIEW);
663 glTranslatef(0.0, 0.0, -DIST);
664 glLightfv(GL_LIGHT0, GL_POSITION, light);
665 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
671 init_planet(ModeInfo * mi)
673 int screen = MI_SCREEN(mi);
677 if (planets == NULL) {
678 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
679 sizeof (planetstruct))) == NULL)
682 gp = &planets[screen];
687 char *f = get_string_resource("imageForeground", "Foreground");
688 char *b = get_string_resource("imageBackground", "Background");
690 if (!f) f = strdup("white");
691 if (!b) b = strdup("black");
693 for (s = f + strlen(f)-1; s > f; s--)
694 if (*s == ' ' || *s == '\t')
696 for (s = b + strlen(b)-1; s > b; s--)
697 if (*s == ' ' || *s == '\t')
700 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
702 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
705 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
707 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
716 gp->window = MI_WINDOW(mi);
717 if ((gp->glx_context = init_GL(mi)) != NULL) {
718 reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
726 draw_planet(ModeInfo * mi)
728 planetstruct *gp = &planets[MI_SCREEN(mi)];
729 Display *display = MI_DISPLAY(mi);
730 Window window = MI_WINDOW(mi);
732 if (!gp->glx_context)
735 glDrawBuffer(GL_BACK);
736 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
738 glXMakeCurrent(display, window, *(gp->glx_context));
742 /* protect our modelview matrix and attributes */
744 glPushAttrib(GL_ALL_ATTRIB_BITS);
747 /* draw the star field. */
748 glCallList(gp->starlist);
755 /* protect our modelview matrix and attributes */
757 glPushAttrib(GL_ALL_ATTRIB_BITS);
759 /* this pair of rotations seem to be necessary to orient the earth correctly */
763 glTranslatef(gp->xpos, gp->ypos, gp->zpos);
764 glRotatef(gp->tx, 1, 0, 0);
765 glRotatef(gp->ty, 0, 1, 0);
766 glRotatef(gp->tz, 0, 0, 1);
767 /* draw the sphere */
776 glXSwapBuffers(display, window);
778 rotate_and_move (mi);
782 release_planet(ModeInfo * mi)
784 if (planets != NULL) {
787 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
788 planetstruct *gp = &planets[screen];
790 if (gp->glx_context) {
791 /* Display lists MUST be freed while their glXContext is current. */
792 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
794 if (glIsList(gp->platelist))
795 glDeleteLists(gp->platelist, 1);
796 if (glIsList(gp->starlist))
797 glDeleteLists(gp->starlist, 1);
800 (void) free((void *) planets);