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.
18 * 16-Jan-02: jwz@jwz.org gdk_pixbuf support.
19 * 21-Mar-01: jwz@jwz.org Broke sphere routine out into its own file.
21 * 9-Oct-98: dek@cgl.ucsf.edu Added stars.
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)
33 * For even more spectacular results, grab the images from the "SSystem"
34 * package (http://www.msu.edu/user/kamelkev/) and use its JPEGs!
39 * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
40 * otherwise caddr_t is not defined correctly
43 #include <X11/Intrinsic.h>
46 # define PROGCLASS "Planet"
47 # define HACK_INIT init_planet
48 # define HACK_DRAW draw_planet
49 # define HACK_RESHAPE reshape_planet
50 # define HACK_HANDLE_EVENT planet_handle_event
51 # define EVENT_MASK PointerMotionMask
52 # define planet_opts xlockmore_opts
53 #define DEFAULTS "*delay: 15000 \n" \
54 "*showFPS: False \n" \
58 "*wireframe: False \n" \
62 "*image: BUILTIN \n" \
63 "*imageForeground: Green \n" \
64 "*imageBackground: Blue \n"
66 # include "xlockmore.h" /* from the xscreensaver distribution */
67 #else /* !STANDALONE */
68 # include "xlock.h" /* from the xlockmore distribution */
69 #endif /* !STANDALONE */
71 #ifdef USE_GL /* whole file */
77 # include <X11/Xmu/Drawing.h>
79 # include <Xmu/Drawing.h>
86 #define DEF_ROTATE "True"
87 #define DEF_ROLL "True"
88 #define DEF_WANDER "True"
89 #define DEF_TEXTURE "True"
90 #define DEF_STARS "True"
91 #define DEF_LIGHT "True"
92 #define DEF_RESOLUTION "64"
93 #define DEF_IMAGE "BUILTIN"
96 #define countof(x) (sizeof((x))/sizeof((*x)))
100 static int do_wander;
101 static int do_texture;
104 static char *which_image;
105 static int resolution;
107 static XrmOptionDescRec opts[] = {
108 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, (caddr_t) "true" },
109 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, (caddr_t) "false" },
110 {"-roll", ".glplanet.roll", XrmoptionNoArg, (caddr_t) "true" },
111 {"+roll", ".glplanet.roll", XrmoptionNoArg, (caddr_t) "false" },
112 {"-wander", ".glplanet.wander", XrmoptionNoArg, (caddr_t) "true" },
113 {"+wander", ".glplanet.wander", XrmoptionNoArg, (caddr_t) "false" },
114 {"-texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "true" },
115 {"+texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "false" },
116 {"-stars", ".glplanet.stars", XrmoptionNoArg, (caddr_t) "true" },
117 {"+stars", ".glplanet.stars", XrmoptionNoArg, (caddr_t) "false" },
118 {"-light", ".glplanet.light", XrmoptionNoArg, (caddr_t) "true" },
119 {"+light", ".glplanet.light", XrmoptionNoArg, (caddr_t) "false" },
120 {"-image", ".glplanet.image", XrmoptionSepArg, (caddr_t) 0 },
121 {"-resolution", ".glplanet.resolution", XrmoptionSepArg, (caddr_t) 0 },
124 static argtype vars[] = {
125 {(caddr_t *) &do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
126 {(caddr_t *) &do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
127 {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
128 {(caddr_t *) &do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
129 {(caddr_t *) &do_stars, "stars", "Stars", DEF_STARS, t_Bool},
130 {(caddr_t *) &do_light, "light", "Light", DEF_LIGHT, t_Bool},
131 {(caddr_t *) &which_image, "image", "Image", DEF_IMAGE, t_String},
132 {(caddr_t *) &resolution, "resolution","Resolution", DEF_RESOLUTION, t_Int},
135 ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
138 ModStruct planet_description =
139 {"planet", "init_planet", "draw_planet", "release_planet",
140 "draw_planet", "init_planet", NULL, &planet_opts,
141 1000, 1, 2, 1, 4, 1.0, "",
142 "Animates texture mapped sphere (planet)", 0, NULL};
145 #include "../images/earth.xpm"
146 #include "xpm-ximage.h"
148 #include "gltrackball.h"
152 * slices and stacks are used in the sphere parameterization routine.
153 * more slices and stacks will increase the quality of the sphere,
154 * at the expense of rendering speed
157 #define NUM_STARS 1000
159 /* radius of the sphere- fairly arbitrary */
162 /* distance away from the sphere model */
167 /* structure for holding the planet data */
172 int screen_width, screen_height;
173 GLXContext *glx_context;
179 trackball_state *trackball;
184 static planetstruct *planets = NULL;
188 normalize(GLfloat v[3])
190 GLfloat d = (GLfloat) sqrt((double) (v[0] * v[0] +
201 v[0] = v[1] = v[2] = 0;
206 /* Set up and enable texturing on our object */
208 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
210 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
211 MI_COLORMAP (mi), xpm_data);
214 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
215 image->width, image->height, 0,
216 GL_RGBA, GL_UNSIGNED_BYTE, image->data);
217 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
220 /* setup parameters for texturing */
221 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
222 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
223 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
224 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
225 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
226 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
231 setup_file_texture (ModeInfo *mi, char *filename)
233 Display *dpy = mi->dpy;
234 Visual *visual = mi->xgwa.visual;
237 Colormap cmap = mi->xgwa.colormap;
238 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
241 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
242 image->width, image->height, 0,
243 GL_RGBA, GL_UNSIGNED_BYTE, image->data);
244 sprintf (buf, "texture: %.100s (%dx%d)",
245 filename, image->width, image->height);
248 /* setup parameters for texturing */
249 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
250 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
252 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
253 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
254 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
255 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
256 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
261 setup_texture(ModeInfo * mi)
263 /* planetstruct *gp = &planets[MI_SCREEN(mi)];*/
265 glEnable(GL_TEXTURE_2D);
269 !strcmp(which_image, "BUILTIN"))
270 setup_xpm_texture (mi, earth_xpm);
272 setup_file_texture (mi, which_image);
274 check_gl_error("texture initialization");
276 /* Need to flip the texture top for bottom for some reason. */
277 glMatrixMode (GL_TEXTURE);
279 glMatrixMode (GL_MODELVIEW);
284 init_stars (ModeInfo *mi)
287 GLfloat max_size = 3;
289 int steps = max_size / inc;
290 int width = MI_WIDTH(mi);
291 int height = MI_HEIGHT(mi);
293 planetstruct *gp = &planets[MI_SCREEN(mi)];
294 Bool wire = MI_IS_WIREFRAME(mi);
297 glEnable (GL_POINT_SMOOTH);
299 gp->starlist = glGenLists(1);
300 glNewList(gp->starlist, GL_COMPILE);
302 glScalef (1.0/width, 1.0/height, 1);
304 for (j = 1; j <= steps; j++)
306 glPointSize(inc * j);
308 for (i = 0 ; i < NUM_STARS / steps; i++)
310 glColor3f (0.6 + frand(0.3),
313 glVertex2f ((GLfloat) (random() % width),
314 (GLfloat) (random() % height));
320 check_gl_error("stars initialization");
325 draw_stars (ModeInfo * mi)
327 int width = MI_WIDTH(mi);
328 int height = MI_HEIGHT(mi);
330 planetstruct *gp = &planets[MI_SCREEN(mi)];
332 /* Sadly, this causes a stall of the graphics pipeline (as would the
333 equivalent calls to glGet*.) But there's no way around this, short
334 of having each caller set up the specific display matrix we need
335 here, which would kind of defeat the purpose of centralizing this
338 glPushAttrib(GL_TRANSFORM_BIT | /* for matrix contents */
339 GL_ENABLE_BIT | /* for various glDisable calls */
340 GL_CURRENT_BIT | /* for glColor3f() */
341 GL_LIST_BIT); /* for glListBase() */
343 check_gl_error ("glPushAttrib");
345 /* disable lighting and texturing when drawing stars!
346 (glPopAttrib() restores these.)
348 glDisable(GL_TEXTURE_2D);
349 glDisable(GL_LIGHTING);
350 glDisable(GL_DEPTH_TEST);
352 /* glPopAttrib() does not restore matrix changes, so we must
353 push/pop the matrix stacks to be non-intrusive there.
355 glMatrixMode(GL_PROJECTION);
358 check_gl_error ("glPushMatrix");
361 /* Each matrix mode has its own stack, so we need to push/pop
363 glMatrixMode(GL_MODELVIEW);
366 check_gl_error ("glPushMatrix");
369 gluOrtho2D (0, width, 0, height);
370 check_gl_error ("gluOrtho2D");
373 glScalef (width, height, 1);
374 glCallList(gp->starlist);
375 check_gl_error ("drawing stars");
379 glMatrixMode(GL_PROJECTION);
383 /* clean up after our state changes */
385 check_gl_error ("glPopAttrib");
390 /* Set up lighting */
392 init_sun (ModeInfo * mi)
394 planetstruct *gp = &planets[MI_SCREEN(mi)];
396 GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
397 GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
398 GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
400 GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
401 GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
402 GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
406 double h = 0.1 + frand(0.8); /* east-west position - screen-side. */
407 double v = -0.3 + frand(0.6); /* north-south position */
409 if (h > 0.3 && h < 0.8) /* avoid having the sun at the camera */
410 h += (h > 0.5 ? 0.2 : -0.2);
412 gp->sunpos[0] = cos(h * M_PI);
413 gp->sunpos[1] = sin(h * M_PI);
414 gp->sunpos[2] = sin(v * M_PI);
415 gp->sunpos[3] = 0.00;
418 glEnable(GL_LIGHTING);
421 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
422 glLightfv (GL_LIGHT0, GL_AMBIENT, lamb);
423 glLightfv (GL_LIGHT0, GL_DIFFUSE, ldif);
424 glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
426 glMaterialfv (GL_FRONT, GL_AMBIENT, mamb);
427 glMaterialfv (GL_FRONT, GL_DIFFUSE, mdif);
428 glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
429 glMaterialf (GL_FRONT, GL_SHININESS, shiny);
433 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
434 glShadeModel(GL_SMOOTH);
436 check_gl_error("lighting");
440 #define RANDSIGN() ((random() & 1) ? 1 : -1)
443 pick_velocity (ModeInfo * mi)
446 planetstruct *gp = &planets[MI_SCREEN(mi)];
448 gp->box_width = 15.0;
449 gp->box_height = 15.0;
456 gp->dtx = (frand(0.4) + frand(0.3)) * RANDSIGN();
457 gp->dty = (frand(0.4) + frand(0.3)) * RANDSIGN();
458 gp->dtz = (frand(5.0) + frand(5.0)); /* the sun sets in the west */
460 gp->dx = (frand(0.2) + frand(0.2)) * RANDSIGN();
461 gp->dy = (frand(0.2) + frand(0.2)) * RANDSIGN();
462 gp->dz = (frand(0.2) + frand(0.2)) * RANDSIGN();
468 reshape_planet (ModeInfo *mi, int width, int height)
470 GLfloat h = (GLfloat) height / (GLfloat) width;
472 glViewport(0, 0, (GLint) width, (GLint) height);
473 glMatrixMode(GL_PROJECTION);
475 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
476 glMatrixMode(GL_MODELVIEW);
478 glTranslatef(0.0, 0.0, -DIST);
480 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
485 planet_handle_event (ModeInfo *mi, XEvent *event)
487 planetstruct *gp = &planets[MI_SCREEN(mi)];
489 if (event->xany.type == ButtonPress &&
490 event->xbutton.button & Button1)
492 gp->button_down_p = True;
493 gltrackball_start (gp->trackball,
494 event->xbutton.x, event->xbutton.y,
495 MI_WIDTH (mi), MI_HEIGHT (mi));
498 else if (event->xany.type == ButtonRelease &&
499 event->xbutton.button & Button1)
501 gp->button_down_p = False;
504 else if (event->xany.type == MotionNotify &&
507 gltrackball_track (gp->trackball,
508 event->xmotion.x, event->xmotion.y,
509 MI_WIDTH (mi), MI_HEIGHT (mi));
518 init_planet (ModeInfo * mi)
521 int screen = MI_SCREEN(mi);
522 Bool wire = MI_IS_WIREFRAME(mi);
524 if (planets == NULL) {
525 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
526 sizeof (planetstruct))) == NULL)
529 gp = &planets[screen];
533 if ((gp->glx_context = init_GL(mi)) != NULL) {
534 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
538 char *f = get_string_resource("imageForeground", "Foreground");
539 char *b = get_string_resource("imageBackground", "Background");
541 if (!f) f = strdup("white");
542 if (!b) b = strdup("black");
544 for (s = f + strlen(f)-1; s > f; s--)
545 if (*s == ' ' || *s == '\t')
547 for (s = b + strlen(b)-1; s > b; s--)
548 if (*s == ' ' || *s == '\t')
551 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
553 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
556 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
558 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
567 double spin_speed = 1.0;
568 double wander_speed = 0.05;
569 gp->rot = make_rotator (do_roll ? spin_speed : 0,
570 do_roll ? spin_speed : 0,
572 do_wander ? wander_speed : 0,
575 gp->trackball = gltrackball_init ();
582 glEnable (GL_LINE_SMOOTH);
594 glEnable(GL_DEPTH_TEST);
595 glEnable(GL_CULL_FACE);
598 /* construct the polygons of the planet
600 gp->platelist = glGenLists(1);
601 glNewList (gp->platelist, GL_COMPILE);
604 glScalef (RADIUS, RADIUS, RADIUS);
605 glRotatef (90, 1, 0, 0);
606 unit_sphere (resolution, resolution, wire);
607 mi->polygon_count += resolution*resolution;
611 /* construct the polygons of the latitude/longitude/axis lines.
613 gp->latlonglist = glGenLists(1);
614 glNewList (gp->latlonglist, GL_COMPILE);
616 if (do_texture) glDisable (GL_TEXTURE_2D);
617 if (do_light) glDisable (GL_LIGHTING);
618 glColor3f (0.1, 0.3, 0.1);
619 glScalef (RADIUS, RADIUS, RADIUS);
620 glScalef (1.01, 1.01, 1.01);
621 glRotatef (90, 1, 0, 0);
622 unit_sphere (12, 24, 1);
624 glVertex3f(0, -2, 0);
627 if (do_light) glEnable(GL_LIGHTING);
628 if (do_texture) glEnable(GL_TEXTURE_2D);
634 draw_planet (ModeInfo * mi)
636 planetstruct *gp = &planets[MI_SCREEN(mi)];
637 Display *display = MI_DISPLAY(mi);
638 Window window = MI_WINDOW(mi);
641 if (!gp->glx_context)
644 glDrawBuffer(GL_BACK);
645 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
647 glXMakeCurrent (display, window, *(gp->glx_context));
654 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
655 glTranslatef((x - 0.5) * 15,
659 gltrackball_rotate (gp->trackball);
661 glRotatef (90,1,0,0);
665 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
666 glRotatef (x * 360, 1.0, 0.0, 0.0);
667 glRotatef (y * 360, 0.0, 1.0, 0.0);
670 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
672 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
673 if (do_rotate && !gp->button_down_p)
676 if (gp->z > 1) gp->z -= 1;
679 glCallList (gp->platelist);
680 if (gp->button_down_p)
681 glCallList (gp->latlonglist);
684 if (mi->fps_p) do_fps (mi);
686 glXSwapBuffers(display, window);
691 release_planet (ModeInfo * mi)
693 if (planets != NULL) {
696 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
697 planetstruct *gp = &planets[screen];
699 if (gp->glx_context) {
700 /* Display lists MUST be freed while their glXContext is current. */
701 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
703 if (glIsList(gp->platelist))
704 glDeleteLists(gp->platelist, 1);
705 if (glIsList(gp->starlist))
706 glDeleteLists(gp->starlist, 1);
709 (void) free((void *) planets);