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 * 10-Nov-14: jwz@jwz.org Night map. Better stars.
19 * 16-Jan-02: jwz@jwz.org gdk_pixbuf support.
20 * 21-Mar-01: jwz@jwz.org Broke sphere routine out into its own file.
22 * 9-Oct-98: dek@cgl.ucsf.edu Added stars.
24 * 8-Oct-98: jwz@jwz.org Made the 512x512x1 xearth image be built in.
25 * Made it possible to load XPM or XBM files.
26 * Made the planet bounce and roll around.
28 * 8-Oct-98: Released initial version of "glplanet"
29 * (David Konerding, dek@cgl.ucsf.edu)
34 #define DEFAULTS "*delay: 20000 \n" \
35 "*showFPS: False \n" \
36 "*wireframe: False \n" \
37 "*imageForeground: Green \n" \
38 "*imageBackground: Blue \n"
39 # define refresh_planet 0
40 # include "xlockmore.h" /* from the xscreensaver distribution */
41 #else /* !STANDALONE */
42 # include "xlock.h" /* from the xlockmore distribution */
43 #endif /* !STANDALONE */
45 #ifdef USE_GL /* whole file */
51 # include <X11/Xmu/Drawing.h>
53 # include <Xmu/Drawing.h>
57 #define DEF_ROTATE "True"
58 #define DEF_ROLL "True"
59 #define DEF_WANDER "True"
60 #define DEF_SPIN "0.03"
61 #define DEF_TEXTURE "True"
62 #define DEF_STARS "True"
63 #define DEF_RESOLUTION "128"
64 #define DEF_IMAGE "BUILTIN"
67 #define countof(x) (sizeof((x))/sizeof((*x)))
70 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
75 static int do_texture;
77 static char *which_image;
78 static int resolution;
80 static XrmOptionDescRec opts[] = {
81 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, "true" },
82 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, "false" },
83 {"-roll", ".glplanet.roll", XrmoptionNoArg, "true" },
84 {"+roll", ".glplanet.roll", XrmoptionNoArg, "false" },
85 {"-wander", ".glplanet.wander", XrmoptionNoArg, "true" },
86 {"+wander", ".glplanet.wander", XrmoptionNoArg, "false" },
87 {"-texture", ".glplanet.texture", XrmoptionNoArg, "true" },
88 {"+texture", ".glplanet.texture", XrmoptionNoArg, "false" },
89 {"-stars", ".glplanet.stars", XrmoptionNoArg, "true" },
90 {"+stars", ".glplanet.stars", XrmoptionNoArg, "false" },
91 {"-spin", ".glplanet.spin", XrmoptionSepArg, 0 },
92 {"-image", ".glplanet.image", XrmoptionSepArg, 0 },
93 {"-resolution", ".glplanet.resolution", XrmoptionSepArg, 0 },
96 static argtype vars[] = {
97 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
98 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
99 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
100 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
101 {&do_stars, "stars", "Stars", DEF_STARS, t_Bool},
102 {&which_image, "image", "Image", DEF_IMAGE, t_String},
103 {&resolution, "resolution","Resolution", DEF_RESOLUTION, t_Int},
106 ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
109 ModStruct planet_description =
110 {"planet", "init_planet", "draw_planet", "release_planet",
111 "draw_planet", "init_planet", NULL, &planet_opts,
112 1000, 1, 2, 1, 4, 1.0, "",
113 "Animates texture mapped sphere (planet)", 0, NULL};
117 __extension__ /* don't warn about "string length is greater than the length
118 ISO C89 compilers are required to support" when including
119 the following XPM file... */
121 #include "../images/earth.xpm"
122 #include "../images/earth_night.xpm"
124 #include "xpm-ximage.h"
126 #include "gltrackball.h"
130 * slices and stacks are used in the sphere parameterization routine.
131 * more slices and stacks will increase the quality of the sphere,
132 * at the expense of rendering speed
135 /* structure for holding the planet data */
142 int screen_width, screen_height;
143 GLXContext *glx_context;
149 trackball_state *trackball;
157 static planetstruct *planets = NULL;
160 /* Set up and enable texturing on our object */
162 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
164 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
165 MI_COLORMAP (mi), xpm_data);
168 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
169 image->width, image->height, 0,
171 /* GL_UNSIGNED_BYTE, */
172 GL_UNSIGNED_INT_8_8_8_8_REV,
174 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
177 /* setup parameters for texturing */
178 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
179 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
180 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
181 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
182 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
183 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
188 setup_file_texture (ModeInfo *mi, char *filename)
190 Display *dpy = mi->dpy;
191 Visual *visual = mi->xgwa.visual;
194 Colormap cmap = mi->xgwa.colormap;
195 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
198 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
199 image->width, image->height, 0,
201 /* GL_UNSIGNED_BYTE, */
202 GL_UNSIGNED_INT_8_8_8_8_REV,
204 sprintf (buf, "texture: %.100s (%dx%d)",
205 filename, image->width, image->height);
208 /* setup parameters for texturing */
209 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
210 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
212 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
213 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
214 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
215 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
216 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
221 setup_texture(ModeInfo * mi)
223 planetstruct *gp = &planets[MI_SCREEN(mi)];
227 !strcmp(which_image, "BUILTIN"))
229 glGenTextures (1, &gp->tex1);
230 glBindTexture (GL_TEXTURE_2D, gp->tex1);
231 setup_xpm_texture (mi, earth_xpm);
232 glGenTextures (1, &gp->tex2);
233 glBindTexture (GL_TEXTURE_2D, gp->tex2);
234 setup_xpm_texture (mi, earth_night_xpm);
238 glGenTextures (1, &gp->tex1);
239 glBindTexture (GL_TEXTURE_2D, gp->tex1);
240 setup_file_texture (mi, which_image);
243 check_gl_error("texture initialization");
245 /* Need to flip the texture top for bottom for some reason. */
246 glMatrixMode (GL_TEXTURE);
248 glMatrixMode (GL_MODELVIEW);
253 init_stars (ModeInfo *mi)
255 planetstruct *gp = &planets[MI_SCREEN(mi)];
257 int width = MI_WIDTH(mi);
258 int height = MI_HEIGHT(mi);
259 int size = (width > height ? width : height);
260 int nstars = size * size / 80;
263 int steps = max_size / inc;
265 gp->starlist = glGenLists(1);
266 glNewList(gp->starlist, GL_COMPILE);
267 for (j = 1; j <= steps; j++)
269 glPointSize(inc * j);
271 for (i = 0; i < nstars / steps; i++)
274 GLfloat r = 0.15 + frand(0.3);
275 GLfloat g = r + frand(d) - d;
276 GLfloat b = r + frand(d) - d;
278 GLfloat x = frand(1)-0.5;
279 GLfloat y = frand(1)-0.5;
280 GLfloat z = ((random() & 1)
282 : (BELLRAND(1)-0.5)/12); /* milky way */
283 d = sqrt (x*x + y*y + z*z);
288 glVertex3f (x, y, z);
295 check_gl_error("stars initialization");
300 reshape_planet (ModeInfo *mi, int width, int height)
302 GLfloat h = (GLfloat) height / (GLfloat) width;
304 glViewport(0, 0, (GLint) width, (GLint) height);
305 glMatrixMode(GL_PROJECTION);
307 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
308 glMatrixMode(GL_MODELVIEW);
310 glTranslatef(0.0, 0.0, -40);
312 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
317 planet_handle_event (ModeInfo *mi, XEvent *event)
319 planetstruct *gp = &planets[MI_SCREEN(mi)];
321 if (gltrackball_event_handler (event, gp->trackball,
322 MI_WIDTH (mi), MI_HEIGHT (mi),
331 init_planet (ModeInfo * mi)
334 int screen = MI_SCREEN(mi);
335 Bool wire = MI_IS_WIREFRAME(mi);
337 if (planets == NULL) {
338 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
339 sizeof (planetstruct))) == NULL)
342 gp = &planets[screen];
344 if ((gp->glx_context = init_GL(mi)) != NULL) {
345 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
349 char *f = get_string_resource(mi->dpy, "imageForeground", "Foreground");
350 char *b = get_string_resource(mi->dpy, "imageBackground", "Background");
352 if (!f) f = strdup("white");
353 if (!b) b = strdup("black");
355 for (s = f + strlen(f)-1; s > f; s--)
356 if (*s == ' ' || *s == '\t')
358 for (s = b + strlen(b)-1; s > b; s--)
359 if (*s == ' ' || *s == '\t')
362 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
364 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
367 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
369 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
378 double spin_speed = 0.1;
379 double wander_speed = 0.005;
380 gp->rot = make_rotator (do_roll ? spin_speed : 0,
381 do_roll ? spin_speed : 0,
383 do_wander ? wander_speed : 0,
386 gp->tilt = frand (23.4);
387 gp->trackball = gltrackball_init (True);
399 /* construct the polygons of the planet
401 gp->platelist = glGenLists(1);
402 glNewList (gp->platelist, GL_COMPILE);
405 glRotatef (90, 1, 0, 0);
406 unit_sphere (resolution, resolution, wire);
410 gp->shadowlist = glGenLists(1);
411 glNewList (gp->shadowlist, GL_COMPILE);
413 unit_dome (resolution, resolution, wire);
416 /* construct the polygons of the latitude/longitude/axis lines.
418 gp->latlonglist = glGenLists(1);
419 glNewList (gp->latlonglist, GL_COMPILE);
421 glRotatef (90, 1, 0, 0);
422 unit_sphere (12, 24, 1);
424 glVertex3f(0, -2, 0);
433 draw_planet (ModeInfo * mi)
435 planetstruct *gp = &planets[MI_SCREEN(mi)];
436 int wire = MI_IS_WIREFRAME(mi);
437 Display *dpy = MI_DISPLAY(mi);
438 Window window = MI_WINDOW(mi);
441 if (!gp->glx_context)
444 glDrawBuffer(GL_BACK);
445 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
447 glXMakeCurrent (dpy, window, *(gp->glx_context));
449 mi->polygon_count = 0;
451 if (gp->button_down_p)
453 else if (!gp->draw_axis && !(random() % 1000))
454 gp->draw_axis = 60 + (random() % 90);
456 if (do_rotate && !gp->button_down_p)
458 gp->z -= 0.001; /* the sun sets in the west */
459 if (gp->z < 0) gp->z += 1;
462 glEnable(GL_LINE_SMOOTH);
463 glEnable(GL_POINT_SMOOTH);
464 glEnable(GL_DEPTH_TEST);
465 glEnable(GL_CULL_FACE);
470 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
474 glTranslatef(x, y, z);
476 gltrackball_rotate (gp->trackball);
480 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
481 glRotatef (x * 360, 1.0, 0.0, 0.0);
482 glRotatef (y * 360, 0.0, 1.0, 0.0);
485 glRotatef (current_device_rotation(), 0, 0, 1);
489 glDisable(GL_TEXTURE_2D);
491 glTranslatef(-x, -y, -z);
492 glScalef (40, 40, 40);
493 glRotatef (90, 1, 0, 0);
494 glRotatef (35, 1, 0, 0);
495 glCallList (gp->starlist);
496 mi->polygon_count += gp->starcount;
498 glClear(GL_DEPTH_BUFFER_BIT);
501 glRotatef (90, 1, 0, 0);
502 glRotatef (35, 1, 0, 0);
503 glRotatef (10, 0, 1, 0);
504 glRotatef (120, 0, 0, 1);
513 glColor3f (0.5, 0.5, 1);
519 glEnable(GL_TEXTURE_2D);
520 glBindTexture (GL_TEXTURE_2D, gp->tex1);
524 glRotatef (gp->z * 360, 0, 0, 1);
525 glCallList (gp->platelist);
526 mi->polygon_count += resolution*resolution;
529 /* Originally we just used GL_LIGHT0 to produce the day/night sides of
530 the planet, but that always looked crappy, even with a vast number of
531 polygons, because the day/night terminator didn't exactly line up with
534 So instead, draw the full "day" sphere; clear the depth buffer; draw
535 a rotated/tilted half-sphere into the depth buffer only; then draw
536 the "night" sphere. That lets us divide the sphere into the two maps,
537 and the dividing line can be at any angle, regardless of polygon layout.
539 The half-sphere is scaled slightly larger to avoid polygon fighting,
540 since those triangles won't exactly line up because of the rotation.
542 The downside of this is that the day/night terminator is 100% sharp.
543 It would be nice if it was a little blurry.
549 glRotatef (gp->tilt, 1, 0, 0);
552 glCallList (gp->shadowlist);
554 mi->polygon_count += resolution*(resolution/2);
557 else if (do_texture && gp->tex2)
559 glClear(GL_DEPTH_BUFFER_BIT);
560 glDisable(GL_TEXTURE_2D);
561 glColorMask (0, 0, 0, 0);
563 glRotatef (gp->tilt, 1, 0, 0);
564 glScalef (1.01, 1.01, 1.01);
565 glCallList (gp->shadowlist);
566 mi->polygon_count += resolution*(resolution/2);
568 glColorMask (1, 1, 1, 1);
569 glEnable(GL_TEXTURE_2D);
571 glBindTexture (GL_TEXTURE_2D, gp->tex2);
573 glRotatef (gp->z * 360, 0, 0, 1);
574 glCallList (gp->platelist);
575 mi->polygon_count += resolution*resolution;
582 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
583 glScalef (1.02, 1.02, 1.02);
584 glDisable (GL_TEXTURE_2D);
585 glDisable (GL_LIGHTING);
586 glDisable (GL_LINE_SMOOTH);
587 glColor3f (0.1, 0.3, 0.1);
588 glCallList (gp->latlonglist);
589 mi->polygon_count += 24*24;
591 if (gp->draw_axis) gp->draw_axis--;
595 if (mi->fps_p) do_fps (mi);
597 glXSwapBuffers(dpy, window);
602 release_planet (ModeInfo * mi)
604 if (planets != NULL) {
607 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
608 planetstruct *gp = &planets[screen];
610 if (gp->glx_context) {
611 /* Display lists MUST be freed while their glXContext is current. */
612 /* but this gets a BadMatch error. -jwz */
613 /*glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));*/
615 if (glIsList(gp->platelist))
616 glDeleteLists(gp->platelist, 1);
617 if (glIsList(gp->starlist))
618 glDeleteLists(gp->starlist, 1);
621 (void) free((void *) planets);
628 XSCREENSAVER_MODULE_2 ("GLPlanet", glplanet, planet)