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 # define release_planet 0
43 # include "xlockmore.h" /* from the xscreensaver distribution */
44 #else /* !STANDALONE */
45 # include "xlock.h" /* from the xlockmore distribution */
46 #endif /* !STANDALONE */
48 #ifdef USE_GL /* whole file */
54 # include <X11/Xmu/Drawing.h>
56 # include <Xmu/Drawing.h>
60 #define DEF_ROTATE "True"
61 #define DEF_ROLL "True"
62 #define DEF_WANDER "True"
63 #define DEF_SPIN "0.03"
64 #define DEF_TEXTURE "True"
65 #define DEF_STARS "True"
66 #define DEF_RESOLUTION "128"
67 #define DEF_IMAGE "BUILTIN"
68 #define DEF_IMAGE2 "BUILTIN"
71 #define countof(x) (sizeof((x))/sizeof((*x)))
74 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
79 static int do_texture;
81 static char *which_image;
82 static char *which_image2;
83 static int resolution;
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 {"-image", ".glplanet.image", XrmoptionSepArg, 0 },
98 {"-image2", ".glplanet.image2", XrmoptionSepArg, 0 },
99 {"-resolution", ".glplanet.resolution", XrmoptionSepArg, 0 },
102 static argtype vars[] = {
103 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
104 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
105 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
106 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
107 {&do_stars, "stars", "Stars", DEF_STARS, t_Bool},
108 {&which_image, "image", "Image", DEF_IMAGE, t_String},
109 {&which_image2,"image2", "Image", DEF_IMAGE2, t_String},
110 {&resolution, "resolution","Resolution", DEF_RESOLUTION, t_Int},
113 ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
116 ModStruct planet_description =
117 {"planet", "init_planet", "draw_planet", NULL,
118 "draw_planet", "init_planet", NULL, &planet_opts,
119 1000, 1, 2, 1, 4, 1.0, "",
120 "Animates texture mapped sphere (planet)", 0, NULL};
124 __extension__ /* don't warn about "string length is greater than the length
125 ISO C89 compilers are required to support" when including
126 the following XPM file... */
128 #include "../images/earth.xpm"
129 #include "../images/earth_night.xpm"
131 #include "xpm-ximage.h"
133 #include "gltrackball.h"
137 * slices and stacks are used in the sphere parameterization routine.
138 * more slices and stacks will increase the quality of the sphere,
139 * at the expense of rendering speed
142 /* structure for holding the planet data */
149 int screen_width, screen_height;
150 GLXContext *glx_context;
156 trackball_state *trackball;
164 static planetstruct *planets = NULL;
167 /* Set up and enable texturing on our object */
169 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
171 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
172 MI_COLORMAP (mi), xpm_data);
175 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
177 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
179 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
180 image->width, image->height, 0,
182 /* GL_UNSIGNED_BYTE, */
183 GL_UNSIGNED_INT_8_8_8_8_REV,
185 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
191 setup_file_texture (ModeInfo *mi, char *filename)
193 Display *dpy = mi->dpy;
194 Visual *visual = mi->xgwa.visual;
197 Colormap cmap = mi->xgwa.colormap;
198 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
199 if (!image) return False;
202 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
203 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
204 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
205 image->width, image->height, 0,
207 /* GL_UNSIGNED_BYTE, */
208 GL_UNSIGNED_INT_8_8_8_8_REV,
210 sprintf (buf, "texture: %.100s (%dx%d)",
211 filename, image->width, image->height);
218 setup_texture(ModeInfo * mi)
220 planetstruct *gp = &planets[MI_SCREEN(mi)];
222 glGenTextures (1, &gp->tex1);
223 glBindTexture (GL_TEXTURE_2D, gp->tex1);
225 /* Must be after glBindTexture */
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);
234 !strcmp(which_image, "BUILTIN"))
237 setup_xpm_texture (mi, earth_xpm);
241 if (! setup_file_texture (mi, which_image))
245 check_gl_error("texture 1 initialization");
247 glGenTextures (1, &gp->tex2);
248 glBindTexture (GL_TEXTURE_2D, gp->tex2);
250 /* Must be after glBindTexture */
251 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
252 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
253 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
254 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
255 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
259 !strcmp(which_image2, "BUILTIN"))
262 setup_xpm_texture (mi, earth_night_xpm);
266 if (! setup_file_texture (mi, which_image2))
270 check_gl_error("texture 2 initialization");
272 /* Need to flip the texture top for bottom for some reason. */
273 glMatrixMode (GL_TEXTURE);
275 glMatrixMode (GL_MODELVIEW);
280 init_stars (ModeInfo *mi)
282 planetstruct *gp = &planets[MI_SCREEN(mi)];
284 int width = MI_WIDTH(mi);
285 int height = MI_HEIGHT(mi);
286 int size = (width > height ? width : height);
287 int nstars = size * size / 80;
290 int steps = max_size / inc;
292 gp->starlist = glGenLists(1);
293 glNewList(gp->starlist, GL_COMPILE);
294 for (j = 1; j <= steps; j++)
296 glPointSize(inc * j);
298 for (i = 0; i < nstars / steps; i++)
301 GLfloat r = 0.15 + frand(0.3);
302 GLfloat g = r + frand(d) - d;
303 GLfloat b = r + frand(d) - d;
305 GLfloat x = frand(1)-0.5;
306 GLfloat y = frand(1)-0.5;
307 GLfloat z = ((random() & 1)
309 : (BELLRAND(1)-0.5)/12); /* milky way */
310 d = sqrt (x*x + y*y + z*z);
315 glVertex3f (x, y, z);
322 check_gl_error("stars initialization");
327 reshape_planet (ModeInfo *mi, int width, int height)
329 planetstruct *gp = &planets[MI_SCREEN(mi)];
330 GLfloat h = (GLfloat) height / (GLfloat) width;
332 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
334 glViewport(0, 0, (GLint) width, (GLint) height);
335 glMatrixMode(GL_PROJECTION);
337 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
338 glMatrixMode(GL_MODELVIEW);
340 glTranslatef(0.0, 0.0, -40);
342 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
344 int o = (int) current_device_rotation();
345 if (o != 0 && o != 180 && o != -180)
350 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
355 planet_handle_event (ModeInfo *mi, XEvent *event)
357 planetstruct *gp = &planets[MI_SCREEN(mi)];
359 if (gltrackball_event_handler (event, gp->trackball,
360 MI_WIDTH (mi), MI_HEIGHT (mi),
368 static void free_planet (ModeInfo * mi);
372 init_planet (ModeInfo * mi)
375 int screen = MI_SCREEN(mi);
376 Bool wire = MI_IS_WIREFRAME(mi);
378 MI_INIT (mi, planets, free_planet);
379 gp = &planets[screen];
381 gp->window = MI_WINDOW(mi);
383 if ((gp->glx_context = init_GL(mi)) != NULL) {
384 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
388 char *f = get_string_resource(mi->dpy, "imageForeground", "Foreground");
389 char *b = get_string_resource(mi->dpy, "imageBackground", "Background");
391 if (!f) f = strdup("white");
392 if (!b) b = strdup("black");
394 for (s = f + strlen(f)-1; s > f; s--)
395 if (*s == ' ' || *s == '\t')
397 for (s = b + strlen(b)-1; s > b; s--)
398 if (*s == ' ' || *s == '\t')
401 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
403 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
406 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
408 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
417 double spin_speed = 0.1;
418 double wander_speed = 0.005;
419 gp->rot = make_rotator (do_roll ? spin_speed : 0,
420 do_roll ? spin_speed : 0,
422 do_wander ? wander_speed : 0,
425 gp->tilt = frand (23.4);
426 gp->trackball = gltrackball_init (True);
438 /* construct the polygons of the planet
440 gp->platelist = glGenLists(1);
441 glNewList (gp->platelist, GL_COMPILE);
444 glRotatef (90, 1, 0, 0);
445 unit_sphere (resolution, resolution, wire);
449 gp->shadowlist = glGenLists(1);
450 glNewList (gp->shadowlist, GL_COMPILE);
452 unit_dome (resolution, resolution, wire);
455 /* construct the polygons of the latitude/longitude/axis lines.
457 gp->latlonglist = glGenLists(1);
458 glNewList (gp->latlonglist, GL_COMPILE);
460 glRotatef (90, 1, 0, 0); /* unit_sphere is off by 90 */
461 glRotatef (8, 0, 1, 0); /* line up the time zones */
462 unit_sphere (12, 24, 1);
463 unit_sphere (12, 24, 1);
465 glVertex3f(0, -2, 0);
474 draw_planet (ModeInfo * mi)
476 planetstruct *gp = &planets[MI_SCREEN(mi)];
477 int wire = MI_IS_WIREFRAME(mi);
478 Display *dpy = MI_DISPLAY(mi);
479 Window window = MI_WINDOW(mi);
482 if (!gp->glx_context)
485 glDrawBuffer(GL_BACK);
486 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
488 glXMakeCurrent (dpy, window, *(gp->glx_context));
490 mi->polygon_count = 0;
492 if (gp->button_down_p)
494 else if (!gp->draw_axis && !(random() % 1000))
495 gp->draw_axis = 60 + (random() % 90);
497 if (do_rotate && !gp->button_down_p)
499 gp->z -= 0.001; /* the sun sets in the west */
500 if (gp->z < 0) gp->z += 1;
503 glEnable(GL_LINE_SMOOTH);
504 glEnable(GL_POINT_SMOOTH);
505 glEnable(GL_DEPTH_TEST);
506 glEnable(GL_CULL_FACE);
511 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
515 glTranslatef(x, y, z);
517 gltrackball_rotate (gp->trackball);
521 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
522 glRotatef (x * 360, 1.0, 0.0, 0.0);
523 glRotatef (y * 360, 0.0, 1.0, 0.0);
526 glRotatef (current_device_rotation(), 0, 0, 1);
530 glDisable(GL_TEXTURE_2D);
532 glScalef (60, 60, 60);
533 glRotatef (90, 1, 0, 0);
534 glRotatef (35, 1, 0, 0);
535 glCallList (gp->starlist);
536 mi->polygon_count += gp->starcount;
538 glClear(GL_DEPTH_BUFFER_BIT);
541 glRotatef (90, 1, 0, 0);
542 glRotatef (35, 1, 0, 0);
543 glRotatef (10, 0, 1, 0);
544 glRotatef (120, 0, 0, 1);
553 glColor3f (0.5, 0.5, 1);
559 glEnable(GL_TEXTURE_2D);
560 glBindTexture (GL_TEXTURE_2D, gp->tex1);
564 glRotatef (gp->z * 360, 0, 0, 1);
565 glCallList (gp->platelist);
566 mi->polygon_count += resolution*resolution;
569 /* Originally we just used GL_LIGHT0 to produce the day/night sides of
570 the planet, but that always looked crappy, even with a vast number of
571 polygons, because the day/night terminator didn't exactly line up with
574 So instead, draw the full "day" sphere; clear the depth buffer; draw
575 a rotated/tilted half-sphere into the depth buffer only; then draw
576 the "night" sphere. That lets us divide the sphere into the two maps,
577 and the dividing line can be at any angle, regardless of polygon layout.
579 The half-sphere is scaled slightly larger to avoid polygon fighting,
580 since those triangles won't exactly line up because of the rotation.
582 The downside of this is that the day/night terminator is 100% sharp.
583 It would be nice if it was a little blurry.
589 glRotatef (gp->tilt, 1, 0, 0);
592 glCallList (gp->shadowlist);
594 mi->polygon_count += resolution*(resolution/2);
597 else if (do_texture && gp->tex2)
599 glClear(GL_DEPTH_BUFFER_BIT);
600 glDisable(GL_TEXTURE_2D);
601 glColorMask (0, 0, 0, 0);
603 glRotatef (gp->tilt, 1, 0, 0);
604 glScalef (1.01, 1.01, 1.01);
605 glCallList (gp->shadowlist);
606 mi->polygon_count += resolution*(resolution/2);
608 glColorMask (1, 1, 1, 1);
609 glEnable(GL_TEXTURE_2D);
611 glBindTexture (GL_TEXTURE_2D, gp->tex2);
613 glRotatef (gp->z * 360, 0, 0, 1);
614 glCallList (gp->platelist);
615 mi->polygon_count += resolution*resolution;
622 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
623 glScalef (1.02, 1.02, 1.02);
624 glDisable (GL_TEXTURE_2D);
625 glDisable (GL_LIGHTING);
626 glDisable (GL_LINE_SMOOTH);
627 glColor3f (0.1, 0.3, 0.1);
628 glCallList (gp->latlonglist);
629 mi->polygon_count += 24*24;
631 if (gp->draw_axis) gp->draw_axis--;
635 if (mi->fps_p) do_fps (mi);
637 glXSwapBuffers(dpy, window);
642 free_planet (ModeInfo * mi)
644 planetstruct *gp = &planets[MI_SCREEN(mi)];
646 if (gp->glx_context) {
647 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
649 if (glIsList(gp->platelist))
650 glDeleteLists(gp->platelist, 1);
651 if (glIsList(gp->starlist))
652 glDeleteLists(gp->starlist, 1);
657 XSCREENSAVER_MODULE_2 ("GLPlanet", glplanet, planet)