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"
67 #define DEF_IMAGE2 "BUILTIN"
70 #define countof(x) (sizeof((x))/sizeof((*x)))
73 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
78 static int do_texture;
80 static char *which_image;
81 static char *which_image2;
82 static int resolution;
84 static XrmOptionDescRec opts[] = {
85 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, "true" },
86 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, "false" },
87 {"-roll", ".glplanet.roll", XrmoptionNoArg, "true" },
88 {"+roll", ".glplanet.roll", XrmoptionNoArg, "false" },
89 {"-wander", ".glplanet.wander", XrmoptionNoArg, "true" },
90 {"+wander", ".glplanet.wander", XrmoptionNoArg, "false" },
91 {"-texture", ".glplanet.texture", XrmoptionNoArg, "true" },
92 {"+texture", ".glplanet.texture", XrmoptionNoArg, "false" },
93 {"-stars", ".glplanet.stars", XrmoptionNoArg, "true" },
94 {"+stars", ".glplanet.stars", XrmoptionNoArg, "false" },
95 {"-spin", ".glplanet.spin", XrmoptionSepArg, 0 },
96 {"-image", ".glplanet.image", XrmoptionSepArg, 0 },
97 {"-image2", ".glplanet.image2", XrmoptionSepArg, 0 },
98 {"-resolution", ".glplanet.resolution", XrmoptionSepArg, 0 },
101 static argtype vars[] = {
102 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
103 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
104 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
105 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
106 {&do_stars, "stars", "Stars", DEF_STARS, t_Bool},
107 {&which_image, "image", "Image", DEF_IMAGE, t_String},
108 {&which_image2,"image2", "Image", DEF_IMAGE2, t_String},
109 {&resolution, "resolution","Resolution", DEF_RESOLUTION, t_Int},
112 ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
115 ModStruct planet_description =
116 {"planet", "init_planet", "draw_planet", "release_planet",
117 "draw_planet", "init_planet", NULL, &planet_opts,
118 1000, 1, 2, 1, 4, 1.0, "",
119 "Animates texture mapped sphere (planet)", 0, NULL};
123 __extension__ /* don't warn about "string length is greater than the length
124 ISO C89 compilers are required to support" when including
125 the following XPM file... */
127 #include "../images/earth.xpm"
128 #include "../images/earth_night.xpm"
130 #include "xpm-ximage.h"
132 #include "gltrackball.h"
136 * slices and stacks are used in the sphere parameterization routine.
137 * more slices and stacks will increase the quality of the sphere,
138 * at the expense of rendering speed
141 /* structure for holding the planet data */
148 int screen_width, screen_height;
149 GLXContext *glx_context;
155 trackball_state *trackball;
163 static planetstruct *planets = NULL;
166 /* Set up and enable texturing on our object */
168 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
170 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
171 MI_COLORMAP (mi), xpm_data);
174 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
176 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
178 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
179 image->width, image->height, 0,
181 /* GL_UNSIGNED_BYTE, */
182 GL_UNSIGNED_INT_8_8_8_8_REV,
184 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
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);
198 if (!image) return False;
201 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
202 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
203 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
204 image->width, image->height, 0,
206 /* GL_UNSIGNED_BYTE, */
207 GL_UNSIGNED_INT_8_8_8_8_REV,
209 sprintf (buf, "texture: %.100s (%dx%d)",
210 filename, image->width, image->height);
217 setup_texture(ModeInfo * mi)
219 planetstruct *gp = &planets[MI_SCREEN(mi)];
221 glGenTextures (1, &gp->tex1);
222 glBindTexture (GL_TEXTURE_2D, gp->tex1);
224 /* Must be after glBindTexture */
225 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
226 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
227 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
228 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
229 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
233 !strcmp(which_image, "BUILTIN"))
236 setup_xpm_texture (mi, earth_xpm);
240 if (! setup_file_texture (mi, which_image))
244 check_gl_error("texture 1 initialization");
246 glGenTextures (1, &gp->tex2);
247 glBindTexture (GL_TEXTURE_2D, gp->tex2);
249 /* Must be after glBindTexture */
250 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
251 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
252 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
253 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
254 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
258 !strcmp(which_image2, "BUILTIN"))
261 setup_xpm_texture (mi, earth_night_xpm);
265 if (! setup_file_texture (mi, which_image2))
269 check_gl_error("texture 2 initialization");
271 /* Need to flip the texture top for bottom for some reason. */
272 glMatrixMode (GL_TEXTURE);
274 glMatrixMode (GL_MODELVIEW);
279 init_stars (ModeInfo *mi)
281 planetstruct *gp = &planets[MI_SCREEN(mi)];
283 int width = MI_WIDTH(mi);
284 int height = MI_HEIGHT(mi);
285 int size = (width > height ? width : height);
286 int nstars = size * size / 80;
289 int steps = max_size / inc;
291 gp->starlist = glGenLists(1);
292 glNewList(gp->starlist, GL_COMPILE);
293 for (j = 1; j <= steps; j++)
295 glPointSize(inc * j);
297 for (i = 0; i < nstars / steps; i++)
300 GLfloat r = 0.15 + frand(0.3);
301 GLfloat g = r + frand(d) - d;
302 GLfloat b = r + frand(d) - d;
304 GLfloat x = frand(1)-0.5;
305 GLfloat y = frand(1)-0.5;
306 GLfloat z = ((random() & 1)
308 : (BELLRAND(1)-0.5)/12); /* milky way */
309 d = sqrt (x*x + y*y + z*z);
314 glVertex3f (x, y, z);
321 check_gl_error("stars initialization");
326 reshape_planet (ModeInfo *mi, int width, int height)
328 GLfloat h = (GLfloat) height / (GLfloat) width;
330 glViewport(0, 0, (GLint) width, (GLint) height);
331 glMatrixMode(GL_PROJECTION);
333 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
334 glMatrixMode(GL_MODELVIEW);
336 glTranslatef(0.0, 0.0, -40);
338 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
340 int o = (int) current_device_rotation();
341 if (o != 0 && o != 180 && o != -180)
346 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
351 planet_handle_event (ModeInfo *mi, XEvent *event)
353 planetstruct *gp = &planets[MI_SCREEN(mi)];
355 if (gltrackball_event_handler (event, gp->trackball,
356 MI_WIDTH (mi), MI_HEIGHT (mi),
365 init_planet (ModeInfo * mi)
368 int screen = MI_SCREEN(mi);
369 Bool wire = MI_IS_WIREFRAME(mi);
371 if (planets == NULL) {
372 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
373 sizeof (planetstruct))) == NULL)
376 gp = &planets[screen];
378 if ((gp->glx_context = init_GL(mi)) != NULL) {
379 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
383 char *f = get_string_resource(mi->dpy, "imageForeground", "Foreground");
384 char *b = get_string_resource(mi->dpy, "imageBackground", "Background");
386 if (!f) f = strdup("white");
387 if (!b) b = strdup("black");
389 for (s = f + strlen(f)-1; s > f; s--)
390 if (*s == ' ' || *s == '\t')
392 for (s = b + strlen(b)-1; s > b; s--)
393 if (*s == ' ' || *s == '\t')
396 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
398 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
401 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
403 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
412 double spin_speed = 0.1;
413 double wander_speed = 0.005;
414 gp->rot = make_rotator (do_roll ? spin_speed : 0,
415 do_roll ? spin_speed : 0,
417 do_wander ? wander_speed : 0,
420 gp->tilt = frand (23.4);
421 gp->trackball = gltrackball_init (True);
433 /* construct the polygons of the planet
435 gp->platelist = glGenLists(1);
436 glNewList (gp->platelist, GL_COMPILE);
439 glRotatef (90, 1, 0, 0);
440 unit_sphere (resolution, resolution, wire);
444 gp->shadowlist = glGenLists(1);
445 glNewList (gp->shadowlist, GL_COMPILE);
447 unit_dome (resolution, resolution, wire);
450 /* construct the polygons of the latitude/longitude/axis lines.
452 gp->latlonglist = glGenLists(1);
453 glNewList (gp->latlonglist, GL_COMPILE);
455 glRotatef (90, 1, 0, 0); /* unit_sphere is off by 90 */
456 glRotatef (8, 0, 1, 0); /* line up the time zones */
457 unit_sphere (12, 24, 1);
458 unit_sphere (12, 24, 1);
460 glVertex3f(0, -2, 0);
469 draw_planet (ModeInfo * mi)
471 planetstruct *gp = &planets[MI_SCREEN(mi)];
472 int wire = MI_IS_WIREFRAME(mi);
473 Display *dpy = MI_DISPLAY(mi);
474 Window window = MI_WINDOW(mi);
477 if (!gp->glx_context)
480 glDrawBuffer(GL_BACK);
481 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
483 glXMakeCurrent (dpy, window, *(gp->glx_context));
485 mi->polygon_count = 0;
487 if (gp->button_down_p)
489 else if (!gp->draw_axis && !(random() % 1000))
490 gp->draw_axis = 60 + (random() % 90);
492 if (do_rotate && !gp->button_down_p)
494 gp->z -= 0.001; /* the sun sets in the west */
495 if (gp->z < 0) gp->z += 1;
498 glEnable(GL_LINE_SMOOTH);
499 glEnable(GL_POINT_SMOOTH);
500 glEnable(GL_DEPTH_TEST);
501 glEnable(GL_CULL_FACE);
506 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
510 glTranslatef(x, y, z);
512 gltrackball_rotate (gp->trackball);
516 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
517 glRotatef (x * 360, 1.0, 0.0, 0.0);
518 glRotatef (y * 360, 0.0, 1.0, 0.0);
521 glRotatef (current_device_rotation(), 0, 0, 1);
525 glDisable(GL_TEXTURE_2D);
527 glScalef (60, 60, 60);
528 glRotatef (90, 1, 0, 0);
529 glRotatef (35, 1, 0, 0);
530 glCallList (gp->starlist);
531 mi->polygon_count += gp->starcount;
533 glClear(GL_DEPTH_BUFFER_BIT);
536 glRotatef (90, 1, 0, 0);
537 glRotatef (35, 1, 0, 0);
538 glRotatef (10, 0, 1, 0);
539 glRotatef (120, 0, 0, 1);
548 glColor3f (0.5, 0.5, 1);
554 glEnable(GL_TEXTURE_2D);
555 glBindTexture (GL_TEXTURE_2D, gp->tex1);
559 glRotatef (gp->z * 360, 0, 0, 1);
560 glCallList (gp->platelist);
561 mi->polygon_count += resolution*resolution;
564 /* Originally we just used GL_LIGHT0 to produce the day/night sides of
565 the planet, but that always looked crappy, even with a vast number of
566 polygons, because the day/night terminator didn't exactly line up with
569 So instead, draw the full "day" sphere; clear the depth buffer; draw
570 a rotated/tilted half-sphere into the depth buffer only; then draw
571 the "night" sphere. That lets us divide the sphere into the two maps,
572 and the dividing line can be at any angle, regardless of polygon layout.
574 The half-sphere is scaled slightly larger to avoid polygon fighting,
575 since those triangles won't exactly line up because of the rotation.
577 The downside of this is that the day/night terminator is 100% sharp.
578 It would be nice if it was a little blurry.
584 glRotatef (gp->tilt, 1, 0, 0);
587 glCallList (gp->shadowlist);
589 mi->polygon_count += resolution*(resolution/2);
592 else if (do_texture && gp->tex2)
594 glClear(GL_DEPTH_BUFFER_BIT);
595 glDisable(GL_TEXTURE_2D);
596 glColorMask (0, 0, 0, 0);
598 glRotatef (gp->tilt, 1, 0, 0);
599 glScalef (1.01, 1.01, 1.01);
600 glCallList (gp->shadowlist);
601 mi->polygon_count += resolution*(resolution/2);
603 glColorMask (1, 1, 1, 1);
604 glEnable(GL_TEXTURE_2D);
606 glBindTexture (GL_TEXTURE_2D, gp->tex2);
608 glRotatef (gp->z * 360, 0, 0, 1);
609 glCallList (gp->platelist);
610 mi->polygon_count += resolution*resolution;
617 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
618 glScalef (1.02, 1.02, 1.02);
619 glDisable (GL_TEXTURE_2D);
620 glDisable (GL_LIGHTING);
621 glDisable (GL_LINE_SMOOTH);
622 glColor3f (0.1, 0.3, 0.1);
623 glCallList (gp->latlonglist);
624 mi->polygon_count += 24*24;
626 if (gp->draw_axis) gp->draw_axis--;
630 if (mi->fps_p) do_fps (mi);
632 glXSwapBuffers(dpy, window);
637 release_planet (ModeInfo * mi)
639 if (planets != NULL) {
642 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
643 planetstruct *gp = &planets[screen];
645 if (gp->glx_context) {
646 /* Display lists MUST be freed while their glXContext is current. */
647 /* but this gets a BadMatch error. -jwz */
648 /*glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));*/
650 if (glIsList(gp->platelist))
651 glDeleteLists(gp->platelist, 1);
652 if (glIsList(gp->starlist))
653 glDeleteLists(gp->starlist, 1);
656 (void) free((void *) planets);
663 XSCREENSAVER_MODULE_2 ("GLPlanet", glplanet, planet)