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))
452 gltrackball_mousewheel (gp->trackball, event->xbutton.button, 10,
453 !!event->xbutton.state);
456 else if (event->xany.type == MotionNotify &&
459 gltrackball_track (gp->trackball,
460 event->xmotion.x, event->xmotion.y,
461 MI_WIDTH (mi), MI_HEIGHT (mi));
470 init_planet (ModeInfo * mi)
473 int screen = MI_SCREEN(mi);
474 Bool wire = MI_IS_WIREFRAME(mi);
476 if (planets == NULL) {
477 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
478 sizeof (planetstruct))) == NULL)
481 gp = &planets[screen];
483 if ((gp->glx_context = init_GL(mi)) != NULL) {
484 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
488 char *f = get_string_resource(mi->dpy, "imageForeground", "Foreground");
489 char *b = get_string_resource(mi->dpy, "imageBackground", "Background");
491 if (!f) f = strdup("white");
492 if (!b) b = strdup("black");
494 for (s = f + strlen(f)-1; s > f; s--)
495 if (*s == ' ' || *s == '\t')
497 for (s = b + strlen(b)-1; s > b; s--)
498 if (*s == ' ' || *s == '\t')
501 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
503 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
506 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
508 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
517 double spin_speed = 0.5;
518 double wander_speed = 0.02;
519 gp->rot = make_rotator (do_roll ? spin_speed : 0,
520 do_roll ? spin_speed : 0,
522 do_wander ? wander_speed : 0,
525 gp->trackball = gltrackball_init ();
532 glEnable (GL_LINE_SMOOTH);
544 glEnable(GL_DEPTH_TEST);
545 glEnable(GL_CULL_FACE);
548 /* construct the polygons of the planet
550 gp->platelist = glGenLists(1);
551 glNewList (gp->platelist, GL_COMPILE);
554 glScalef (RADIUS, RADIUS, RADIUS);
555 glRotatef (90, 1, 0, 0);
556 unit_sphere (resolution, resolution, wire);
557 mi->polygon_count += resolution*resolution;
561 /* construct the polygons of the latitude/longitude/axis lines.
563 gp->latlonglist = glGenLists(1);
564 glNewList (gp->latlonglist, GL_COMPILE);
566 if (do_texture) glDisable (GL_TEXTURE_2D);
567 if (do_light) glDisable (GL_LIGHTING);
568 glColor3f (0.1, 0.3, 0.1);
569 glScalef (RADIUS, RADIUS, RADIUS);
570 glScalef (1.01, 1.01, 1.01);
571 glRotatef (90, 1, 0, 0);
572 unit_sphere (12, 24, 1);
574 glVertex3f(0, -2, 0);
577 if (do_light) glEnable(GL_LIGHTING);
578 if (do_texture) glEnable(GL_TEXTURE_2D);
584 draw_planet (ModeInfo * mi)
586 planetstruct *gp = &planets[MI_SCREEN(mi)];
587 Display *display = MI_DISPLAY(mi);
588 Window window = MI_WINDOW(mi);
591 if (!gp->glx_context)
594 glDrawBuffer(GL_BACK);
595 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
597 glXMakeCurrent (display, window, *(gp->glx_context));
604 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
605 glTranslatef((x - 0.5) * 15,
609 gltrackball_rotate (gp->trackball);
611 glRotatef (90,1,0,0);
615 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
616 glRotatef (x * 360, 1.0, 0.0, 0.0);
617 glRotatef (y * 360, 0.0, 1.0, 0.0);
620 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
622 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
623 if (do_rotate && !gp->button_down_p)
625 gp->z -= 0.005; /* the sun sets in the west */
626 if (gp->z < 0) gp->z += 1;
629 glCallList (gp->platelist);
630 if (gp->button_down_p)
631 glCallList (gp->latlonglist);
634 if (mi->fps_p) do_fps (mi);
636 glXSwapBuffers(display, window);
641 release_planet (ModeInfo * mi)
643 if (planets != NULL) {
646 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
647 planetstruct *gp = &planets[screen];
649 if (gp->glx_context) {
650 /* Display lists MUST be freed while their glXContext is current. */
651 /* but this gets a BadMatch error. -jwz */
652 /*glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));*/
654 if (glIsList(gp->platelist))
655 glDeleteLists(gp->platelist, 1);
656 if (glIsList(gp->starlist))
657 glDeleteLists(gp->starlist, 1);
660 (void) free((void *) planets);
667 XSCREENSAVER_MODULE_2 ("GLPlanet", glplanet, planet)