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 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
126 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
127 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
128 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
129 {&do_stars, "stars", "Stars", DEF_STARS, t_Bool},
130 {&do_light, "light", "Light", DEF_LIGHT, t_Bool},
131 {&which_image, "image", "Image", DEF_IMAGE, t_String},
132 {&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};
146 __extension__ /* don't warn about "string length is greater than the length
147 ISO C89 compilers are required to support" when including
148 the following XPM file... */
150 #include "../images/earth.xpm"
152 #include "xpm-ximage.h"
154 #include "gltrackball.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
165 /* radius of the sphere- fairly arbitrary */
168 /* distance away from the sphere model */
173 /* structure for holding the planet data */
178 int screen_width, screen_height;
179 GLXContext *glx_context;
185 trackball_state *trackball;
190 static planetstruct *planets = NULL;
194 normalize(GLfloat v[3])
196 GLfloat d = (GLfloat) sqrt((double) (v[0] * v[0] +
207 v[0] = v[1] = v[2] = 0;
212 /* Set up and enable texturing on our object */
214 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
216 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
217 MI_COLORMAP (mi), xpm_data);
220 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
221 image->width, image->height, 0,
223 /* GL_UNSIGNED_BYTE, */
224 GL_UNSIGNED_INT_8_8_8_8_REV,
226 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
229 /* setup parameters for texturing */
230 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
231 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
232 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
233 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
234 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
235 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
240 setup_file_texture (ModeInfo *mi, char *filename)
242 Display *dpy = mi->dpy;
243 Visual *visual = mi->xgwa.visual;
246 Colormap cmap = mi->xgwa.colormap;
247 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
250 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
251 image->width, image->height, 0,
253 /* GL_UNSIGNED_BYTE, */
254 GL_UNSIGNED_INT_8_8_8_8_REV,
256 sprintf (buf, "texture: %.100s (%dx%d)",
257 filename, image->width, image->height);
260 /* setup parameters for texturing */
261 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
262 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
264 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
265 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
266 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
267 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
268 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
273 setup_texture(ModeInfo * mi)
275 /* planetstruct *gp = &planets[MI_SCREEN(mi)];*/
277 glEnable(GL_TEXTURE_2D);
281 !strcmp(which_image, "BUILTIN"))
282 setup_xpm_texture (mi, earth_xpm);
284 setup_file_texture (mi, which_image);
286 check_gl_error("texture initialization");
288 /* Need to flip the texture top for bottom for some reason. */
289 glMatrixMode (GL_TEXTURE);
291 glMatrixMode (GL_MODELVIEW);
296 init_stars (ModeInfo *mi)
299 GLfloat max_size = 3;
301 int steps = max_size / inc;
302 int width = MI_WIDTH(mi);
303 int height = MI_HEIGHT(mi);
305 planetstruct *gp = &planets[MI_SCREEN(mi)];
306 Bool wire = MI_IS_WIREFRAME(mi);
309 glEnable (GL_POINT_SMOOTH);
311 gp->starlist = glGenLists(1);
312 glNewList(gp->starlist, GL_COMPILE);
314 glScalef (1.0/width, 1.0/height, 1);
316 for (j = 1; j <= steps; j++)
318 glPointSize(inc * j);
320 for (i = 0 ; i < NUM_STARS / steps; i++)
322 glColor3f (0.6 + frand(0.3),
325 glVertex2f ((GLfloat) (random() % width),
326 (GLfloat) (random() % height));
332 check_gl_error("stars initialization");
337 draw_stars (ModeInfo * mi)
339 int width = MI_WIDTH(mi);
340 int height = MI_HEIGHT(mi);
342 planetstruct *gp = &planets[MI_SCREEN(mi)];
344 /* Sadly, this causes a stall of the graphics pipeline (as would the
345 equivalent calls to glGet*.) But there's no way around this, short
346 of having each caller set up the specific display matrix we need
347 here, which would kind of defeat the purpose of centralizing this
350 glPushAttrib(GL_TRANSFORM_BIT | /* for matrix contents */
351 GL_ENABLE_BIT | /* for various glDisable calls */
352 GL_CURRENT_BIT | /* for glColor3f() */
353 GL_LIST_BIT); /* for glListBase() */
355 check_gl_error ("glPushAttrib");
357 /* disable lighting and texturing when drawing stars!
358 (glPopAttrib() restores these.)
360 glDisable(GL_TEXTURE_2D);
361 glDisable(GL_LIGHTING);
362 glDisable(GL_DEPTH_TEST);
364 /* glPopAttrib() does not restore matrix changes, so we must
365 push/pop the matrix stacks to be non-intrusive there.
367 glMatrixMode(GL_PROJECTION);
370 check_gl_error ("glPushMatrix");
373 /* Each matrix mode has its own stack, so we need to push/pop
375 glMatrixMode(GL_MODELVIEW);
378 check_gl_error ("glPushMatrix");
381 gluOrtho2D (0, width, 0, height);
382 check_gl_error ("gluOrtho2D");
385 glScalef (width, height, 1);
386 glCallList(gp->starlist);
387 check_gl_error ("drawing stars");
391 glMatrixMode(GL_PROJECTION);
395 /* clean up after our state changes */
397 check_gl_error ("glPopAttrib");
402 /* Set up lighting */
404 init_sun (ModeInfo * mi)
406 planetstruct *gp = &planets[MI_SCREEN(mi)];
408 GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
409 GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
410 GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
412 GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
413 GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
414 GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
418 double h = 0.1 + frand(0.8); /* east-west position - screen-side. */
419 double v = -0.3 + frand(0.6); /* north-south position */
421 if (h > 0.3 && h < 0.8) /* avoid having the sun at the camera */
422 h += (h > 0.5 ? 0.2 : -0.2);
424 gp->sunpos[0] = cos(h * M_PI);
425 gp->sunpos[1] = sin(h * M_PI);
426 gp->sunpos[2] = sin(v * M_PI);
427 gp->sunpos[3] = 0.00;
430 glEnable(GL_LIGHTING);
433 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
434 glLightfv (GL_LIGHT0, GL_AMBIENT, lamb);
435 glLightfv (GL_LIGHT0, GL_DIFFUSE, ldif);
436 glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
438 glMaterialfv (GL_FRONT, GL_AMBIENT, mamb);
439 glMaterialfv (GL_FRONT, GL_DIFFUSE, mdif);
440 glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
441 glMaterialf (GL_FRONT, GL_SHININESS, shiny);
445 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
446 glShadeModel(GL_SMOOTH);
448 check_gl_error("lighting");
452 #define RANDSIGN() ((random() & 1) ? 1 : -1)
455 reshape_planet (ModeInfo *mi, int width, int height)
457 GLfloat h = (GLfloat) height / (GLfloat) width;
459 glViewport(0, 0, (GLint) width, (GLint) height);
460 glMatrixMode(GL_PROJECTION);
462 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
463 glMatrixMode(GL_MODELVIEW);
465 glTranslatef(0.0, 0.0, -DIST);
467 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
472 planet_handle_event (ModeInfo *mi, XEvent *event)
474 planetstruct *gp = &planets[MI_SCREEN(mi)];
476 if (event->xany.type == ButtonPress &&
477 event->xbutton.button & Button1)
479 gp->button_down_p = True;
480 gltrackball_start (gp->trackball,
481 event->xbutton.x, event->xbutton.y,
482 MI_WIDTH (mi), MI_HEIGHT (mi));
485 else if (event->xany.type == ButtonRelease &&
486 event->xbutton.button & Button1)
488 gp->button_down_p = False;
491 else if (event->xany.type == MotionNotify &&
494 gltrackball_track (gp->trackball,
495 event->xmotion.x, event->xmotion.y,
496 MI_WIDTH (mi), MI_HEIGHT (mi));
505 init_planet (ModeInfo * mi)
508 int screen = MI_SCREEN(mi);
509 Bool wire = MI_IS_WIREFRAME(mi);
511 if (planets == NULL) {
512 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
513 sizeof (planetstruct))) == NULL)
516 gp = &planets[screen];
518 if ((gp->glx_context = init_GL(mi)) != NULL) {
519 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
523 char *f = get_string_resource("imageForeground", "Foreground");
524 char *b = get_string_resource("imageBackground", "Background");
526 if (!f) f = strdup("white");
527 if (!b) b = strdup("black");
529 for (s = f + strlen(f)-1; s > f; s--)
530 if (*s == ' ' || *s == '\t')
532 for (s = b + strlen(b)-1; s > b; s--)
533 if (*s == ' ' || *s == '\t')
536 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
538 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
541 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
543 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
552 double spin_speed = 1.0;
553 double wander_speed = 0.05;
554 gp->rot = make_rotator (do_roll ? spin_speed : 0,
555 do_roll ? spin_speed : 0,
557 do_wander ? wander_speed : 0,
560 gp->trackball = gltrackball_init ();
567 glEnable (GL_LINE_SMOOTH);
579 glEnable(GL_DEPTH_TEST);
580 glEnable(GL_CULL_FACE);
583 /* construct the polygons of the planet
585 gp->platelist = glGenLists(1);
586 glNewList (gp->platelist, GL_COMPILE);
589 glScalef (RADIUS, RADIUS, RADIUS);
590 glRotatef (90, 1, 0, 0);
591 unit_sphere (resolution, resolution, wire);
592 mi->polygon_count += resolution*resolution;
596 /* construct the polygons of the latitude/longitude/axis lines.
598 gp->latlonglist = glGenLists(1);
599 glNewList (gp->latlonglist, GL_COMPILE);
601 if (do_texture) glDisable (GL_TEXTURE_2D);
602 if (do_light) glDisable (GL_LIGHTING);
603 glColor3f (0.1, 0.3, 0.1);
604 glScalef (RADIUS, RADIUS, RADIUS);
605 glScalef (1.01, 1.01, 1.01);
606 glRotatef (90, 1, 0, 0);
607 unit_sphere (12, 24, 1);
609 glVertex3f(0, -2, 0);
612 if (do_light) glEnable(GL_LIGHTING);
613 if (do_texture) glEnable(GL_TEXTURE_2D);
619 draw_planet (ModeInfo * mi)
621 planetstruct *gp = &planets[MI_SCREEN(mi)];
622 Display *display = MI_DISPLAY(mi);
623 Window window = MI_WINDOW(mi);
626 if (!gp->glx_context)
629 glDrawBuffer(GL_BACK);
630 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
632 glXMakeCurrent (display, window, *(gp->glx_context));
639 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
640 glTranslatef((x - 0.5) * 15,
644 gltrackball_rotate (gp->trackball);
646 glRotatef (90,1,0,0);
650 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
651 glRotatef (x * 360, 1.0, 0.0, 0.0);
652 glRotatef (y * 360, 0.0, 1.0, 0.0);
655 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
657 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
658 if (do_rotate && !gp->button_down_p)
660 gp->z -= 0.01; /* the sun sets in the west */
661 if (gp->z < 0) gp->z += 1;
664 glCallList (gp->platelist);
665 if (gp->button_down_p)
666 glCallList (gp->latlonglist);
669 if (mi->fps_p) do_fps (mi);
671 glXSwapBuffers(display, window);
676 release_planet (ModeInfo * mi)
678 if (planets != NULL) {
681 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
682 planetstruct *gp = &planets[screen];
684 if (gp->glx_context) {
685 /* Display lists MUST be freed while their glXContext is current. */
686 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
688 if (glIsList(gp->platelist))
689 glDeleteLists(gp->platelist, 1);
690 if (glIsList(gp->starlist))
691 glDeleteLists(gp->starlist, 1);
694 (void) free((void *) planets);