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 release_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", NULL,
117 "draw_planet", "init_planet", "free_planet", &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 planetstruct *gp = &planets[MI_SCREEN(mi)];
329 GLfloat h = (GLfloat) height / (GLfloat) width;
331 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
333 glViewport(0, 0, (GLint) width, (GLint) height);
334 glMatrixMode(GL_PROJECTION);
336 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
337 glMatrixMode(GL_MODELVIEW);
339 glTranslatef(0.0, 0.0, -40);
341 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
343 int o = (int) current_device_rotation();
344 if (o != 0 && o != 180 && o != -180)
349 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
354 planet_handle_event (ModeInfo *mi, XEvent *event)
356 planetstruct *gp = &planets[MI_SCREEN(mi)];
358 if (gltrackball_event_handler (event, gp->trackball,
359 MI_WIDTH (mi), MI_HEIGHT (mi),
368 init_planet (ModeInfo * mi)
371 int screen = MI_SCREEN(mi);
372 Bool wire = MI_IS_WIREFRAME(mi);
374 MI_INIT (mi, planets);
375 gp = &planets[screen];
377 gp->window = MI_WINDOW(mi);
379 if ((gp->glx_context = init_GL(mi)) != NULL) {
380 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
384 char *f = get_string_resource(mi->dpy, "imageForeground", "Foreground");
385 char *b = get_string_resource(mi->dpy, "imageBackground", "Background");
387 if (!f) f = strdup("white");
388 if (!b) b = strdup("black");
390 for (s = f + strlen(f)-1; s > f; s--)
391 if (*s == ' ' || *s == '\t')
393 for (s = b + strlen(b)-1; s > b; s--)
394 if (*s == ' ' || *s == '\t')
397 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
399 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
402 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
404 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
413 double spin_speed = 0.1;
414 double wander_speed = 0.005;
415 gp->rot = make_rotator (do_roll ? spin_speed : 0,
416 do_roll ? spin_speed : 0,
418 do_wander ? wander_speed : 0,
421 gp->tilt = frand (23.4);
422 gp->trackball = gltrackball_init (True);
434 /* construct the polygons of the planet
436 gp->platelist = glGenLists(1);
437 glNewList (gp->platelist, GL_COMPILE);
440 glRotatef (90, 1, 0, 0);
441 unit_sphere (resolution, resolution, wire);
445 gp->shadowlist = glGenLists(1);
446 glNewList (gp->shadowlist, GL_COMPILE);
448 unit_dome (resolution, resolution, wire);
451 /* construct the polygons of the latitude/longitude/axis lines.
453 gp->latlonglist = glGenLists(1);
454 glNewList (gp->latlonglist, GL_COMPILE);
456 glRotatef (90, 1, 0, 0); /* unit_sphere is off by 90 */
457 glRotatef (8, 0, 1, 0); /* line up the time zones */
458 unit_sphere (12, 24, 1);
459 unit_sphere (12, 24, 1);
461 glVertex3f(0, -2, 0);
470 draw_planet (ModeInfo * mi)
472 planetstruct *gp = &planets[MI_SCREEN(mi)];
473 int wire = MI_IS_WIREFRAME(mi);
474 Display *dpy = MI_DISPLAY(mi);
475 Window window = MI_WINDOW(mi);
478 if (!gp->glx_context)
481 glDrawBuffer(GL_BACK);
482 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
484 glXMakeCurrent (dpy, window, *(gp->glx_context));
486 mi->polygon_count = 0;
488 if (gp->button_down_p)
490 else if (!gp->draw_axis && !(random() % 1000))
491 gp->draw_axis = 60 + (random() % 90);
493 if (do_rotate && !gp->button_down_p)
495 gp->z -= 0.001; /* the sun sets in the west */
496 if (gp->z < 0) gp->z += 1;
499 glEnable(GL_LINE_SMOOTH);
500 glEnable(GL_POINT_SMOOTH);
501 glEnable(GL_DEPTH_TEST);
502 glEnable(GL_CULL_FACE);
507 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
511 glTranslatef(x, y, z);
513 gltrackball_rotate (gp->trackball);
517 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
518 glRotatef (x * 360, 1.0, 0.0, 0.0);
519 glRotatef (y * 360, 0.0, 1.0, 0.0);
522 glRotatef (current_device_rotation(), 0, 0, 1);
526 glDisable(GL_TEXTURE_2D);
528 glScalef (60, 60, 60);
529 glRotatef (90, 1, 0, 0);
530 glRotatef (35, 1, 0, 0);
531 glCallList (gp->starlist);
532 mi->polygon_count += gp->starcount;
534 glClear(GL_DEPTH_BUFFER_BIT);
537 glRotatef (90, 1, 0, 0);
538 glRotatef (35, 1, 0, 0);
539 glRotatef (10, 0, 1, 0);
540 glRotatef (120, 0, 0, 1);
549 glColor3f (0.5, 0.5, 1);
555 glEnable(GL_TEXTURE_2D);
556 glBindTexture (GL_TEXTURE_2D, gp->tex1);
560 glRotatef (gp->z * 360, 0, 0, 1);
561 glCallList (gp->platelist);
562 mi->polygon_count += resolution*resolution;
565 /* Originally we just used GL_LIGHT0 to produce the day/night sides of
566 the planet, but that always looked crappy, even with a vast number of
567 polygons, because the day/night terminator didn't exactly line up with
570 So instead, draw the full "day" sphere; clear the depth buffer; draw
571 a rotated/tilted half-sphere into the depth buffer only; then draw
572 the "night" sphere. That lets us divide the sphere into the two maps,
573 and the dividing line can be at any angle, regardless of polygon layout.
575 The half-sphere is scaled slightly larger to avoid polygon fighting,
576 since those triangles won't exactly line up because of the rotation.
578 The downside of this is that the day/night terminator is 100% sharp.
579 It would be nice if it was a little blurry.
585 glRotatef (gp->tilt, 1, 0, 0);
588 glCallList (gp->shadowlist);
590 mi->polygon_count += resolution*(resolution/2);
593 else if (do_texture && gp->tex2)
595 glClear(GL_DEPTH_BUFFER_BIT);
596 glDisable(GL_TEXTURE_2D);
597 glColorMask (0, 0, 0, 0);
599 glRotatef (gp->tilt, 1, 0, 0);
600 glScalef (1.01, 1.01, 1.01);
601 glCallList (gp->shadowlist);
602 mi->polygon_count += resolution*(resolution/2);
604 glColorMask (1, 1, 1, 1);
605 glEnable(GL_TEXTURE_2D);
607 glBindTexture (GL_TEXTURE_2D, gp->tex2);
609 glRotatef (gp->z * 360, 0, 0, 1);
610 glCallList (gp->platelist);
611 mi->polygon_count += resolution*resolution;
618 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
619 glScalef (1.02, 1.02, 1.02);
620 glDisable (GL_TEXTURE_2D);
621 glDisable (GL_LIGHTING);
622 glDisable (GL_LINE_SMOOTH);
623 glColor3f (0.1, 0.3, 0.1);
624 glCallList (gp->latlonglist);
625 mi->polygon_count += 24*24;
627 if (gp->draw_axis) gp->draw_axis--;
631 if (mi->fps_p) do_fps (mi);
633 glXSwapBuffers(dpy, window);
638 free_planet (ModeInfo * mi)
640 planetstruct *gp = &planets[MI_SCREEN(mi)];
642 if (gp->glx_context) {
643 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
645 if (glIsList(gp->platelist))
646 glDeleteLists(gp->platelist, 1);
647 if (glIsList(gp->starlist))
648 glDeleteLists(gp->starlist, 1);
653 XSCREENSAVER_MODULE_2 ("GLPlanet", glplanet, planet)