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 # define PROGCLASS "Planet"
40 # define HACK_INIT init_planet
41 # define HACK_DRAW draw_planet
42 # define HACK_RESHAPE reshape_planet
43 # define HACK_HANDLE_EVENT planet_handle_event
44 # define EVENT_MASK PointerMotionMask
45 # define planet_opts xlockmore_opts
46 #define DEFAULTS "*delay: 20000 \n" \
47 "*showFPS: False \n" \
51 "*wireframe: False \n" \
55 "*image: BUILTIN \n" \
56 "*imageForeground: Green \n" \
57 "*imageBackground: Blue \n"
59 # include "xlockmore.h" /* from the xscreensaver distribution */
60 #else /* !STANDALONE */
61 # include "xlock.h" /* from the xlockmore distribution */
62 #endif /* !STANDALONE */
64 #ifdef USE_GL /* whole file */
70 # include <X11/Xmu/Drawing.h>
72 # include <Xmu/Drawing.h>
79 #define DEF_ROTATE "True"
80 #define DEF_ROLL "True"
81 #define DEF_WANDER "True"
82 #define DEF_TEXTURE "True"
83 #define DEF_STARS "True"
84 #define DEF_LIGHT "True"
85 #define DEF_RESOLUTION "128"
86 #define DEF_IMAGE "BUILTIN"
89 #define countof(x) (sizeof((x))/sizeof((*x)))
94 static int do_texture;
97 static char *which_image;
98 static int resolution;
100 static XrmOptionDescRec opts[] = {
101 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, "true" },
102 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, "false" },
103 {"-roll", ".glplanet.roll", XrmoptionNoArg, "true" },
104 {"+roll", ".glplanet.roll", XrmoptionNoArg, "false" },
105 {"-wander", ".glplanet.wander", XrmoptionNoArg, "true" },
106 {"+wander", ".glplanet.wander", XrmoptionNoArg, "false" },
107 {"-texture", ".glplanet.texture", XrmoptionNoArg, "true" },
108 {"+texture", ".glplanet.texture", XrmoptionNoArg, "false" },
109 {"-stars", ".glplanet.stars", XrmoptionNoArg, "true" },
110 {"+stars", ".glplanet.stars", XrmoptionNoArg, "false" },
111 {"-light", ".glplanet.light", XrmoptionNoArg, "true" },
112 {"+light", ".glplanet.light", XrmoptionNoArg, "false" },
113 {"-image", ".glplanet.image", XrmoptionSepArg, 0 },
114 {"-resolution", ".glplanet.resolution", XrmoptionSepArg, 0 },
117 static argtype vars[] = {
118 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
119 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
120 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
121 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
122 {&do_stars, "stars", "Stars", DEF_STARS, t_Bool},
123 {&do_light, "light", "Light", DEF_LIGHT, t_Bool},
124 {&which_image, "image", "Image", DEF_IMAGE, t_String},
125 {&resolution, "resolution","Resolution", DEF_RESOLUTION, t_Int},
128 ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
131 ModStruct planet_description =
132 {"planet", "init_planet", "draw_planet", "release_planet",
133 "draw_planet", "init_planet", NULL, &planet_opts,
134 1000, 1, 2, 1, 4, 1.0, "",
135 "Animates texture mapped sphere (planet)", 0, NULL};
139 __extension__ /* don't warn about "string length is greater than the length
140 ISO C89 compilers are required to support" when including
141 the following XPM file... */
143 #include "../images/earth.xpm"
145 #include "xpm-ximage.h"
147 #include "gltrackball.h"
151 * slices and stacks are used in the sphere parameterization routine.
152 * more slices and stacks will increase the quality of the sphere,
153 * at the expense of rendering speed
156 #define NUM_STARS 1000
158 /* radius of the sphere- fairly arbitrary */
161 /* distance away from the sphere model */
166 /* structure for holding the planet data */
171 int screen_width, screen_height;
172 GLXContext *glx_context;
178 trackball_state *trackball;
183 static planetstruct *planets = NULL;
186 /* Set up and enable texturing on our object */
188 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
190 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
191 MI_COLORMAP (mi), xpm_data);
194 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
195 image->width, image->height, 0,
197 /* GL_UNSIGNED_BYTE, */
198 GL_UNSIGNED_INT_8_8_8_8_REV,
200 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
203 /* setup parameters for texturing */
204 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
205 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
206 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
207 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
208 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
209 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
214 setup_file_texture (ModeInfo *mi, char *filename)
216 Display *dpy = mi->dpy;
217 Visual *visual = mi->xgwa.visual;
220 Colormap cmap = mi->xgwa.colormap;
221 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
224 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
225 image->width, image->height, 0,
227 /* GL_UNSIGNED_BYTE, */
228 GL_UNSIGNED_INT_8_8_8_8_REV,
230 sprintf (buf, "texture: %.100s (%dx%d)",
231 filename, image->width, image->height);
234 /* setup parameters for texturing */
235 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
236 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
238 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
239 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
240 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
241 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
242 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
247 setup_texture(ModeInfo * mi)
249 /* planetstruct *gp = &planets[MI_SCREEN(mi)];*/
251 glEnable(GL_TEXTURE_2D);
255 !strcmp(which_image, "BUILTIN"))
256 setup_xpm_texture (mi, earth_xpm);
258 setup_file_texture (mi, which_image);
260 check_gl_error("texture initialization");
262 /* Need to flip the texture top for bottom for some reason. */
263 glMatrixMode (GL_TEXTURE);
265 glMatrixMode (GL_MODELVIEW);
270 init_stars (ModeInfo *mi)
273 GLfloat max_size = 3;
275 int steps = max_size / inc;
276 int width = MI_WIDTH(mi);
277 int height = MI_HEIGHT(mi);
279 planetstruct *gp = &planets[MI_SCREEN(mi)];
280 Bool wire = MI_IS_WIREFRAME(mi);
283 glEnable (GL_POINT_SMOOTH);
285 gp->starlist = glGenLists(1);
286 glNewList(gp->starlist, GL_COMPILE);
288 glScalef (1.0/width, 1.0/height, 1);
290 for (j = 1; j <= steps; j++)
292 glPointSize(inc * j);
294 for (i = 0 ; i < NUM_STARS / steps; i++)
296 glColor3f (0.6 + frand(0.3),
299 glVertex2f ((GLfloat) (random() % width),
300 (GLfloat) (random() % height));
306 check_gl_error("stars initialization");
311 draw_stars (ModeInfo * mi)
313 int width = MI_WIDTH(mi);
314 int height = MI_HEIGHT(mi);
316 planetstruct *gp = &planets[MI_SCREEN(mi)];
318 /* Sadly, this causes a stall of the graphics pipeline (as would the
319 equivalent calls to glGet*.) But there's no way around this, short
320 of having each caller set up the specific display matrix we need
321 here, which would kind of defeat the purpose of centralizing this
324 glPushAttrib(GL_TRANSFORM_BIT | /* for matrix contents */
325 GL_ENABLE_BIT | /* for various glDisable calls */
326 GL_CURRENT_BIT | /* for glColor3f() */
327 GL_LIST_BIT); /* for glListBase() */
329 check_gl_error ("glPushAttrib");
331 /* disable lighting and texturing when drawing stars!
332 (glPopAttrib() restores these.)
334 glDisable(GL_TEXTURE_2D);
335 glDisable(GL_LIGHTING);
336 glDisable(GL_DEPTH_TEST);
338 /* glPopAttrib() does not restore matrix changes, so we must
339 push/pop the matrix stacks to be non-intrusive there.
341 glMatrixMode(GL_PROJECTION);
344 check_gl_error ("glPushMatrix");
347 /* Each matrix mode has its own stack, so we need to push/pop
349 glMatrixMode(GL_MODELVIEW);
352 check_gl_error ("glPushMatrix");
355 gluOrtho2D (0, width, 0, height);
356 check_gl_error ("gluOrtho2D");
359 glScalef (width, height, 1);
360 glCallList(gp->starlist);
361 check_gl_error ("drawing stars");
365 glMatrixMode(GL_PROJECTION);
369 /* clean up after our state changes */
371 check_gl_error ("glPopAttrib");
376 /* Set up lighting */
378 init_sun (ModeInfo * mi)
380 planetstruct *gp = &planets[MI_SCREEN(mi)];
382 GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
383 GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
384 GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
386 GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
387 GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
388 GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
392 double h = 0.1 + frand(0.8); /* east-west position - screen-side. */
393 double v = -0.3 + frand(0.6); /* north-south position */
395 if (h > 0.3 && h < 0.8) /* avoid having the sun at the camera */
396 h += (h > 0.5 ? 0.2 : -0.2);
398 gp->sunpos[0] = cos(h * M_PI);
399 gp->sunpos[1] = sin(h * M_PI);
400 gp->sunpos[2] = sin(v * M_PI);
401 gp->sunpos[3] = 0.00;
404 glEnable(GL_LIGHTING);
407 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
408 glLightfv (GL_LIGHT0, GL_AMBIENT, lamb);
409 glLightfv (GL_LIGHT0, GL_DIFFUSE, ldif);
410 glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
412 glMaterialfv (GL_FRONT, GL_AMBIENT, mamb);
413 glMaterialfv (GL_FRONT, GL_DIFFUSE, mdif);
414 glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
415 glMaterialf (GL_FRONT, GL_SHININESS, shiny);
419 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
420 glShadeModel(GL_SMOOTH);
422 check_gl_error("lighting");
426 #define RANDSIGN() ((random() & 1) ? 1 : -1)
429 reshape_planet (ModeInfo *mi, int width, int height)
431 GLfloat h = (GLfloat) height / (GLfloat) width;
433 glViewport(0, 0, (GLint) width, (GLint) height);
434 glMatrixMode(GL_PROJECTION);
436 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
437 glMatrixMode(GL_MODELVIEW);
439 glTranslatef(0.0, 0.0, -DIST);
441 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
446 planet_handle_event (ModeInfo *mi, XEvent *event)
448 planetstruct *gp = &planets[MI_SCREEN(mi)];
450 if (event->xany.type == ButtonPress &&
451 event->xbutton.button == Button1)
453 gp->button_down_p = True;
454 gltrackball_start (gp->trackball,
455 event->xbutton.x, event->xbutton.y,
456 MI_WIDTH (mi), MI_HEIGHT (mi));
459 else if (event->xany.type == ButtonRelease &&
460 event->xbutton.button == Button1)
462 gp->button_down_p = False;
465 else if (event->xany.type == ButtonPress &&
466 (event->xbutton.button == Button4 ||
467 event->xbutton.button == Button5))
469 gltrackball_mousewheel (gp->trackball, event->xbutton.button, 10,
470 !!event->xbutton.state);
473 else if (event->xany.type == MotionNotify &&
476 gltrackball_track (gp->trackball,
477 event->xmotion.x, event->xmotion.y,
478 MI_WIDTH (mi), MI_HEIGHT (mi));
487 init_planet (ModeInfo * mi)
490 int screen = MI_SCREEN(mi);
491 Bool wire = MI_IS_WIREFRAME(mi);
493 if (planets == NULL) {
494 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
495 sizeof (planetstruct))) == NULL)
498 gp = &planets[screen];
500 if ((gp->glx_context = init_GL(mi)) != NULL) {
501 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
505 char *f = get_string_resource("imageForeground", "Foreground");
506 char *b = get_string_resource("imageBackground", "Background");
508 if (!f) f = strdup("white");
509 if (!b) b = strdup("black");
511 for (s = f + strlen(f)-1; s > f; s--)
512 if (*s == ' ' || *s == '\t')
514 for (s = b + strlen(b)-1; s > b; s--)
515 if (*s == ' ' || *s == '\t')
518 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
520 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
523 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
525 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
534 double spin_speed = 0.5;
535 double wander_speed = 0.02;
536 gp->rot = make_rotator (do_roll ? spin_speed : 0,
537 do_roll ? spin_speed : 0,
539 do_wander ? wander_speed : 0,
542 gp->trackball = gltrackball_init ();
549 glEnable (GL_LINE_SMOOTH);
561 glEnable(GL_DEPTH_TEST);
562 glEnable(GL_CULL_FACE);
565 /* construct the polygons of the planet
567 gp->platelist = glGenLists(1);
568 glNewList (gp->platelist, GL_COMPILE);
571 glScalef (RADIUS, RADIUS, RADIUS);
572 glRotatef (90, 1, 0, 0);
573 unit_sphere (resolution, resolution, wire);
574 mi->polygon_count += resolution*resolution;
578 /* construct the polygons of the latitude/longitude/axis lines.
580 gp->latlonglist = glGenLists(1);
581 glNewList (gp->latlonglist, GL_COMPILE);
583 if (do_texture) glDisable (GL_TEXTURE_2D);
584 if (do_light) glDisable (GL_LIGHTING);
585 glColor3f (0.1, 0.3, 0.1);
586 glScalef (RADIUS, RADIUS, RADIUS);
587 glScalef (1.01, 1.01, 1.01);
588 glRotatef (90, 1, 0, 0);
589 unit_sphere (12, 24, 1);
591 glVertex3f(0, -2, 0);
594 if (do_light) glEnable(GL_LIGHTING);
595 if (do_texture) glEnable(GL_TEXTURE_2D);
601 draw_planet (ModeInfo * mi)
603 planetstruct *gp = &planets[MI_SCREEN(mi)];
604 Display *display = MI_DISPLAY(mi);
605 Window window = MI_WINDOW(mi);
608 if (!gp->glx_context)
611 glDrawBuffer(GL_BACK);
612 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
614 glXMakeCurrent (display, window, *(gp->glx_context));
621 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
622 glTranslatef((x - 0.5) * 15,
626 gltrackball_rotate (gp->trackball);
628 glRotatef (90,1,0,0);
632 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
633 glRotatef (x * 360, 1.0, 0.0, 0.0);
634 glRotatef (y * 360, 0.0, 1.0, 0.0);
637 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
639 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
640 if (do_rotate && !gp->button_down_p)
642 gp->z -= 0.005; /* the sun sets in the west */
643 if (gp->z < 0) gp->z += 1;
646 glCallList (gp->platelist);
647 if (gp->button_down_p)
648 glCallList (gp->latlonglist);
651 if (mi->fps_p) do_fps (mi);
653 glXSwapBuffers(display, window);
658 release_planet (ModeInfo * mi)
660 if (planets != NULL) {
663 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
664 planetstruct *gp = &planets[screen];
666 if (gp->glx_context) {
667 /* Display lists MUST be freed while their glXContext is current. */
668 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
670 if (glIsList(gp->platelist))
671 glDeleteLists(gp->platelist, 1);
672 if (glIsList(gp->starlist))
673 glDeleteLists(gp->starlist, 1);
676 (void) free((void *) planets);