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 * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
40 * otherwise caddr_t is not defined correctly
43 #include <X11/Intrinsic.h>
46 # define PROGCLASS "Planet"
47 # define HACK_INIT init_planet
48 # define HACK_DRAW draw_planet
49 # define HACK_RESHAPE reshape_planet
50 # define HACK_HANDLE_EVENT planet_handle_event
51 # define EVENT_MASK PointerMotionMask
52 # define planet_opts xlockmore_opts
53 #define DEFAULTS "*delay: 15000 \n" \
54 "*showFPS: False \n" \
58 "*wireframe: False \n" \
62 "*image: BUILTIN \n" \
63 "*imageForeground: Green \n" \
64 "*imageBackground: Blue \n"
66 # include "xlockmore.h" /* from the xscreensaver distribution */
67 #else /* !STANDALONE */
68 # include "xlock.h" /* from the xlockmore distribution */
69 #endif /* !STANDALONE */
71 #ifdef USE_GL /* whole file */
77 # include <X11/Xmu/Drawing.h>
79 # include <Xmu/Drawing.h>
86 #define DEF_ROTATE "True"
87 #define DEF_ROLL "True"
88 #define DEF_WANDER "True"
89 #define DEF_TEXTURE "True"
90 #define DEF_STARS "True"
91 #define DEF_LIGHT "True"
92 #define DEF_RESOLUTION "64"
93 #define DEF_IMAGE "BUILTIN"
96 #define countof(x) (sizeof((x))/sizeof((*x)))
100 static int do_wander;
101 static int do_texture;
104 static char *which_image;
105 static int resolution;
107 static XrmOptionDescRec opts[] = {
108 {"-rotate", ".glplanet.rotate", XrmoptionNoArg, (caddr_t) "true" },
109 {"+rotate", ".glplanet.rotate", XrmoptionNoArg, (caddr_t) "false" },
110 {"-roll", ".glplanet.roll", XrmoptionNoArg, (caddr_t) "true" },
111 {"+roll", ".glplanet.roll", XrmoptionNoArg, (caddr_t) "false" },
112 {"-wander", ".glplanet.wander", XrmoptionNoArg, (caddr_t) "true" },
113 {"+wander", ".glplanet.wander", XrmoptionNoArg, (caddr_t) "false" },
114 {"-texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "true" },
115 {"+texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "false" },
116 {"-stars", ".glplanet.stars", XrmoptionNoArg, (caddr_t) "true" },
117 {"+stars", ".glplanet.stars", XrmoptionNoArg, (caddr_t) "false" },
118 {"-light", ".glplanet.light", XrmoptionNoArg, (caddr_t) "true" },
119 {"+light", ".glplanet.light", XrmoptionNoArg, (caddr_t) "false" },
120 {"-image", ".glplanet.image", XrmoptionSepArg, (caddr_t) 0 },
121 {"-resolution", ".glplanet.resolution", XrmoptionSepArg, (caddr_t) 0 },
124 static argtype vars[] = {
125 {(caddr_t *) &do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
126 {(caddr_t *) &do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
127 {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
128 {(caddr_t *) &do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
129 {(caddr_t *) &do_stars, "stars", "Stars", DEF_STARS, t_Bool},
130 {(caddr_t *) &do_light, "light", "Light", DEF_LIGHT, t_Bool},
131 {(caddr_t *) &which_image, "image", "Image", DEF_IMAGE, t_String},
132 {(caddr_t *) &resolution, "resolution","Resolution", DEF_RESOLUTION, t_Int},
135 ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
138 ModStruct planet_description =
139 {"planet", "init_planet", "draw_planet", "release_planet",
140 "draw_planet", "init_planet", NULL, &planet_opts,
141 1000, 1, 2, 1, 4, 1.0, "",
142 "Animates texture mapped sphere (planet)", 0, NULL};
145 #include "../images/earth.xpm"
146 #include "xpm-ximage.h"
148 #include "gltrackball.h"
152 * slices and stacks are used in the sphere parameterization routine.
153 * more slices and stacks will increase the quality of the sphere,
154 * at the expense of rendering speed
157 #define NUM_STARS 1000
159 /* radius of the sphere- fairly arbitrary */
162 /* distance away from the sphere model */
167 /* structure for holding the planet data */
172 int screen_width, screen_height;
173 GLXContext *glx_context;
179 trackball_state *trackball;
184 static planetstruct *planets = NULL;
188 normalize(GLfloat v[3])
190 GLfloat d = (GLfloat) sqrt((double) (v[0] * v[0] +
201 v[0] = v[1] = v[2] = 0;
206 /* Set up and enable texturing on our object */
208 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
210 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
211 MI_COLORMAP (mi), xpm_data);
214 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
215 image->width, image->height, 0,
217 /* GL_UNSIGNED_BYTE, */
218 GL_UNSIGNED_INT_8_8_8_8_REV,
220 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
223 /* setup parameters for texturing */
224 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
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);
234 setup_file_texture (ModeInfo *mi, char *filename)
236 Display *dpy = mi->dpy;
237 Visual *visual = mi->xgwa.visual;
240 Colormap cmap = mi->xgwa.colormap;
241 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
244 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
245 image->width, image->height, 0,
247 /* GL_UNSIGNED_BYTE, */
248 GL_UNSIGNED_INT_8_8_8_8_REV,
250 sprintf (buf, "texture: %.100s (%dx%d)",
251 filename, image->width, image->height);
254 /* setup parameters for texturing */
255 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
256 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
258 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
259 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
260 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
261 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
262 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
267 setup_texture(ModeInfo * mi)
269 /* planetstruct *gp = &planets[MI_SCREEN(mi)];*/
271 glEnable(GL_TEXTURE_2D);
275 !strcmp(which_image, "BUILTIN"))
276 setup_xpm_texture (mi, earth_xpm);
278 setup_file_texture (mi, which_image);
280 check_gl_error("texture initialization");
282 /* Need to flip the texture top for bottom for some reason. */
283 glMatrixMode (GL_TEXTURE);
285 glMatrixMode (GL_MODELVIEW);
290 init_stars (ModeInfo *mi)
293 GLfloat max_size = 3;
295 int steps = max_size / inc;
296 int width = MI_WIDTH(mi);
297 int height = MI_HEIGHT(mi);
299 planetstruct *gp = &planets[MI_SCREEN(mi)];
300 Bool wire = MI_IS_WIREFRAME(mi);
303 glEnable (GL_POINT_SMOOTH);
305 gp->starlist = glGenLists(1);
306 glNewList(gp->starlist, GL_COMPILE);
308 glScalef (1.0/width, 1.0/height, 1);
310 for (j = 1; j <= steps; j++)
312 glPointSize(inc * j);
314 for (i = 0 ; i < NUM_STARS / steps; i++)
316 glColor3f (0.6 + frand(0.3),
319 glVertex2f ((GLfloat) (random() % width),
320 (GLfloat) (random() % height));
326 check_gl_error("stars initialization");
331 draw_stars (ModeInfo * mi)
333 int width = MI_WIDTH(mi);
334 int height = MI_HEIGHT(mi);
336 planetstruct *gp = &planets[MI_SCREEN(mi)];
338 /* Sadly, this causes a stall of the graphics pipeline (as would the
339 equivalent calls to glGet*.) But there's no way around this, short
340 of having each caller set up the specific display matrix we need
341 here, which would kind of defeat the purpose of centralizing this
344 glPushAttrib(GL_TRANSFORM_BIT | /* for matrix contents */
345 GL_ENABLE_BIT | /* for various glDisable calls */
346 GL_CURRENT_BIT | /* for glColor3f() */
347 GL_LIST_BIT); /* for glListBase() */
349 check_gl_error ("glPushAttrib");
351 /* disable lighting and texturing when drawing stars!
352 (glPopAttrib() restores these.)
354 glDisable(GL_TEXTURE_2D);
355 glDisable(GL_LIGHTING);
356 glDisable(GL_DEPTH_TEST);
358 /* glPopAttrib() does not restore matrix changes, so we must
359 push/pop the matrix stacks to be non-intrusive there.
361 glMatrixMode(GL_PROJECTION);
364 check_gl_error ("glPushMatrix");
367 /* Each matrix mode has its own stack, so we need to push/pop
369 glMatrixMode(GL_MODELVIEW);
372 check_gl_error ("glPushMatrix");
375 gluOrtho2D (0, width, 0, height);
376 check_gl_error ("gluOrtho2D");
379 glScalef (width, height, 1);
380 glCallList(gp->starlist);
381 check_gl_error ("drawing stars");
385 glMatrixMode(GL_PROJECTION);
389 /* clean up after our state changes */
391 check_gl_error ("glPopAttrib");
396 /* Set up lighting */
398 init_sun (ModeInfo * mi)
400 planetstruct *gp = &planets[MI_SCREEN(mi)];
402 GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
403 GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
404 GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
406 GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
407 GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
408 GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
412 double h = 0.1 + frand(0.8); /* east-west position - screen-side. */
413 double v = -0.3 + frand(0.6); /* north-south position */
415 if (h > 0.3 && h < 0.8) /* avoid having the sun at the camera */
416 h += (h > 0.5 ? 0.2 : -0.2);
418 gp->sunpos[0] = cos(h * M_PI);
419 gp->sunpos[1] = sin(h * M_PI);
420 gp->sunpos[2] = sin(v * M_PI);
421 gp->sunpos[3] = 0.00;
424 glEnable(GL_LIGHTING);
427 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
428 glLightfv (GL_LIGHT0, GL_AMBIENT, lamb);
429 glLightfv (GL_LIGHT0, GL_DIFFUSE, ldif);
430 glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
432 glMaterialfv (GL_FRONT, GL_AMBIENT, mamb);
433 glMaterialfv (GL_FRONT, GL_DIFFUSE, mdif);
434 glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
435 glMaterialf (GL_FRONT, GL_SHININESS, shiny);
439 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
440 glShadeModel(GL_SMOOTH);
442 check_gl_error("lighting");
446 #define RANDSIGN() ((random() & 1) ? 1 : -1)
449 reshape_planet (ModeInfo *mi, int width, int height)
451 GLfloat h = (GLfloat) height / (GLfloat) width;
453 glViewport(0, 0, (GLint) width, (GLint) height);
454 glMatrixMode(GL_PROJECTION);
456 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
457 glMatrixMode(GL_MODELVIEW);
459 glTranslatef(0.0, 0.0, -DIST);
461 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
466 planet_handle_event (ModeInfo *mi, XEvent *event)
468 planetstruct *gp = &planets[MI_SCREEN(mi)];
470 if (event->xany.type == ButtonPress &&
471 event->xbutton.button & Button1)
473 gp->button_down_p = True;
474 gltrackball_start (gp->trackball,
475 event->xbutton.x, event->xbutton.y,
476 MI_WIDTH (mi), MI_HEIGHT (mi));
479 else if (event->xany.type == ButtonRelease &&
480 event->xbutton.button & Button1)
482 gp->button_down_p = False;
485 else if (event->xany.type == MotionNotify &&
488 gltrackball_track (gp->trackball,
489 event->xmotion.x, event->xmotion.y,
490 MI_WIDTH (mi), MI_HEIGHT (mi));
499 init_planet (ModeInfo * mi)
502 int screen = MI_SCREEN(mi);
503 Bool wire = MI_IS_WIREFRAME(mi);
505 if (planets == NULL) {
506 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
507 sizeof (planetstruct))) == NULL)
510 gp = &planets[screen];
512 if ((gp->glx_context = init_GL(mi)) != NULL) {
513 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
517 char *f = get_string_resource("imageForeground", "Foreground");
518 char *b = get_string_resource("imageBackground", "Background");
520 if (!f) f = strdup("white");
521 if (!b) b = strdup("black");
523 for (s = f + strlen(f)-1; s > f; s--)
524 if (*s == ' ' || *s == '\t')
526 for (s = b + strlen(b)-1; s > b; s--)
527 if (*s == ' ' || *s == '\t')
530 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
532 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
535 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
537 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
546 double spin_speed = 1.0;
547 double wander_speed = 0.05;
548 gp->rot = make_rotator (do_roll ? spin_speed : 0,
549 do_roll ? spin_speed : 0,
551 do_wander ? wander_speed : 0,
554 gp->trackball = gltrackball_init ();
561 glEnable (GL_LINE_SMOOTH);
573 glEnable(GL_DEPTH_TEST);
574 glEnable(GL_CULL_FACE);
577 /* construct the polygons of the planet
579 gp->platelist = glGenLists(1);
580 glNewList (gp->platelist, GL_COMPILE);
583 glScalef (RADIUS, RADIUS, RADIUS);
584 glRotatef (90, 1, 0, 0);
585 unit_sphere (resolution, resolution, wire);
586 mi->polygon_count += resolution*resolution;
590 /* construct the polygons of the latitude/longitude/axis lines.
592 gp->latlonglist = glGenLists(1);
593 glNewList (gp->latlonglist, GL_COMPILE);
595 if (do_texture) glDisable (GL_TEXTURE_2D);
596 if (do_light) glDisable (GL_LIGHTING);
597 glColor3f (0.1, 0.3, 0.1);
598 glScalef (RADIUS, RADIUS, RADIUS);
599 glScalef (1.01, 1.01, 1.01);
600 glRotatef (90, 1, 0, 0);
601 unit_sphere (12, 24, 1);
603 glVertex3f(0, -2, 0);
606 if (do_light) glEnable(GL_LIGHTING);
607 if (do_texture) glEnable(GL_TEXTURE_2D);
613 draw_planet (ModeInfo * mi)
615 planetstruct *gp = &planets[MI_SCREEN(mi)];
616 Display *display = MI_DISPLAY(mi);
617 Window window = MI_WINDOW(mi);
620 if (!gp->glx_context)
623 glDrawBuffer(GL_BACK);
624 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
626 glXMakeCurrent (display, window, *(gp->glx_context));
633 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
634 glTranslatef((x - 0.5) * 15,
638 gltrackball_rotate (gp->trackball);
640 glRotatef (90,1,0,0);
644 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
645 glRotatef (x * 360, 1.0, 0.0, 0.0);
646 glRotatef (y * 360, 0.0, 1.0, 0.0);
649 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
651 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
652 if (do_rotate && !gp->button_down_p)
654 gp->z -= 0.01; /* the sun sets in the west */
655 if (gp->z < 0) gp->z += 1;
658 glCallList (gp->platelist);
659 if (gp->button_down_p)
660 glCallList (gp->latlonglist);
663 if (mi->fps_p) do_fps (mi);
665 glXSwapBuffers(display, window);
670 release_planet (ModeInfo * mi)
672 if (planets != NULL) {
675 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
676 planetstruct *gp = &planets[screen];
678 if (gp->glx_context) {
679 /* Display lists MUST be freed while their glXContext is current. */
680 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
682 if (glIsList(gp->platelist))
683 glDeleteLists(gp->platelist, 1);
684 if (glIsList(gp->starlist))
685 glDeleteLists(gp->starlist, 1);
688 (void) free((void *) planets);