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" \
48 "*wireframe: False \n" \
49 "*imageForeground: Green \n" \
50 "*imageBackground: Blue \n"
52 # include "xlockmore.h" /* from the xscreensaver distribution */
53 #else /* !STANDALONE */
54 # include "xlock.h" /* from the xlockmore distribution */
55 #endif /* !STANDALONE */
57 #ifdef USE_GL /* whole file */
63 # include <X11/Xmu/Drawing.h>
65 # include <Xmu/Drawing.h>
72 #define DEF_ROTATE "True"
73 #define DEF_ROLL "True"
74 #define DEF_WANDER "True"
75 #define DEF_TEXTURE "True"
76 #define DEF_STARS "True"
77 #define DEF_LIGHT "True"
78 #define DEF_RESOLUTION "128"
79 #define DEF_IMAGE "BUILTIN"
82 #define countof(x) (sizeof((x))/sizeof((*x)))
87 static int do_texture;
90 static char *which_image;
91 static int resolution;
93 static XrmOptionDescRec opts[] = {
94 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, "true" },
95 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, "false" },
96 {"-roll", ".glplanet.roll", XrmoptionNoArg, "true" },
97 {"+roll", ".glplanet.roll", XrmoptionNoArg, "false" },
98 {"-wander", ".glplanet.wander", XrmoptionNoArg, "true" },
99 {"+wander", ".glplanet.wander", XrmoptionNoArg, "false" },
100 {"-texture", ".glplanet.texture", XrmoptionNoArg, "true" },
101 {"+texture", ".glplanet.texture", XrmoptionNoArg, "false" },
102 {"-stars", ".glplanet.stars", XrmoptionNoArg, "true" },
103 {"+stars", ".glplanet.stars", XrmoptionNoArg, "false" },
104 {"-light", ".glplanet.light", XrmoptionNoArg, "true" },
105 {"+light", ".glplanet.light", XrmoptionNoArg, "false" },
106 {"-image", ".glplanet.image", XrmoptionSepArg, 0 },
107 {"-resolution", ".glplanet.resolution", XrmoptionSepArg, 0 },
110 static argtype vars[] = {
111 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
112 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
113 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
114 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
115 {&do_stars, "stars", "Stars", DEF_STARS, t_Bool},
116 {&do_light, "light", "Light", DEF_LIGHT, t_Bool},
117 {&which_image, "image", "Image", DEF_IMAGE, t_String},
118 {&resolution, "resolution","Resolution", DEF_RESOLUTION, t_Int},
121 ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
124 ModStruct planet_description =
125 {"planet", "init_planet", "draw_planet", "release_planet",
126 "draw_planet", "init_planet", NULL, &planet_opts,
127 1000, 1, 2, 1, 4, 1.0, "",
128 "Animates texture mapped sphere (planet)", 0, NULL};
132 __extension__ /* don't warn about "string length is greater than the length
133 ISO C89 compilers are required to support" when including
134 the following XPM file... */
136 #include "../images/earth.xpm"
138 #include "xpm-ximage.h"
140 #include "gltrackball.h"
144 * slices and stacks are used in the sphere parameterization routine.
145 * more slices and stacks will increase the quality of the sphere,
146 * at the expense of rendering speed
149 #define NUM_STARS 1000
151 /* radius of the sphere- fairly arbitrary */
154 /* distance away from the sphere model */
159 /* structure for holding the planet data */
164 int screen_width, screen_height;
165 GLXContext *glx_context;
171 trackball_state *trackball;
176 static planetstruct *planets = NULL;
179 /* Set up and enable texturing on our object */
181 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
183 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
184 MI_COLORMAP (mi), xpm_data);
187 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
188 image->width, image->height, 0,
190 /* GL_UNSIGNED_BYTE, */
191 GL_UNSIGNED_INT_8_8_8_8_REV,
193 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
196 /* setup parameters for texturing */
197 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
198 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
199 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
200 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
201 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
202 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
207 setup_file_texture (ModeInfo *mi, char *filename)
209 Display *dpy = mi->dpy;
210 Visual *visual = mi->xgwa.visual;
213 Colormap cmap = mi->xgwa.colormap;
214 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
217 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
218 image->width, image->height, 0,
220 /* GL_UNSIGNED_BYTE, */
221 GL_UNSIGNED_INT_8_8_8_8_REV,
223 sprintf (buf, "texture: %.100s (%dx%d)",
224 filename, image->width, image->height);
227 /* setup parameters for texturing */
228 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
229 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
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_texture(ModeInfo * mi)
242 /* planetstruct *gp = &planets[MI_SCREEN(mi)];*/
244 glEnable(GL_TEXTURE_2D);
248 !strcmp(which_image, "BUILTIN"))
249 setup_xpm_texture (mi, earth_xpm);
251 setup_file_texture (mi, which_image);
253 check_gl_error("texture initialization");
255 /* Need to flip the texture top for bottom for some reason. */
256 glMatrixMode (GL_TEXTURE);
258 glMatrixMode (GL_MODELVIEW);
263 init_stars (ModeInfo *mi)
266 GLfloat max_size = 3;
268 int steps = max_size / inc;
269 int width = MI_WIDTH(mi);
270 int height = MI_HEIGHT(mi);
272 planetstruct *gp = &planets[MI_SCREEN(mi)];
273 Bool wire = MI_IS_WIREFRAME(mi);
276 glEnable (GL_POINT_SMOOTH);
278 gp->starlist = glGenLists(1);
279 glNewList(gp->starlist, GL_COMPILE);
281 glScalef (1.0/width, 1.0/height, 1);
283 for (j = 1; j <= steps; j++)
285 glPointSize(inc * j);
287 for (i = 0 ; i < NUM_STARS / steps; i++)
289 glColor3f (0.6 + frand(0.3),
292 glVertex2f ((GLfloat) (random() % width),
293 (GLfloat) (random() % height));
299 check_gl_error("stars initialization");
304 draw_stars (ModeInfo * mi)
306 int width = MI_WIDTH(mi);
307 int height = MI_HEIGHT(mi);
309 planetstruct *gp = &planets[MI_SCREEN(mi)];
311 /* Sadly, this causes a stall of the graphics pipeline (as would the
312 equivalent calls to glGet*.) But there's no way around this, short
313 of having each caller set up the specific display matrix we need
314 here, which would kind of defeat the purpose of centralizing this
317 glPushAttrib(GL_TRANSFORM_BIT | /* for matrix contents */
318 GL_ENABLE_BIT | /* for various glDisable calls */
319 GL_CURRENT_BIT | /* for glColor3f() */
320 GL_LIST_BIT); /* for glListBase() */
322 check_gl_error ("glPushAttrib");
324 /* disable lighting and texturing when drawing stars!
325 (glPopAttrib() restores these.)
327 glDisable(GL_TEXTURE_2D);
328 glDisable(GL_LIGHTING);
329 glDisable(GL_DEPTH_TEST);
331 /* glPopAttrib() does not restore matrix changes, so we must
332 push/pop the matrix stacks to be non-intrusive there.
334 glMatrixMode(GL_PROJECTION);
337 check_gl_error ("glPushMatrix");
340 /* Each matrix mode has its own stack, so we need to push/pop
342 glMatrixMode(GL_MODELVIEW);
345 check_gl_error ("glPushMatrix");
348 gluOrtho2D (0, width, 0, height);
349 check_gl_error ("gluOrtho2D");
352 glScalef (width, height, 1);
353 glCallList(gp->starlist);
354 check_gl_error ("drawing stars");
358 glMatrixMode(GL_PROJECTION);
362 /* clean up after our state changes */
364 check_gl_error ("glPopAttrib");
369 /* Set up lighting */
371 init_sun (ModeInfo * mi)
373 planetstruct *gp = &planets[MI_SCREEN(mi)];
375 GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
376 GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
377 GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
379 GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
380 GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
381 GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
385 double h = 0.1 + frand(0.8); /* east-west position - screen-side. */
386 double v = -0.3 + frand(0.6); /* north-south position */
388 if (h > 0.3 && h < 0.8) /* avoid having the sun at the camera */
389 h += (h > 0.5 ? 0.2 : -0.2);
391 gp->sunpos[0] = cos(h * M_PI);
392 gp->sunpos[1] = sin(h * M_PI);
393 gp->sunpos[2] = sin(v * M_PI);
394 gp->sunpos[3] = 0.00;
397 glEnable(GL_LIGHTING);
400 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
401 glLightfv (GL_LIGHT0, GL_AMBIENT, lamb);
402 glLightfv (GL_LIGHT0, GL_DIFFUSE, ldif);
403 glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
405 glMaterialfv (GL_FRONT, GL_AMBIENT, mamb);
406 glMaterialfv (GL_FRONT, GL_DIFFUSE, mdif);
407 glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
408 glMaterialf (GL_FRONT, GL_SHININESS, shiny);
412 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
413 glShadeModel(GL_SMOOTH);
415 check_gl_error("lighting");
419 #define RANDSIGN() ((random() & 1) ? 1 : -1)
422 reshape_planet (ModeInfo *mi, int width, int height)
424 GLfloat h = (GLfloat) height / (GLfloat) width;
426 glViewport(0, 0, (GLint) width, (GLint) height);
427 glMatrixMode(GL_PROJECTION);
429 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
430 glMatrixMode(GL_MODELVIEW);
432 glTranslatef(0.0, 0.0, -DIST);
434 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
439 planet_handle_event (ModeInfo *mi, XEvent *event)
441 planetstruct *gp = &planets[MI_SCREEN(mi)];
443 if (event->xany.type == ButtonPress &&
444 event->xbutton.button == Button1)
446 gp->button_down_p = True;
447 gltrackball_start (gp->trackball,
448 event->xbutton.x, event->xbutton.y,
449 MI_WIDTH (mi), MI_HEIGHT (mi));
452 else if (event->xany.type == ButtonRelease &&
453 event->xbutton.button == Button1)
455 gp->button_down_p = False;
458 else if (event->xany.type == ButtonPress &&
459 (event->xbutton.button == Button4 ||
460 event->xbutton.button == Button5))
462 gltrackball_mousewheel (gp->trackball, event->xbutton.button, 10,
463 !!event->xbutton.state);
466 else if (event->xany.type == MotionNotify &&
469 gltrackball_track (gp->trackball,
470 event->xmotion.x, event->xmotion.y,
471 MI_WIDTH (mi), MI_HEIGHT (mi));
480 init_planet (ModeInfo * mi)
483 int screen = MI_SCREEN(mi);
484 Bool wire = MI_IS_WIREFRAME(mi);
486 if (planets == NULL) {
487 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
488 sizeof (planetstruct))) == NULL)
491 gp = &planets[screen];
493 if ((gp->glx_context = init_GL(mi)) != NULL) {
494 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
498 char *f = get_string_resource("imageForeground", "Foreground");
499 char *b = get_string_resource("imageBackground", "Background");
501 if (!f) f = strdup("white");
502 if (!b) b = strdup("black");
504 for (s = f + strlen(f)-1; s > f; s--)
505 if (*s == ' ' || *s == '\t')
507 for (s = b + strlen(b)-1; s > b; s--)
508 if (*s == ' ' || *s == '\t')
511 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
513 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
516 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
518 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
527 double spin_speed = 0.5;
528 double wander_speed = 0.02;
529 gp->rot = make_rotator (do_roll ? spin_speed : 0,
530 do_roll ? spin_speed : 0,
532 do_wander ? wander_speed : 0,
535 gp->trackball = gltrackball_init ();
542 glEnable (GL_LINE_SMOOTH);
554 glEnable(GL_DEPTH_TEST);
555 glEnable(GL_CULL_FACE);
558 /* construct the polygons of the planet
560 gp->platelist = glGenLists(1);
561 glNewList (gp->platelist, GL_COMPILE);
564 glScalef (RADIUS, RADIUS, RADIUS);
565 glRotatef (90, 1, 0, 0);
566 unit_sphere (resolution, resolution, wire);
567 mi->polygon_count += resolution*resolution;
571 /* construct the polygons of the latitude/longitude/axis lines.
573 gp->latlonglist = glGenLists(1);
574 glNewList (gp->latlonglist, GL_COMPILE);
576 if (do_texture) glDisable (GL_TEXTURE_2D);
577 if (do_light) glDisable (GL_LIGHTING);
578 glColor3f (0.1, 0.3, 0.1);
579 glScalef (RADIUS, RADIUS, RADIUS);
580 glScalef (1.01, 1.01, 1.01);
581 glRotatef (90, 1, 0, 0);
582 unit_sphere (12, 24, 1);
584 glVertex3f(0, -2, 0);
587 if (do_light) glEnable(GL_LIGHTING);
588 if (do_texture) glEnable(GL_TEXTURE_2D);
594 draw_planet (ModeInfo * mi)
596 planetstruct *gp = &planets[MI_SCREEN(mi)];
597 Display *display = MI_DISPLAY(mi);
598 Window window = MI_WINDOW(mi);
601 if (!gp->glx_context)
604 glDrawBuffer(GL_BACK);
605 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
607 glXMakeCurrent (display, window, *(gp->glx_context));
614 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
615 glTranslatef((x - 0.5) * 15,
619 gltrackball_rotate (gp->trackball);
621 glRotatef (90,1,0,0);
625 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
626 glRotatef (x * 360, 1.0, 0.0, 0.0);
627 glRotatef (y * 360, 0.0, 1.0, 0.0);
630 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
632 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
633 if (do_rotate && !gp->button_down_p)
635 gp->z -= 0.005; /* the sun sets in the west */
636 if (gp->z < 0) gp->z += 1;
639 glCallList (gp->platelist);
640 if (gp->button_down_p)
641 glCallList (gp->latlonglist);
644 if (mi->fps_p) do_fps (mi);
646 glXSwapBuffers(display, window);
651 release_planet (ModeInfo * mi)
653 if (planets != NULL) {
656 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
657 planetstruct *gp = &planets[screen];
659 if (gp->glx_context) {
660 /* Display lists MUST be freed while their glXContext is current. */
661 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
663 if (glIsList(gp->platelist))
664 glDeleteLists(gp->platelist, 1);
665 if (glIsList(gp->starlist))
666 glDeleteLists(gp->starlist, 1);
669 (void) free((void *) planets);