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,
216 GL_RGBA, GL_UNSIGNED_BYTE, image->data);
217 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
220 /* setup parameters for texturing */
221 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
222 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
223 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
224 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
225 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
226 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
231 setup_file_texture (ModeInfo *mi, char *filename)
233 Display *dpy = mi->dpy;
234 Visual *visual = mi->xgwa.visual;
237 Colormap cmap = mi->xgwa.colormap;
238 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
241 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
242 image->width, image->height, 0,
243 GL_RGBA, GL_UNSIGNED_BYTE, image->data);
244 sprintf (buf, "texture: %.100s (%dx%d)",
245 filename, image->width, image->height);
248 /* setup parameters for texturing */
249 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
250 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
252 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
253 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
254 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
255 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
256 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
261 setup_texture(ModeInfo * mi)
263 /* planetstruct *gp = &planets[MI_SCREEN(mi)];*/
265 glEnable(GL_TEXTURE_2D);
269 !strcmp(which_image, "BUILTIN"))
270 setup_xpm_texture (mi, earth_xpm);
272 setup_file_texture (mi, which_image);
274 check_gl_error("texture initialization");
276 /* Need to flip the texture top for bottom for some reason. */
277 glMatrixMode (GL_TEXTURE);
279 glMatrixMode (GL_MODELVIEW);
284 init_stars (ModeInfo *mi)
287 GLfloat max_size = 3;
289 int steps = max_size / inc;
290 int width = MI_WIDTH(mi);
291 int height = MI_HEIGHT(mi);
293 planetstruct *gp = &planets[MI_SCREEN(mi)];
294 Bool wire = MI_IS_WIREFRAME(mi);
297 glEnable (GL_POINT_SMOOTH);
299 gp->starlist = glGenLists(1);
300 glNewList(gp->starlist, GL_COMPILE);
302 glScalef (1.0/width, 1.0/height, 1);
304 for (j = 1; j <= steps; j++)
306 glPointSize(inc * j);
308 for (i = 0 ; i < NUM_STARS / steps; i++)
310 glColor3f (0.6 + frand(0.3),
313 glVertex2f ((GLfloat) (random() % width),
314 (GLfloat) (random() % height));
320 check_gl_error("stars initialization");
325 draw_stars (ModeInfo * mi)
327 int width = MI_WIDTH(mi);
328 int height = MI_HEIGHT(mi);
330 planetstruct *gp = &planets[MI_SCREEN(mi)];
332 /* Sadly, this causes a stall of the graphics pipeline (as would the
333 equivalent calls to glGet*.) But there's no way around this, short
334 of having each caller set up the specific display matrix we need
335 here, which would kind of defeat the purpose of centralizing this
338 glPushAttrib(GL_TRANSFORM_BIT | /* for matrix contents */
339 GL_ENABLE_BIT | /* for various glDisable calls */
340 GL_CURRENT_BIT | /* for glColor3f() */
341 GL_LIST_BIT); /* for glListBase() */
343 check_gl_error ("glPushAttrib");
345 /* disable lighting and texturing when drawing stars!
346 (glPopAttrib() restores these.)
348 glDisable(GL_TEXTURE_2D);
349 glDisable(GL_LIGHTING);
350 glDisable(GL_DEPTH_TEST);
352 /* glPopAttrib() does not restore matrix changes, so we must
353 push/pop the matrix stacks to be non-intrusive there.
355 glMatrixMode(GL_PROJECTION);
358 check_gl_error ("glPushMatrix");
361 /* Each matrix mode has its own stack, so we need to push/pop
363 glMatrixMode(GL_MODELVIEW);
366 check_gl_error ("glPushMatrix");
369 gluOrtho2D (0, width, 0, height);
370 check_gl_error ("gluOrtho2D");
373 glScalef (width, height, 1);
374 glCallList(gp->starlist);
375 check_gl_error ("drawing stars");
379 glMatrixMode(GL_PROJECTION);
383 /* clean up after our state changes */
385 check_gl_error ("glPopAttrib");
390 /* Set up lighting */
392 init_sun (ModeInfo * mi)
394 planetstruct *gp = &planets[MI_SCREEN(mi)];
396 GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
397 GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
398 GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
400 GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
401 GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
402 GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
406 double h = 0.1 + frand(0.8); /* east-west position - screen-side. */
407 double v = -0.3 + frand(0.6); /* north-south position */
409 if (h > 0.3 && h < 0.8) /* avoid having the sun at the camera */
410 h += (h > 0.5 ? 0.2 : -0.2);
412 gp->sunpos[0] = cos(h * M_PI);
413 gp->sunpos[1] = sin(h * M_PI);
414 gp->sunpos[2] = sin(v * M_PI);
415 gp->sunpos[3] = 0.00;
418 glEnable(GL_LIGHTING);
421 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
422 glLightfv (GL_LIGHT0, GL_AMBIENT, lamb);
423 glLightfv (GL_LIGHT0, GL_DIFFUSE, ldif);
424 glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
426 glMaterialfv (GL_FRONT, GL_AMBIENT, mamb);
427 glMaterialfv (GL_FRONT, GL_DIFFUSE, mdif);
428 glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
429 glMaterialf (GL_FRONT, GL_SHININESS, shiny);
433 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
434 glShadeModel(GL_SMOOTH);
436 check_gl_error("lighting");
440 #define RANDSIGN() ((random() & 1) ? 1 : -1)
443 reshape_planet (ModeInfo *mi, int width, int height)
445 GLfloat h = (GLfloat) height / (GLfloat) width;
447 glViewport(0, 0, (GLint) width, (GLint) height);
448 glMatrixMode(GL_PROJECTION);
450 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
451 glMatrixMode(GL_MODELVIEW);
453 glTranslatef(0.0, 0.0, -DIST);
455 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
460 planet_handle_event (ModeInfo *mi, XEvent *event)
462 planetstruct *gp = &planets[MI_SCREEN(mi)];
464 if (event->xany.type == ButtonPress &&
465 event->xbutton.button & Button1)
467 gp->button_down_p = True;
468 gltrackball_start (gp->trackball,
469 event->xbutton.x, event->xbutton.y,
470 MI_WIDTH (mi), MI_HEIGHT (mi));
473 else if (event->xany.type == ButtonRelease &&
474 event->xbutton.button & Button1)
476 gp->button_down_p = False;
479 else if (event->xany.type == MotionNotify &&
482 gltrackball_track (gp->trackball,
483 event->xmotion.x, event->xmotion.y,
484 MI_WIDTH (mi), MI_HEIGHT (mi));
493 init_planet (ModeInfo * mi)
496 int screen = MI_SCREEN(mi);
497 Bool wire = MI_IS_WIREFRAME(mi);
499 if (planets == NULL) {
500 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
501 sizeof (planetstruct))) == NULL)
504 gp = &planets[screen];
506 if ((gp->glx_context = init_GL(mi)) != NULL) {
507 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
511 char *f = get_string_resource("imageForeground", "Foreground");
512 char *b = get_string_resource("imageBackground", "Background");
514 if (!f) f = strdup("white");
515 if (!b) b = strdup("black");
517 for (s = f + strlen(f)-1; s > f; s--)
518 if (*s == ' ' || *s == '\t')
520 for (s = b + strlen(b)-1; s > b; s--)
521 if (*s == ' ' || *s == '\t')
524 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
526 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
529 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
531 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
540 double spin_speed = 1.0;
541 double wander_speed = 0.05;
542 gp->rot = make_rotator (do_roll ? spin_speed : 0,
543 do_roll ? spin_speed : 0,
545 do_wander ? wander_speed : 0,
548 gp->trackball = gltrackball_init ();
555 glEnable (GL_LINE_SMOOTH);
567 glEnable(GL_DEPTH_TEST);
568 glEnable(GL_CULL_FACE);
571 /* construct the polygons of the planet
573 gp->platelist = glGenLists(1);
574 glNewList (gp->platelist, GL_COMPILE);
577 glScalef (RADIUS, RADIUS, RADIUS);
578 glRotatef (90, 1, 0, 0);
579 unit_sphere (resolution, resolution, wire);
580 mi->polygon_count += resolution*resolution;
584 /* construct the polygons of the latitude/longitude/axis lines.
586 gp->latlonglist = glGenLists(1);
587 glNewList (gp->latlonglist, GL_COMPILE);
589 if (do_texture) glDisable (GL_TEXTURE_2D);
590 if (do_light) glDisable (GL_LIGHTING);
591 glColor3f (0.1, 0.3, 0.1);
592 glScalef (RADIUS, RADIUS, RADIUS);
593 glScalef (1.01, 1.01, 1.01);
594 glRotatef (90, 1, 0, 0);
595 unit_sphere (12, 24, 1);
597 glVertex3f(0, -2, 0);
600 if (do_light) glEnable(GL_LIGHTING);
601 if (do_texture) glEnable(GL_TEXTURE_2D);
607 draw_planet (ModeInfo * mi)
609 planetstruct *gp = &planets[MI_SCREEN(mi)];
610 Display *display = MI_DISPLAY(mi);
611 Window window = MI_WINDOW(mi);
614 if (!gp->glx_context)
617 glDrawBuffer(GL_BACK);
618 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
620 glXMakeCurrent (display, window, *(gp->glx_context));
627 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
628 glTranslatef((x - 0.5) * 15,
632 gltrackball_rotate (gp->trackball);
634 glRotatef (90,1,0,0);
638 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
639 glRotatef (x * 360, 1.0, 0.0, 0.0);
640 glRotatef (y * 360, 0.0, 1.0, 0.0);
643 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
645 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
646 if (do_rotate && !gp->button_down_p)
648 gp->z -= 0.01; /* the sun sets in the west */
649 if (gp->z < 0) gp->z += 1;
652 glCallList (gp->platelist);
653 if (gp->button_down_p)
654 glCallList (gp->latlonglist);
657 if (mi->fps_p) do_fps (mi);
659 glXSwapBuffers(display, window);
664 release_planet (ModeInfo * mi)
666 if (planets != NULL) {
669 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
670 planetstruct *gp = &planets[screen];
672 if (gp->glx_context) {
673 /* Display lists MUST be freed while their glXContext is current. */
674 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
676 if (glIsList(gp->platelist))
677 glDeleteLists(gp->platelist, 1);
678 if (glIsList(gp->starlist))
679 glDeleteLists(gp->starlist, 1);
682 (void) free((void *) planets);