1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* glplanet --- 3D rotating planet, e.g., Earth.
4 * Permission to use, copy, modify, and distribute this software and its
5 * documentation for any purpose and without fee is hereby granted,
6 * provided that the above copyright notice appear in all copies and that
7 * both that copyright notice and this permission notice appear in
8 * supporting documentation.
10 * This file is provided AS IS with no warranties of any kind. The author
11 * shall have no liability with respect to the infringement of copyrights,
12 * trade secrets or any patents by this file or any part thereof. In no
13 * event will the author be liable for any lost revenue or profits or
14 * other special, indirect and consequential damages.
17 * 21-Mar-01: jwz@jwz.org Broke sphere routine out into its own file.
19 * 9-Oct-98: dek@cgl.ucsf.edu Added stars.
21 * 8-Oct-98: jwz@jwz.org Made the 512x512x1 xearth image be built in.
22 * Made it possible to load XPM or XBM files.
23 * Made the planet bounce and roll around.
25 * 8-Oct-98: Released initial version of "glplanet"
26 * (David Konerding, dek@cgl.ucsf.edu)
31 * For even more spectacular results, grab the images from the "SSysten"
32 * package (http://www.msu.edu/user/kamelkev/) and do this:
34 * cd ssystem-1.4/hires/
36 * djpeg $f | ppmquant 254 | ppmtoxpm > /tmp/$f:r.xpm
47 * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
48 * otherwise caddr_t is not defined correctly
51 #include <X11/Intrinsic.h>
54 # define PROGCLASS "Planet"
55 # define HACK_INIT init_planet
56 # define HACK_DRAW draw_planet
57 # define HACK_RESHAPE reshape_planet
58 # define planet_opts xlockmore_opts
59 #define DEFAULTS "*delay: 15000 \n" \
60 "*showFPS: False \n" \
64 "*wireframe: False \n" \
68 "*image: BUILTIN \n" \
69 "*imageForeground: Green \n" \
70 "*imageBackground: Blue \n"
72 # include "xlockmore.h" /* from the xscreensaver distribution */
73 #else /* !STANDALONE */
74 # include "xlock.h" /* from the xlockmore distribution */
75 #endif /* !STANDALONE */
77 #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);
380 /* Function for determining points on the surface of the sphere */
381 static void inline ParametricSphere(float theta, float rho, GLfloat *vector)
383 vector[0] = -sin(theta) * sin(rho);
384 vector[1] = cos(theta) * sin(rho);
385 vector[2] = cos(rho);
388 vector[0] = -(1- cos(theta)) * cos(rho);
389 vector[1] = -(1- cos(theta)) * sin(rho);
390 vector[2] = -(sin(theta) + rho);
391 #endif /* DO_HELIX */
398 /* lame way to generate some random stars */
399 void generate_stars(int width, int height)
402 /* GLfloat size_range[2], size;*/
405 planetstruct *gp = &planets[MI_SCREEN(mi)];
407 /* glGetFloatv(GL_POINT_SIZE_RANGE, size_range); */
409 /* printf("size range: %f\t%f\n", size_range[0], size_range[1]); */
410 gp->starlist = glGenLists(1);
411 glNewList(gp->starlist, GL_COMPILE);
413 /* this hackery makes the viewport map one-to-one with Vertex arguments */
414 glMatrixMode(GL_PROJECTION);
416 glMatrixMode(GL_PROJECTION);
418 gluOrtho2D(0, width, 0, height);
419 glMatrixMode(GL_MODELVIEW);
422 /* disable depth testing for the stars, so they don't obscure the planet */
423 glDisable(GL_DEPTH_TEST);
424 glEnable(GL_POINT_SMOOTH);
426 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
429 for(i = 0 ; i < NUM_STARS ; i++)
431 /* size = ((random()%size_range[0])) * size_range[1]/2.; */
432 /* glPointSize(size); */
433 x = random() % width;
434 y = random() % height;
439 /* return to original PROJECT and MODELVIEW */
440 glMatrixMode(GL_PROJECTION);
442 glMatrixMode(GL_MODELVIEW);
449 /* Initialization function for screen saver */
453 Bool wire = MI_IS_WIREFRAME(mi);
454 planetstruct *gp = &planets[MI_SCREEN(mi)];
457 glEnable(GL_LINE_SMOOTH);
461 /* turn on various options we like */
470 glEnable(GL_POINT_SMOOTH);
471 generate_stars(MI_WIDTH(mi), MI_HEIGHT(mi));
474 gp->platelist=glGenLists(1);
475 glNewList(gp->platelist, GL_COMPILE);
477 glScalef (RADIUS, RADIUS, RADIUS);
478 unit_sphere (STACKS, SLICES, wire);
484 draw_sphere(ModeInfo * mi)
486 planetstruct *gp = &planets[MI_SCREEN(mi)];
488 glEnable(GL_DEPTH_TEST);
490 /* turn on the various attributes for making the sphere look nice */
492 glEnable(GL_TEXTURE_2D);
496 glEnable(GL_LIGHTING);
498 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
499 glEnable(GL_COLOR_MATERIAL);
502 glCallList(gp->platelist);
507 #define RANDSIGN() ((random() & 1) ? 1 : -1)
510 pick_velocity (ModeInfo * mi)
512 planetstruct *gp = &planets[MI_SCREEN(mi)];
514 gp->box_width = 15.0;
515 gp->box_height = 15.0;
522 gp->dtx = (frand(0.4) + frand(0.3)) * RANDSIGN();
523 gp->dty = (frand(0.4) + frand(0.3)) * RANDSIGN();
524 gp->dtz = (frand(5.0) + frand(5.0)); /* the sun sets in the west */
526 gp->dx = (frand(0.2) + frand(0.2)) * RANDSIGN();
527 gp->dy = (frand(0.2) + frand(0.2)) * RANDSIGN();
528 gp->dz = (frand(0.2) + frand(0.2)) * RANDSIGN();
533 rotate_and_move (ModeInfo * mi)
535 planetstruct *gp = &planets[MI_SCREEN(mi)];
540 while (gp->tx < 0) gp->tx += 360;
541 while (gp->tx > 360) gp->tx -= 360;
544 while (gp->ty < 0) gp->ty += 360;
545 while (gp->ty > 360) gp->ty -= 360;
551 while (gp->tz < 0) gp->tz += 360;
552 while (gp->tz > 360) gp->tz -= 360;
557 static int frame = 0;
558 # define SINOID(SCALE,SIZE) \
559 ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
560 gp->xpos = SINOID(0.031, gp->box_width);
561 gp->ypos = SINOID(0.023, gp->box_height);
562 gp->zpos = SINOID(0.017, gp->box_depth);
568 /* Standard reshape function */
570 reshape_planet(ModeInfo *mi, int width, int height)
573 GLfloat h = (GLfloat) height / (GLfloat) width;
576 light[1] = (int) (((random() % 3) & 0xFF) - 1);
577 light[2] = (int) (((random() % 3) & 0xFF) - 1);
580 glViewport(0, 0, (GLint) width, (GLint) height);
581 glMatrixMode(GL_PROJECTION);
583 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
584 glMatrixMode(GL_MODELVIEW);
586 glTranslatef(0.0, 0.0, -DIST);
587 glLightfv(GL_LIGHT0, GL_POSITION, light);
588 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
594 init_planet(ModeInfo * mi)
596 int screen = MI_SCREEN(mi);
600 if (planets == NULL) {
601 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
602 sizeof (planetstruct))) == NULL)
605 gp = &planets[screen];
610 char *f = get_string_resource("imageForeground", "Foreground");
611 char *b = get_string_resource("imageBackground", "Background");
613 if (!f) f = strdup("white");
614 if (!b) b = strdup("black");
616 for (s = f + strlen(f)-1; s > f; s--)
617 if (*s == ' ' || *s == '\t')
619 for (s = b + strlen(b)-1; s > b; s--)
620 if (*s == ' ' || *s == '\t')
623 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
625 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
628 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
630 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
639 gp->window = MI_WINDOW(mi);
640 if ((gp->glx_context = init_GL(mi)) != NULL) {
641 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
649 draw_planet(ModeInfo * mi)
651 planetstruct *gp = &planets[MI_SCREEN(mi)];
652 Display *display = MI_DISPLAY(mi);
653 Window window = MI_WINDOW(mi);
655 if (!gp->glx_context)
658 glDrawBuffer(GL_BACK);
659 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
661 glXMakeCurrent(display, window, *(gp->glx_context));
665 /* protect our modelview matrix and attributes */
667 glPushAttrib(GL_ALL_ATTRIB_BITS);
670 /* draw the star field. */
671 glCallList(gp->starlist);
678 /* protect our modelview matrix and attributes */
680 glPushAttrib(GL_ALL_ATTRIB_BITS);
682 /* this pair of rotations seem to be necessary to orient the earth correctly */
686 glTranslatef(gp->xpos, gp->ypos, gp->zpos);
687 glRotatef(gp->tx, 1, 0, 0);
688 glRotatef(gp->ty, 0, 1, 0);
689 glRotatef(gp->tz, 0, 0, 1);
690 /* draw the sphere */
698 if (mi->fps_p) do_fps (mi);
700 glXSwapBuffers(display, window);
702 rotate_and_move (mi);
706 release_planet(ModeInfo * mi)
708 if (planets != NULL) {
711 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
712 planetstruct *gp = &planets[screen];
714 if (gp->glx_context) {
715 /* Display lists MUST be freed while their glXContext is current. */
716 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
718 if (glIsList(gp->platelist))
719 glDeleteLists(gp->platelist, 1);
720 if (glIsList(gp->starlist))
721 glDeleteLists(gp->starlist, 1);
724 (void) free((void *) planets);