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 * 16-Jan-02: jwz@jwz.org gdk_pixbuf support.
19 * 21-Mar-01: jwz@jwz.org Broke sphere routine out into its own file.
21 * 9-Oct-98: dek@cgl.ucsf.edu Added stars.
23 * 8-Oct-98: jwz@jwz.org Made the 512x512x1 xearth image be built in.
24 * Made it possible to load XPM or XBM files.
25 * Made the planet bounce and roll around.
27 * 8-Oct-98: Released initial version of "glplanet"
28 * (David Konerding, dek@cgl.ucsf.edu)
33 * For even more spectacular results, grab the images from the "SSystem"
34 * package (http://www.msu.edu/user/kamelkev/) and use its JPEGs!
39 #define DEFAULTS "*delay: 20000 \n" \
40 "*showFPS: False \n" \
41 "*wireframe: False \n" \
42 "*imageForeground: Green \n" \
43 "*imageBackground: Blue \n"
44 # define refresh_planet 0
45 # include "xlockmore.h" /* from the xscreensaver distribution */
46 #else /* !STANDALONE */
47 # include "xlock.h" /* from the xlockmore distribution */
48 #endif /* !STANDALONE */
50 #ifdef USE_GL /* whole file */
56 # include <X11/Xmu/Drawing.h>
58 # include <Xmu/Drawing.h>
62 #define DEF_ROTATE "True"
63 #define DEF_ROLL "True"
64 #define DEF_WANDER "True"
65 #define DEF_TEXTURE "True"
66 #define DEF_STARS "True"
67 #define DEF_LIGHT "True"
68 #define DEF_RESOLUTION "128"
69 #define DEF_IMAGE "BUILTIN"
72 #define countof(x) (sizeof((x))/sizeof((*x)))
77 static int do_texture;
80 static char *which_image;
81 static int resolution;
83 static XrmOptionDescRec opts[] = {
84 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, "true" },
85 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, "false" },
86 {"-roll", ".glplanet.roll", XrmoptionNoArg, "true" },
87 {"+roll", ".glplanet.roll", XrmoptionNoArg, "false" },
88 {"-wander", ".glplanet.wander", XrmoptionNoArg, "true" },
89 {"+wander", ".glplanet.wander", XrmoptionNoArg, "false" },
90 {"-texture", ".glplanet.texture", XrmoptionNoArg, "true" },
91 {"+texture", ".glplanet.texture", XrmoptionNoArg, "false" },
92 {"-stars", ".glplanet.stars", XrmoptionNoArg, "true" },
93 {"+stars", ".glplanet.stars", XrmoptionNoArg, "false" },
94 {"-light", ".glplanet.light", XrmoptionNoArg, "true" },
95 {"+light", ".glplanet.light", XrmoptionNoArg, "false" },
96 {"-image", ".glplanet.image", XrmoptionSepArg, 0 },
97 {"-resolution", ".glplanet.resolution", XrmoptionSepArg, 0 },
100 static argtype vars[] = {
101 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
102 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
103 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
104 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
105 {&do_stars, "stars", "Stars", DEF_STARS, t_Bool},
106 {&do_light, "light", "Light", DEF_LIGHT, t_Bool},
107 {&which_image, "image", "Image", DEF_IMAGE, t_String},
108 {&resolution, "resolution","Resolution", DEF_RESOLUTION, t_Int},
111 ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
114 ModStruct planet_description =
115 {"planet", "init_planet", "draw_planet", "release_planet",
116 "draw_planet", "init_planet", NULL, &planet_opts,
117 1000, 1, 2, 1, 4, 1.0, "",
118 "Animates texture mapped sphere (planet)", 0, NULL};
122 __extension__ /* don't warn about "string length is greater than the length
123 ISO C89 compilers are required to support" when including
124 the following XPM file... */
126 #include "../images/earth.xpm"
128 #include "xpm-ximage.h"
130 #include "gltrackball.h"
134 * slices and stacks are used in the sphere parameterization routine.
135 * more slices and stacks will increase the quality of the sphere,
136 * at the expense of rendering speed
139 #define NUM_STARS 1000
141 /* radius of the sphere- fairly arbitrary */
144 /* distance away from the sphere model */
149 /* structure for holding the planet data */
154 int screen_width, screen_height;
155 GLXContext *glx_context;
161 trackball_state *trackball;
166 static planetstruct *planets = NULL;
169 /* Set up and enable texturing on our object */
171 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
173 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
174 MI_COLORMAP (mi), xpm_data);
177 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
178 image->width, image->height, 0,
180 /* GL_UNSIGNED_BYTE, */
181 GL_UNSIGNED_INT_8_8_8_8_REV,
183 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
186 /* setup parameters for texturing */
187 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
188 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
189 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
190 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
191 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
192 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
197 setup_file_texture (ModeInfo *mi, char *filename)
199 Display *dpy = mi->dpy;
200 Visual *visual = mi->xgwa.visual;
203 Colormap cmap = mi->xgwa.colormap;
204 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
207 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
208 image->width, image->height, 0,
210 /* GL_UNSIGNED_BYTE, */
211 GL_UNSIGNED_INT_8_8_8_8_REV,
213 sprintf (buf, "texture: %.100s (%dx%d)",
214 filename, image->width, image->height);
217 /* setup parameters for texturing */
218 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
219 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
221 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
222 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
223 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
224 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
225 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
230 setup_texture(ModeInfo * mi)
232 /* planetstruct *gp = &planets[MI_SCREEN(mi)];*/
234 glEnable(GL_TEXTURE_2D);
238 !strcmp(which_image, "BUILTIN"))
239 setup_xpm_texture (mi, earth_xpm);
241 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)
256 GLfloat max_size = 3;
258 int steps = max_size / inc;
259 int width = MI_WIDTH(mi);
260 int height = MI_HEIGHT(mi);
262 planetstruct *gp = &planets[MI_SCREEN(mi)];
263 Bool wire = MI_IS_WIREFRAME(mi);
266 glEnable (GL_POINT_SMOOTH);
268 gp->starlist = glGenLists(1);
269 glNewList(gp->starlist, GL_COMPILE);
271 glScalef (1.0/width, 1.0/height, 1);
273 for (j = 1; j <= steps; j++)
275 glPointSize(inc * j);
277 for (i = 0 ; i < NUM_STARS / steps; i++)
279 glColor3f (0.6 + frand(0.3),
282 glVertex2f ((GLfloat) (random() % width),
283 (GLfloat) (random() % height));
289 check_gl_error("stars initialization");
294 draw_stars (ModeInfo * mi)
296 int width = MI_WIDTH(mi);
297 int height = MI_HEIGHT(mi);
299 planetstruct *gp = &planets[MI_SCREEN(mi)];
301 /* Sadly, this causes a stall of the graphics pipeline (as would the
302 equivalent calls to glGet*.) But there's no way around this, short
303 of having each caller set up the specific display matrix we need
304 here, which would kind of defeat the purpose of centralizing this
307 glPushAttrib(GL_TRANSFORM_BIT | /* for matrix contents */
308 GL_ENABLE_BIT | /* for various glDisable calls */
309 GL_CURRENT_BIT | /* for glColor3f() */
310 GL_LIST_BIT); /* for glListBase() */
312 check_gl_error ("glPushAttrib");
314 /* disable lighting and texturing when drawing stars!
315 (glPopAttrib() restores these.)
317 glDisable(GL_TEXTURE_2D);
318 glDisable(GL_LIGHTING);
319 glDisable(GL_DEPTH_TEST);
321 /* glPopAttrib() does not restore matrix changes, so we must
322 push/pop the matrix stacks to be non-intrusive there.
324 glMatrixMode(GL_PROJECTION);
327 check_gl_error ("glPushMatrix");
330 /* Each matrix mode has its own stack, so we need to push/pop
332 glMatrixMode(GL_MODELVIEW);
335 check_gl_error ("glPushMatrix");
338 gluOrtho2D (0, width, 0, height);
339 check_gl_error ("gluOrtho2D");
342 glScalef (width, height, 1);
343 glCallList(gp->starlist);
344 check_gl_error ("drawing stars");
348 glMatrixMode(GL_PROJECTION);
352 /* clean up after our state changes */
354 check_gl_error ("glPopAttrib");
359 /* Set up lighting */
361 init_sun (ModeInfo * mi)
363 planetstruct *gp = &planets[MI_SCREEN(mi)];
365 GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
366 GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
367 GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
369 GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
370 GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
371 GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
375 double h = 0.1 + frand(0.8); /* east-west position - screen-side. */
376 double v = -0.3 + frand(0.6); /* north-south position */
378 if (h > 0.3 && h < 0.8) /* avoid having the sun at the camera */
379 h += (h > 0.5 ? 0.2 : -0.2);
381 gp->sunpos[0] = cos(h * M_PI);
382 gp->sunpos[1] = sin(h * M_PI);
383 gp->sunpos[2] = sin(v * M_PI);
384 gp->sunpos[3] = 0.00;
387 glEnable(GL_LIGHTING);
390 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
391 glLightfv (GL_LIGHT0, GL_AMBIENT, lamb);
392 glLightfv (GL_LIGHT0, GL_DIFFUSE, ldif);
393 glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
395 glMaterialfv (GL_FRONT, GL_AMBIENT, mamb);
396 glMaterialfv (GL_FRONT, GL_DIFFUSE, mdif);
397 glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
398 glMaterialf (GL_FRONT, GL_SHININESS, shiny);
402 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
403 glShadeModel(GL_SMOOTH);
405 check_gl_error("lighting");
409 #define RANDSIGN() ((random() & 1) ? 1 : -1)
412 reshape_planet (ModeInfo *mi, int width, int height)
414 GLfloat h = (GLfloat) height / (GLfloat) width;
416 glViewport(0, 0, (GLint) width, (GLint) height);
417 glMatrixMode(GL_PROJECTION);
419 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
420 glMatrixMode(GL_MODELVIEW);
422 glTranslatef(0.0, 0.0, -DIST);
424 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
429 planet_handle_event (ModeInfo *mi, XEvent *event)
431 planetstruct *gp = &planets[MI_SCREEN(mi)];
433 if (event->xany.type == ButtonPress &&
434 event->xbutton.button == Button1)
436 gp->button_down_p = True;
437 gltrackball_start (gp->trackball,
438 event->xbutton.x, event->xbutton.y,
439 MI_WIDTH (mi), MI_HEIGHT (mi));
442 else if (event->xany.type == ButtonRelease &&
443 event->xbutton.button == Button1)
445 gp->button_down_p = False;
448 else if (event->xany.type == ButtonPress &&
449 (event->xbutton.button == Button4 ||
450 event->xbutton.button == Button5 ||
451 event->xbutton.button == Button6 ||
452 event->xbutton.button == Button7))
454 gltrackball_mousewheel (gp->trackball, event->xbutton.button, 10,
455 !!event->xbutton.state);
458 else if (event->xany.type == MotionNotify &&
461 gltrackball_track (gp->trackball,
462 event->xmotion.x, event->xmotion.y,
463 MI_WIDTH (mi), MI_HEIGHT (mi));
472 init_planet (ModeInfo * mi)
475 int screen = MI_SCREEN(mi);
476 Bool wire = MI_IS_WIREFRAME(mi);
478 if (planets == NULL) {
479 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
480 sizeof (planetstruct))) == NULL)
483 gp = &planets[screen];
485 if ((gp->glx_context = init_GL(mi)) != NULL) {
486 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
490 char *f = get_string_resource(mi->dpy, "imageForeground", "Foreground");
491 char *b = get_string_resource(mi->dpy, "imageBackground", "Background");
493 if (!f) f = strdup("white");
494 if (!b) b = strdup("black");
496 for (s = f + strlen(f)-1; s > f; s--)
497 if (*s == ' ' || *s == '\t')
499 for (s = b + strlen(b)-1; s > b; s--)
500 if (*s == ' ' || *s == '\t')
503 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
505 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
508 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
510 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
519 double spin_speed = 0.5;
520 double wander_speed = 0.02;
521 gp->rot = make_rotator (do_roll ? spin_speed : 0,
522 do_roll ? spin_speed : 0,
524 do_wander ? wander_speed : 0,
527 gp->trackball = gltrackball_init ();
534 glEnable (GL_LINE_SMOOTH);
546 glEnable(GL_DEPTH_TEST);
547 glEnable(GL_CULL_FACE);
550 /* construct the polygons of the planet
552 gp->platelist = glGenLists(1);
553 glNewList (gp->platelist, GL_COMPILE);
556 glScalef (RADIUS, RADIUS, RADIUS);
557 glRotatef (90, 1, 0, 0);
558 unit_sphere (resolution, resolution, wire);
559 mi->polygon_count += resolution*resolution;
563 /* construct the polygons of the latitude/longitude/axis lines.
565 gp->latlonglist = glGenLists(1);
566 glNewList (gp->latlonglist, GL_COMPILE);
568 if (do_texture) glDisable (GL_TEXTURE_2D);
569 if (do_light) glDisable (GL_LIGHTING);
570 glColor3f (0.1, 0.3, 0.1);
571 glScalef (RADIUS, RADIUS, RADIUS);
572 glScalef (1.01, 1.01, 1.01);
573 glRotatef (90, 1, 0, 0);
574 unit_sphere (12, 24, 1);
576 glVertex3f(0, -2, 0);
579 if (do_light) glEnable(GL_LIGHTING);
580 if (do_texture) glEnable(GL_TEXTURE_2D);
586 draw_planet (ModeInfo * mi)
588 planetstruct *gp = &planets[MI_SCREEN(mi)];
589 Display *display = MI_DISPLAY(mi);
590 Window window = MI_WINDOW(mi);
593 if (!gp->glx_context)
596 glDrawBuffer(GL_BACK);
597 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
599 glXMakeCurrent (display, window, *(gp->glx_context));
606 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
607 glTranslatef((x - 0.5) * 15,
611 gltrackball_rotate (gp->trackball);
613 glRotatef (90,1,0,0);
617 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
618 glRotatef (x * 360, 1.0, 0.0, 0.0);
619 glRotatef (y * 360, 0.0, 1.0, 0.0);
622 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
624 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
625 if (do_rotate && !gp->button_down_p)
627 gp->z -= 0.005; /* the sun sets in the west */
628 if (gp->z < 0) gp->z += 1;
631 glCallList (gp->platelist);
632 if (gp->button_down_p)
633 glCallList (gp->latlonglist);
636 if (mi->fps_p) do_fps (mi);
638 glXSwapBuffers(display, window);
643 release_planet (ModeInfo * mi)
645 if (planets != NULL) {
648 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
649 planetstruct *gp = &planets[screen];
651 if (gp->glx_context) {
652 /* Display lists MUST be freed while their glXContext is current. */
653 /* but this gets a BadMatch error. -jwz */
654 /*glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));*/
656 if (glIsList(gp->platelist))
657 glDeleteLists(gp->platelist, 1);
658 if (glIsList(gp->starlist))
659 glDeleteLists(gp->starlist, 1);
662 (void) free((void *) planets);
669 XSCREENSAVER_MODULE_2 ("GLPlanet", glplanet, planet)