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;
187 normalize(GLfloat v[3])
189 GLfloat d = (GLfloat) sqrt((double) (v[0] * v[0] +
200 v[0] = v[1] = v[2] = 0;
205 /* Set up and enable texturing on our object */
207 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
209 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
210 MI_COLORMAP (mi), xpm_data);
213 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
214 image->width, image->height, 0,
216 /* GL_UNSIGNED_BYTE, */
217 GL_UNSIGNED_INT_8_8_8_8_REV,
219 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
222 /* setup parameters for texturing */
223 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
224 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
225 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
226 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
227 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
228 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
233 setup_file_texture (ModeInfo *mi, char *filename)
235 Display *dpy = mi->dpy;
236 Visual *visual = mi->xgwa.visual;
239 Colormap cmap = mi->xgwa.colormap;
240 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
243 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
244 image->width, image->height, 0,
246 /* GL_UNSIGNED_BYTE, */
247 GL_UNSIGNED_INT_8_8_8_8_REV,
249 sprintf (buf, "texture: %.100s (%dx%d)",
250 filename, image->width, image->height);
253 /* setup parameters for texturing */
254 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
255 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
257 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
258 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
259 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
260 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
261 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
266 setup_texture(ModeInfo * mi)
268 /* planetstruct *gp = &planets[MI_SCREEN(mi)];*/
270 glEnable(GL_TEXTURE_2D);
274 !strcmp(which_image, "BUILTIN"))
275 setup_xpm_texture (mi, earth_xpm);
277 setup_file_texture (mi, which_image);
279 check_gl_error("texture initialization");
281 /* Need to flip the texture top for bottom for some reason. */
282 glMatrixMode (GL_TEXTURE);
284 glMatrixMode (GL_MODELVIEW);
289 init_stars (ModeInfo *mi)
292 GLfloat max_size = 3;
294 int steps = max_size / inc;
295 int width = MI_WIDTH(mi);
296 int height = MI_HEIGHT(mi);
298 planetstruct *gp = &planets[MI_SCREEN(mi)];
299 Bool wire = MI_IS_WIREFRAME(mi);
302 glEnable (GL_POINT_SMOOTH);
304 gp->starlist = glGenLists(1);
305 glNewList(gp->starlist, GL_COMPILE);
307 glScalef (1.0/width, 1.0/height, 1);
309 for (j = 1; j <= steps; j++)
311 glPointSize(inc * j);
313 for (i = 0 ; i < NUM_STARS / steps; i++)
315 glColor3f (0.6 + frand(0.3),
318 glVertex2f ((GLfloat) (random() % width),
319 (GLfloat) (random() % height));
325 check_gl_error("stars initialization");
330 draw_stars (ModeInfo * mi)
332 int width = MI_WIDTH(mi);
333 int height = MI_HEIGHT(mi);
335 planetstruct *gp = &planets[MI_SCREEN(mi)];
337 /* Sadly, this causes a stall of the graphics pipeline (as would the
338 equivalent calls to glGet*.) But there's no way around this, short
339 of having each caller set up the specific display matrix we need
340 here, which would kind of defeat the purpose of centralizing this
343 glPushAttrib(GL_TRANSFORM_BIT | /* for matrix contents */
344 GL_ENABLE_BIT | /* for various glDisable calls */
345 GL_CURRENT_BIT | /* for glColor3f() */
346 GL_LIST_BIT); /* for glListBase() */
348 check_gl_error ("glPushAttrib");
350 /* disable lighting and texturing when drawing stars!
351 (glPopAttrib() restores these.)
353 glDisable(GL_TEXTURE_2D);
354 glDisable(GL_LIGHTING);
355 glDisable(GL_DEPTH_TEST);
357 /* glPopAttrib() does not restore matrix changes, so we must
358 push/pop the matrix stacks to be non-intrusive there.
360 glMatrixMode(GL_PROJECTION);
363 check_gl_error ("glPushMatrix");
366 /* Each matrix mode has its own stack, so we need to push/pop
368 glMatrixMode(GL_MODELVIEW);
371 check_gl_error ("glPushMatrix");
374 gluOrtho2D (0, width, 0, height);
375 check_gl_error ("gluOrtho2D");
378 glScalef (width, height, 1);
379 glCallList(gp->starlist);
380 check_gl_error ("drawing stars");
384 glMatrixMode(GL_PROJECTION);
388 /* clean up after our state changes */
390 check_gl_error ("glPopAttrib");
395 /* Set up lighting */
397 init_sun (ModeInfo * mi)
399 planetstruct *gp = &planets[MI_SCREEN(mi)];
401 GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
402 GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
403 GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
405 GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
406 GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
407 GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
411 double h = 0.1 + frand(0.8); /* east-west position - screen-side. */
412 double v = -0.3 + frand(0.6); /* north-south position */
414 if (h > 0.3 && h < 0.8) /* avoid having the sun at the camera */
415 h += (h > 0.5 ? 0.2 : -0.2);
417 gp->sunpos[0] = cos(h * M_PI);
418 gp->sunpos[1] = sin(h * M_PI);
419 gp->sunpos[2] = sin(v * M_PI);
420 gp->sunpos[3] = 0.00;
423 glEnable(GL_LIGHTING);
426 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
427 glLightfv (GL_LIGHT0, GL_AMBIENT, lamb);
428 glLightfv (GL_LIGHT0, GL_DIFFUSE, ldif);
429 glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
431 glMaterialfv (GL_FRONT, GL_AMBIENT, mamb);
432 glMaterialfv (GL_FRONT, GL_DIFFUSE, mdif);
433 glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
434 glMaterialf (GL_FRONT, GL_SHININESS, shiny);
438 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
439 glShadeModel(GL_SMOOTH);
441 check_gl_error("lighting");
445 #define RANDSIGN() ((random() & 1) ? 1 : -1)
448 reshape_planet (ModeInfo *mi, int width, int height)
450 GLfloat h = (GLfloat) height / (GLfloat) width;
452 glViewport(0, 0, (GLint) width, (GLint) height);
453 glMatrixMode(GL_PROJECTION);
455 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
456 glMatrixMode(GL_MODELVIEW);
458 glTranslatef(0.0, 0.0, -DIST);
460 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
465 planet_handle_event (ModeInfo *mi, XEvent *event)
467 planetstruct *gp = &planets[MI_SCREEN(mi)];
469 if (event->xany.type == ButtonPress &&
470 event->xbutton.button & Button1)
472 gp->button_down_p = True;
473 gltrackball_start (gp->trackball,
474 event->xbutton.x, event->xbutton.y,
475 MI_WIDTH (mi), MI_HEIGHT (mi));
478 else if (event->xany.type == ButtonRelease &&
479 event->xbutton.button & Button1)
481 gp->button_down_p = False;
484 else if (event->xany.type == MotionNotify &&
487 gltrackball_track (gp->trackball,
488 event->xmotion.x, event->xmotion.y,
489 MI_WIDTH (mi), MI_HEIGHT (mi));
498 init_planet (ModeInfo * mi)
501 int screen = MI_SCREEN(mi);
502 Bool wire = MI_IS_WIREFRAME(mi);
504 if (planets == NULL) {
505 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
506 sizeof (planetstruct))) == NULL)
509 gp = &planets[screen];
511 if ((gp->glx_context = init_GL(mi)) != NULL) {
512 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
516 char *f = get_string_resource("imageForeground", "Foreground");
517 char *b = get_string_resource("imageBackground", "Background");
519 if (!f) f = strdup("white");
520 if (!b) b = strdup("black");
522 for (s = f + strlen(f)-1; s > f; s--)
523 if (*s == ' ' || *s == '\t')
525 for (s = b + strlen(b)-1; s > b; s--)
526 if (*s == ' ' || *s == '\t')
529 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
531 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
534 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
536 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
545 double spin_speed = 0.5;
546 double wander_speed = 0.02;
547 gp->rot = make_rotator (do_roll ? spin_speed : 0,
548 do_roll ? spin_speed : 0,
550 do_wander ? wander_speed : 0,
553 gp->trackball = gltrackball_init ();
560 glEnable (GL_LINE_SMOOTH);
572 glEnable(GL_DEPTH_TEST);
573 glEnable(GL_CULL_FACE);
576 /* construct the polygons of the planet
578 gp->platelist = glGenLists(1);
579 glNewList (gp->platelist, GL_COMPILE);
582 glScalef (RADIUS, RADIUS, RADIUS);
583 glRotatef (90, 1, 0, 0);
584 unit_sphere (resolution, resolution, wire);
585 mi->polygon_count += resolution*resolution;
589 /* construct the polygons of the latitude/longitude/axis lines.
591 gp->latlonglist = glGenLists(1);
592 glNewList (gp->latlonglist, GL_COMPILE);
594 if (do_texture) glDisable (GL_TEXTURE_2D);
595 if (do_light) glDisable (GL_LIGHTING);
596 glColor3f (0.1, 0.3, 0.1);
597 glScalef (RADIUS, RADIUS, RADIUS);
598 glScalef (1.01, 1.01, 1.01);
599 glRotatef (90, 1, 0, 0);
600 unit_sphere (12, 24, 1);
602 glVertex3f(0, -2, 0);
605 if (do_light) glEnable(GL_LIGHTING);
606 if (do_texture) glEnable(GL_TEXTURE_2D);
612 draw_planet (ModeInfo * mi)
614 planetstruct *gp = &planets[MI_SCREEN(mi)];
615 Display *display = MI_DISPLAY(mi);
616 Window window = MI_WINDOW(mi);
619 if (!gp->glx_context)
622 glDrawBuffer(GL_BACK);
623 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
625 glXMakeCurrent (display, window, *(gp->glx_context));
632 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
633 glTranslatef((x - 0.5) * 15,
637 gltrackball_rotate (gp->trackball);
639 glRotatef (90,1,0,0);
643 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
644 glRotatef (x * 360, 1.0, 0.0, 0.0);
645 glRotatef (y * 360, 0.0, 1.0, 0.0);
648 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
650 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
651 if (do_rotate && !gp->button_down_p)
653 gp->z -= 0.005; /* the sun sets in the west */
654 if (gp->z < 0) gp->z += 1;
657 glCallList (gp->platelist);
658 if (gp->button_down_p)
659 glCallList (gp->latlonglist);
662 if (mi->fps_p) do_fps (mi);
664 glXSwapBuffers(display, window);
669 release_planet (ModeInfo * mi)
671 if (planets != NULL) {
674 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
675 planetstruct *gp = &planets[screen];
677 if (gp->glx_context) {
678 /* Display lists MUST be freed while their glXContext is current. */
679 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
681 if (glIsList(gp->platelist))
682 glDeleteLists(gp->platelist, 1);
683 if (glIsList(gp->starlist))
684 glDeleteLists(gp->starlist, 1);
687 (void) free((void *) planets);