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 DEFAULTS "*delay: 20000 \n" \
40 "*showFPS: False \n" \
41 "*wireframe: False \n" \
42 "*imageForeground: Green \n" \
43 "*imageBackground: Blue \n"
44 # define refresh_planet 0
45 # include "xlockmore.h" /* from the xscreensaver distribution */
46 #else /* !STANDALONE */
47 # include "xlock.h" /* from the xlockmore distribution */
48 #endif /* !STANDALONE */
50 #ifdef USE_GL /* whole file */
56 # include <X11/Xmu/Drawing.h>
58 # include <Xmu/Drawing.h>
62 #define DEF_ROTATE "True"
63 #define DEF_ROLL "True"
64 #define DEF_WANDER "True"
65 #define DEF_SPIN "0.03"
66 #define DEF_TEXTURE "True"
67 #define DEF_STARS "True"
68 #define DEF_LIGHT "True"
69 #define DEF_RESOLUTION "128"
70 #define DEF_IMAGE "BUILTIN"
73 #define countof(x) (sizeof((x))/sizeof((*x)))
78 static int do_texture;
81 static char *which_image;
82 static int resolution;
83 static float star_spin;
85 static XrmOptionDescRec opts[] = {
86 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, "true" },
87 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, "false" },
88 {"-roll", ".glplanet.roll", XrmoptionNoArg, "true" },
89 {"+roll", ".glplanet.roll", XrmoptionNoArg, "false" },
90 {"-wander", ".glplanet.wander", XrmoptionNoArg, "true" },
91 {"+wander", ".glplanet.wander", XrmoptionNoArg, "false" },
92 {"-texture", ".glplanet.texture", XrmoptionNoArg, "true" },
93 {"+texture", ".glplanet.texture", XrmoptionNoArg, "false" },
94 {"-stars", ".glplanet.stars", XrmoptionNoArg, "true" },
95 {"+stars", ".glplanet.stars", XrmoptionNoArg, "false" },
96 {"-spin", ".glplanet.spin", XrmoptionSepArg, 0 },
97 {"-light", ".glplanet.light", XrmoptionNoArg, "true" },
98 {"+light", ".glplanet.light", XrmoptionNoArg, "false" },
99 {"-image", ".glplanet.image", XrmoptionSepArg, 0 },
100 {"-resolution", ".glplanet.resolution", XrmoptionSepArg, 0 },
103 static argtype vars[] = {
104 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
105 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
106 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
107 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
108 {&do_stars, "stars", "Stars", DEF_STARS, t_Bool},
109 {&do_light, "light", "Light", DEF_LIGHT, t_Bool},
110 {&which_image, "image", "Image", DEF_IMAGE, t_String},
111 {&resolution, "resolution","Resolution", DEF_RESOLUTION, t_Int},
112 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
115 ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
118 ModStruct planet_description =
119 {"planet", "init_planet", "draw_planet", "release_planet",
120 "draw_planet", "init_planet", NULL, &planet_opts,
121 1000, 1, 2, 1, 4, 1.0, "",
122 "Animates texture mapped sphere (planet)", 0, NULL};
126 __extension__ /* don't warn about "string length is greater than the length
127 ISO C89 compilers are required to support" when including
128 the following XPM file... */
130 #include "../images/earth.xpm"
132 #include "xpm-ximage.h"
134 #include "gltrackball.h"
138 * slices and stacks are used in the sphere parameterization routine.
139 * more slices and stacks will increase the quality of the sphere,
140 * at the expense of rendering speed
143 #define NUM_STARS 1000
145 /* radius of the sphere- fairly arbitrary */
148 /* distance away from the sphere model */
153 /* structure for holding the planet data */
158 int screen_width, screen_height;
159 GLXContext *glx_context;
165 trackball_state *trackball;
171 static planetstruct *planets = NULL;
174 /* Set up and enable texturing on our object */
176 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
178 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
179 MI_COLORMAP (mi), xpm_data);
182 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
183 image->width, image->height, 0,
185 /* GL_UNSIGNED_BYTE, */
186 GL_UNSIGNED_INT_8_8_8_8_REV,
188 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
191 /* setup parameters for texturing */
192 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
193 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
194 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
195 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
196 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
197 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
202 setup_file_texture (ModeInfo *mi, char *filename)
204 Display *dpy = mi->dpy;
205 Visual *visual = mi->xgwa.visual;
208 Colormap cmap = mi->xgwa.colormap;
209 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
212 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
213 image->width, image->height, 0,
215 /* GL_UNSIGNED_BYTE, */
216 GL_UNSIGNED_INT_8_8_8_8_REV,
218 sprintf (buf, "texture: %.100s (%dx%d)",
219 filename, image->width, image->height);
222 /* setup parameters for texturing */
223 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
224 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
226 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
227 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
228 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
229 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
230 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
235 setup_texture(ModeInfo * mi)
237 /* planetstruct *gp = &planets[MI_SCREEN(mi)];*/
239 glEnable(GL_TEXTURE_2D);
243 !strcmp(which_image, "BUILTIN"))
244 setup_xpm_texture (mi, earth_xpm);
246 setup_file_texture (mi, which_image);
248 check_gl_error("texture initialization");
250 /* Need to flip the texture top for bottom for some reason. */
251 glMatrixMode (GL_TEXTURE);
253 glMatrixMode (GL_MODELVIEW);
258 init_stars (ModeInfo *mi)
260 planetstruct *gp = &planets[MI_SCREEN(mi)];
262 int width = MI_WIDTH(mi);
263 int height = MI_HEIGHT(mi);
264 int size = (width > height ? width : height);
265 int nstars = size * size / 320;
268 int steps = max_size / inc;
270 gp->starlist = glGenLists(1);
271 glNewList(gp->starlist, GL_COMPILE);
273 glEnable(GL_POINT_SMOOTH);
275 for (j = 1; j <= steps; j++)
277 glPointSize(inc * j);
279 for (i = 0; i < nstars / steps; i++)
281 glColor3f (0.6 + frand(0.3),
284 glVertex2f (2 * size * (0.5 - frand(1.0)),
285 2 * size * (0.5 - frand(1.0)));
291 check_gl_error("stars initialization");
296 draw_stars (ModeInfo *mi)
298 planetstruct *gp = &planets[MI_SCREEN(mi)];
300 glDisable(GL_TEXTURE_2D);
301 glDisable(GL_LIGHTING);
302 glDisable(GL_DEPTH_TEST);
304 glMatrixMode (GL_PROJECTION);
309 glMatrixMode (GL_MODELVIEW);
313 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
314 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
316 glRotatef (gp->star_theta, 0.0, 0.0, 1.0);
317 glCallList (gp->starlist);
321 glMatrixMode (GL_PROJECTION);
324 glMatrixMode (GL_MODELVIEW);
329 /* Set up lighting */
331 init_sun (ModeInfo * mi)
333 planetstruct *gp = &planets[MI_SCREEN(mi)];
335 GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
336 GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
337 GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
339 GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
340 GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
341 GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
345 double h = 0.1 + frand(0.8); /* east-west position - screen-side. */
346 double v = -0.3 + frand(0.6); /* north-south position */
348 if (h > 0.3 && h < 0.8) /* avoid having the sun at the camera */
349 h += (h > 0.5 ? 0.2 : -0.2);
351 gp->sunpos[0] = cos(h * M_PI);
352 gp->sunpos[1] = sin(h * M_PI);
353 gp->sunpos[2] = sin(v * M_PI);
354 gp->sunpos[3] = 0.00;
357 glEnable(GL_LIGHTING);
360 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
361 glLightfv (GL_LIGHT0, GL_AMBIENT, lamb);
362 glLightfv (GL_LIGHT0, GL_DIFFUSE, ldif);
363 glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
365 check_gl_error("sun");
366 glMaterialfv (GL_FRONT, GL_AMBIENT, mamb);
367 glMaterialfv (GL_FRONT, GL_DIFFUSE, mdif);
368 glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
369 glMaterialf (GL_FRONT, GL_SHININESS, shiny);
372 /* glEnable(GL_BLEND);*/
373 /* glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);*/
374 glShadeModel(GL_SMOOTH);
376 check_gl_error("lighting");
380 #define RANDSIGN() ((random() & 1) ? 1 : -1)
383 reshape_planet (ModeInfo *mi, int width, int height)
385 GLfloat h = (GLfloat) height / (GLfloat) width;
387 glViewport(0, 0, (GLint) width, (GLint) height);
388 glMatrixMode(GL_PROJECTION);
390 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
391 glMatrixMode(GL_MODELVIEW);
393 glTranslatef(0.0, 0.0, -DIST);
395 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
400 planet_handle_event (ModeInfo *mi, XEvent *event)
402 planetstruct *gp = &planets[MI_SCREEN(mi)];
404 if (event->xany.type == ButtonPress &&
405 event->xbutton.button == Button1)
407 gp->button_down_p = True;
408 gltrackball_start (gp->trackball,
409 event->xbutton.x, event->xbutton.y,
410 MI_WIDTH (mi), MI_HEIGHT (mi));
413 else if (event->xany.type == ButtonRelease &&
414 event->xbutton.button == Button1)
416 gp->button_down_p = False;
419 else if (event->xany.type == ButtonPress &&
420 (event->xbutton.button == Button4 ||
421 event->xbutton.button == Button5 ||
422 event->xbutton.button == Button6 ||
423 event->xbutton.button == Button7))
425 gltrackball_mousewheel (gp->trackball, event->xbutton.button, 10,
426 !!event->xbutton.state);
429 else if (event->xany.type == MotionNotify &&
432 gltrackball_track (gp->trackball,
433 event->xmotion.x, event->xmotion.y,
434 MI_WIDTH (mi), MI_HEIGHT (mi));
443 init_planet (ModeInfo * mi)
446 int screen = MI_SCREEN(mi);
447 Bool wire = MI_IS_WIREFRAME(mi);
449 if (planets == NULL) {
450 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
451 sizeof (planetstruct))) == NULL)
454 gp = &planets[screen];
456 if ((gp->glx_context = init_GL(mi)) != NULL) {
457 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
461 char *f = get_string_resource(mi->dpy, "imageForeground", "Foreground");
462 char *b = get_string_resource(mi->dpy, "imageBackground", "Background");
464 if (!f) f = strdup("white");
465 if (!b) b = strdup("black");
467 for (s = f + strlen(f)-1; s > f; s--)
468 if (*s == ' ' || *s == '\t')
470 for (s = b + strlen(b)-1; s > b; s--)
471 if (*s == ' ' || *s == '\t')
474 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
476 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
479 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
481 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
490 double spin_speed = 0.5;
491 double wander_speed = 0.02;
492 gp->rot = make_rotator (do_roll ? spin_speed : 0,
493 do_roll ? spin_speed : 0,
495 do_wander ? wander_speed : 0,
498 gp->trackball = gltrackball_init ();
517 star_spin = -star_spin;
519 /* construct the polygons of the planet
521 gp->platelist = glGenLists(1);
522 glNewList (gp->platelist, GL_COMPILE);
525 glScalef (RADIUS, RADIUS, RADIUS);
526 glRotatef (90, 1, 0, 0);
528 unit_sphere (resolution, resolution, wire);
532 /* construct the polygons of the latitude/longitude/axis lines.
534 gp->latlonglist = glGenLists(1);
535 glNewList (gp->latlonglist, GL_COMPILE);
537 glDisable (GL_TEXTURE_2D);
538 glDisable (GL_LIGHTING);
539 glDisable (GL_LINE_SMOOTH);
540 glColor3f (0.1, 0.3, 0.1);
541 glScalef (RADIUS, RADIUS, RADIUS);
542 glScalef (1.01, 1.01, 1.01);
543 glRotatef (90, 1, 0, 0);
544 unit_sphere (12, 24, 1);
546 glVertex3f(0, -2, 0);
554 draw_planet (ModeInfo * mi)
556 planetstruct *gp = &planets[MI_SCREEN(mi)];
557 Display *display = MI_DISPLAY(mi);
558 Window window = MI_WINDOW(mi);
561 if (!gp->glx_context)
564 glDrawBuffer(GL_BACK);
565 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
567 glXMakeCurrent (display, window, *(gp->glx_context));
569 mi->polygon_count = 0;
574 mi->polygon_count += NUM_STARS;
577 if (do_light) glEnable(GL_LIGHTING);
578 if (do_texture) glEnable(GL_TEXTURE_2D);
580 glEnable (GL_LINE_SMOOTH);
581 glEnable(GL_DEPTH_TEST);
582 glEnable(GL_CULL_FACE);
587 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
588 glTranslatef((x - 0.5) * 15,
592 /* Do it twice because we don't track the device's orientation. */
593 glRotatef( current_device_rotation(), 0, 0, 1);
594 gltrackball_rotate (gp->trackball);
595 glRotatef(-current_device_rotation(), 0, 0, 1);
597 glRotatef (90,1,0,0);
601 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
602 glRotatef (x * 360, 1.0, 0.0, 0.0);
603 glRotatef (y * 360, 0.0, 1.0, 0.0);
606 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
608 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
609 if (do_rotate && !gp->button_down_p)
611 gp->z -= 0.005; /* the sun sets in the west */
612 if (gp->z < 0) gp->z += 1;
615 glCallList (gp->platelist);
616 mi->polygon_count += resolution*resolution;
618 if (gp->button_down_p)
620 glCallList (gp->latlonglist);
621 mi->polygon_count += 24*24;
625 if (mi->fps_p) do_fps (mi);
627 glXSwapBuffers(display, window);
629 gp->star_theta += star_spin;
634 release_planet (ModeInfo * mi)
636 if (planets != NULL) {
639 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
640 planetstruct *gp = &planets[screen];
642 if (gp->glx_context) {
643 /* Display lists MUST be freed while their glXContext is current. */
644 /* but this gets a BadMatch error. -jwz */
645 /*glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));*/
647 if (glIsList(gp->platelist))
648 glDeleteLists(gp->platelist, 1);
649 if (glIsList(gp->starlist))
650 glDeleteLists(gp->starlist, 1);
653 (void) free((void *) planets);
660 XSCREENSAVER_MODULE_2 ("GLPlanet", glplanet, planet)