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 "*suppressRotationAnimation: True\n" \
41 # define refresh_planet 0
42 # include "xlockmore.h" /* from the xscreensaver distribution */
43 #else /* !STANDALONE */
44 # include "xlock.h" /* from the xlockmore distribution */
45 #endif /* !STANDALONE */
47 #ifdef USE_GL /* whole file */
53 # include <X11/Xmu/Drawing.h>
55 # include <Xmu/Drawing.h>
59 #define DEF_ROTATE "True"
60 #define DEF_ROLL "True"
61 #define DEF_WANDER "True"
62 #define DEF_SPIN "0.03"
63 #define DEF_TEXTURE "True"
64 #define DEF_STARS "True"
65 #define DEF_RESOLUTION "128"
66 #define DEF_IMAGE "BUILTIN"
69 #define countof(x) (sizeof((x))/sizeof((*x)))
72 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
77 static int do_texture;
79 static char *which_image;
80 static int resolution;
82 static XrmOptionDescRec opts[] = {
83 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, "true" },
84 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, "false" },
85 {"-roll", ".glplanet.roll", XrmoptionNoArg, "true" },
86 {"+roll", ".glplanet.roll", XrmoptionNoArg, "false" },
87 {"-wander", ".glplanet.wander", XrmoptionNoArg, "true" },
88 {"+wander", ".glplanet.wander", XrmoptionNoArg, "false" },
89 {"-texture", ".glplanet.texture", XrmoptionNoArg, "true" },
90 {"+texture", ".glplanet.texture", XrmoptionNoArg, "false" },
91 {"-stars", ".glplanet.stars", XrmoptionNoArg, "true" },
92 {"+stars", ".glplanet.stars", XrmoptionNoArg, "false" },
93 {"-spin", ".glplanet.spin", XrmoptionSepArg, 0 },
94 {"-image", ".glplanet.image", XrmoptionSepArg, 0 },
95 {"-resolution", ".glplanet.resolution", XrmoptionSepArg, 0 },
98 static argtype vars[] = {
99 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
100 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
101 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
102 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
103 {&do_stars, "stars", "Stars", DEF_STARS, t_Bool},
104 {&which_image, "image", "Image", DEF_IMAGE, t_String},
105 {&resolution, "resolution","Resolution", DEF_RESOLUTION, t_Int},
108 ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
111 ModStruct planet_description =
112 {"planet", "init_planet", "draw_planet", "release_planet",
113 "draw_planet", "init_planet", NULL, &planet_opts,
114 1000, 1, 2, 1, 4, 1.0, "",
115 "Animates texture mapped sphere (planet)", 0, NULL};
119 __extension__ /* don't warn about "string length is greater than the length
120 ISO C89 compilers are required to support" when including
121 the following XPM file... */
123 #include "../images/earth.xpm"
124 #include "../images/earth_night.xpm"
126 #include "xpm-ximage.h"
128 #include "gltrackball.h"
132 * slices and stacks are used in the sphere parameterization routine.
133 * more slices and stacks will increase the quality of the sphere,
134 * at the expense of rendering speed
137 /* structure for holding the planet data */
144 int screen_width, screen_height;
145 GLXContext *glx_context;
151 trackball_state *trackball;
159 static planetstruct *planets = NULL;
162 /* Set up and enable texturing on our object */
164 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
166 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
167 MI_COLORMAP (mi), xpm_data);
170 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
171 image->width, image->height, 0,
173 /* GL_UNSIGNED_BYTE, */
174 GL_UNSIGNED_INT_8_8_8_8_REV,
176 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
179 /* setup parameters for texturing */
180 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
181 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
182 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
183 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
184 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
185 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
190 setup_file_texture (ModeInfo *mi, char *filename)
192 Display *dpy = mi->dpy;
193 Visual *visual = mi->xgwa.visual;
196 Colormap cmap = mi->xgwa.colormap;
197 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
200 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
201 image->width, image->height, 0,
203 /* GL_UNSIGNED_BYTE, */
204 GL_UNSIGNED_INT_8_8_8_8_REV,
206 sprintf (buf, "texture: %.100s (%dx%d)",
207 filename, image->width, image->height);
210 /* setup parameters for texturing */
211 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
212 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
214 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
215 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
216 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
217 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
218 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
223 setup_texture(ModeInfo * mi)
225 planetstruct *gp = &planets[MI_SCREEN(mi)];
229 !strcmp(which_image, "BUILTIN"))
231 glGenTextures (1, &gp->tex1);
232 glBindTexture (GL_TEXTURE_2D, gp->tex1);
233 setup_xpm_texture (mi, earth_xpm);
234 glGenTextures (1, &gp->tex2);
235 glBindTexture (GL_TEXTURE_2D, gp->tex2);
236 setup_xpm_texture (mi, earth_night_xpm);
240 glGenTextures (1, &gp->tex1);
241 glBindTexture (GL_TEXTURE_2D, gp->tex1);
242 setup_file_texture (mi, which_image);
245 check_gl_error("texture initialization");
247 /* Need to flip the texture top for bottom for some reason. */
248 glMatrixMode (GL_TEXTURE);
250 glMatrixMode (GL_MODELVIEW);
255 init_stars (ModeInfo *mi)
257 planetstruct *gp = &planets[MI_SCREEN(mi)];
259 int width = MI_WIDTH(mi);
260 int height = MI_HEIGHT(mi);
261 int size = (width > height ? width : height);
262 int nstars = size * size / 80;
265 int steps = max_size / inc;
267 gp->starlist = glGenLists(1);
268 glNewList(gp->starlist, GL_COMPILE);
269 for (j = 1; j <= steps; j++)
271 glPointSize(inc * j);
273 for (i = 0; i < nstars / steps; i++)
276 GLfloat r = 0.15 + frand(0.3);
277 GLfloat g = r + frand(d) - d;
278 GLfloat b = r + frand(d) - d;
280 GLfloat x = frand(1)-0.5;
281 GLfloat y = frand(1)-0.5;
282 GLfloat z = ((random() & 1)
284 : (BELLRAND(1)-0.5)/12); /* milky way */
285 d = sqrt (x*x + y*y + z*z);
290 glVertex3f (x, y, z);
297 check_gl_error("stars initialization");
302 reshape_planet (ModeInfo *mi, int width, int height)
304 GLfloat h = (GLfloat) height / (GLfloat) width;
306 glViewport(0, 0, (GLint) width, (GLint) height);
307 glMatrixMode(GL_PROJECTION);
309 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
310 glMatrixMode(GL_MODELVIEW);
312 glTranslatef(0.0, 0.0, -40);
314 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
316 int o = (int) current_device_rotation();
317 if (o != 0 && o != 180 && o != -180)
322 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
327 planet_handle_event (ModeInfo *mi, XEvent *event)
329 planetstruct *gp = &planets[MI_SCREEN(mi)];
331 if (gltrackball_event_handler (event, gp->trackball,
332 MI_WIDTH (mi), MI_HEIGHT (mi),
341 init_planet (ModeInfo * mi)
344 int screen = MI_SCREEN(mi);
345 Bool wire = MI_IS_WIREFRAME(mi);
347 if (planets == NULL) {
348 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
349 sizeof (planetstruct))) == NULL)
352 gp = &planets[screen];
354 if ((gp->glx_context = init_GL(mi)) != NULL) {
355 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
359 char *f = get_string_resource(mi->dpy, "imageForeground", "Foreground");
360 char *b = get_string_resource(mi->dpy, "imageBackground", "Background");
362 if (!f) f = strdup("white");
363 if (!b) b = strdup("black");
365 for (s = f + strlen(f)-1; s > f; s--)
366 if (*s == ' ' || *s == '\t')
368 for (s = b + strlen(b)-1; s > b; s--)
369 if (*s == ' ' || *s == '\t')
372 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
374 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
377 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
379 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
388 double spin_speed = 0.1;
389 double wander_speed = 0.005;
390 gp->rot = make_rotator (do_roll ? spin_speed : 0,
391 do_roll ? spin_speed : 0,
393 do_wander ? wander_speed : 0,
396 gp->tilt = frand (23.4);
397 gp->trackball = gltrackball_init (True);
409 /* construct the polygons of the planet
411 gp->platelist = glGenLists(1);
412 glNewList (gp->platelist, GL_COMPILE);
415 glRotatef (90, 1, 0, 0);
416 unit_sphere (resolution, resolution, wire);
420 gp->shadowlist = glGenLists(1);
421 glNewList (gp->shadowlist, GL_COMPILE);
423 unit_dome (resolution, resolution, wire);
426 /* construct the polygons of the latitude/longitude/axis lines.
428 gp->latlonglist = glGenLists(1);
429 glNewList (gp->latlonglist, GL_COMPILE);
431 glRotatef (90, 1, 0, 0); /* unit_sphere is off by 90 */
432 glRotatef (8, 0, 1, 0); /* line up the time zones */
433 unit_sphere (12, 24, 1);
434 unit_sphere (12, 24, 1);
436 glVertex3f(0, -2, 0);
445 draw_planet (ModeInfo * mi)
447 planetstruct *gp = &planets[MI_SCREEN(mi)];
448 int wire = MI_IS_WIREFRAME(mi);
449 Display *dpy = MI_DISPLAY(mi);
450 Window window = MI_WINDOW(mi);
453 if (!gp->glx_context)
456 glDrawBuffer(GL_BACK);
457 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
459 glXMakeCurrent (dpy, window, *(gp->glx_context));
461 mi->polygon_count = 0;
463 if (gp->button_down_p)
465 else if (!gp->draw_axis && !(random() % 1000))
466 gp->draw_axis = 60 + (random() % 90);
468 if (do_rotate && !gp->button_down_p)
470 gp->z -= 0.001; /* the sun sets in the west */
471 if (gp->z < 0) gp->z += 1;
474 glEnable(GL_LINE_SMOOTH);
475 glEnable(GL_POINT_SMOOTH);
476 glEnable(GL_DEPTH_TEST);
477 glEnable(GL_CULL_FACE);
482 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
486 glTranslatef(x, y, z);
488 gltrackball_rotate (gp->trackball);
492 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
493 glRotatef (x * 360, 1.0, 0.0, 0.0);
494 glRotatef (y * 360, 0.0, 1.0, 0.0);
497 glRotatef (current_device_rotation(), 0, 0, 1);
501 glDisable(GL_TEXTURE_2D);
503 glScalef (60, 60, 60);
504 glRotatef (90, 1, 0, 0);
505 glRotatef (35, 1, 0, 0);
506 glCallList (gp->starlist);
507 mi->polygon_count += gp->starcount;
509 glClear(GL_DEPTH_BUFFER_BIT);
512 glRotatef (90, 1, 0, 0);
513 glRotatef (35, 1, 0, 0);
514 glRotatef (10, 0, 1, 0);
515 glRotatef (120, 0, 0, 1);
524 glColor3f (0.5, 0.5, 1);
530 glEnable(GL_TEXTURE_2D);
531 glBindTexture (GL_TEXTURE_2D, gp->tex1);
535 glRotatef (gp->z * 360, 0, 0, 1);
536 glCallList (gp->platelist);
537 mi->polygon_count += resolution*resolution;
540 /* Originally we just used GL_LIGHT0 to produce the day/night sides of
541 the planet, but that always looked crappy, even with a vast number of
542 polygons, because the day/night terminator didn't exactly line up with
545 So instead, draw the full "day" sphere; clear the depth buffer; draw
546 a rotated/tilted half-sphere into the depth buffer only; then draw
547 the "night" sphere. That lets us divide the sphere into the two maps,
548 and the dividing line can be at any angle, regardless of polygon layout.
550 The half-sphere is scaled slightly larger to avoid polygon fighting,
551 since those triangles won't exactly line up because of the rotation.
553 The downside of this is that the day/night terminator is 100% sharp.
554 It would be nice if it was a little blurry.
560 glRotatef (gp->tilt, 1, 0, 0);
563 glCallList (gp->shadowlist);
565 mi->polygon_count += resolution*(resolution/2);
568 else if (do_texture && gp->tex2)
570 glClear(GL_DEPTH_BUFFER_BIT);
571 glDisable(GL_TEXTURE_2D);
572 glColorMask (0, 0, 0, 0);
574 glRotatef (gp->tilt, 1, 0, 0);
575 glScalef (1.01, 1.01, 1.01);
576 glCallList (gp->shadowlist);
577 mi->polygon_count += resolution*(resolution/2);
579 glColorMask (1, 1, 1, 1);
580 glEnable(GL_TEXTURE_2D);
582 glBindTexture (GL_TEXTURE_2D, gp->tex2);
584 glRotatef (gp->z * 360, 0, 0, 1);
585 glCallList (gp->platelist);
586 mi->polygon_count += resolution*resolution;
593 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
594 glScalef (1.02, 1.02, 1.02);
595 glDisable (GL_TEXTURE_2D);
596 glDisable (GL_LIGHTING);
597 glDisable (GL_LINE_SMOOTH);
598 glColor3f (0.1, 0.3, 0.1);
599 glCallList (gp->latlonglist);
600 mi->polygon_count += 24*24;
602 if (gp->draw_axis) gp->draw_axis--;
606 if (mi->fps_p) do_fps (mi);
608 glXSwapBuffers(dpy, window);
613 release_planet (ModeInfo * mi)
615 if (planets != NULL) {
618 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
619 planetstruct *gp = &planets[screen];
621 if (gp->glx_context) {
622 /* Display lists MUST be freed while their glXContext is current. */
623 /* but this gets a BadMatch error. -jwz */
624 /*glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));*/
626 if (glIsList(gp->platelist))
627 glDeleteLists(gp->platelist, 1);
628 if (glIsList(gp->starlist))
629 glDeleteLists(gp->starlist, 1);
632 (void) free((void *) planets);
639 XSCREENSAVER_MODULE_2 ("GLPlanet", glplanet, planet)