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 */
171 int screen_width, screen_height;
172 GLXContext *glx_context;
178 trackball_state *trackball;
183 static planetstruct *planets = NULL;
187 normalize(GLfloat v[3])
189 GLfloat d = (GLfloat) sqrt((double) (v[0] * v[0] +
200 v[0] = v[1] = v[2] = 0;
205 /* Set up and enable texturing on our object */
207 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
209 XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
210 MI_COLORMAP (mi), xpm_data);
213 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
214 image->width, image->height, 0,
215 GL_RGBA, GL_UNSIGNED_BYTE, image->data);
216 sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
219 /* setup parameters for texturing */
220 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
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_file_texture (ModeInfo *mi, char *filename)
232 Display *dpy = mi->dpy;
233 Visual *visual = mi->xgwa.visual;
236 Colormap cmap = mi->xgwa.colormap;
237 XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
240 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
241 image->width, image->height, 0,
242 GL_RGBA, GL_UNSIGNED_BYTE, image->data);
243 sprintf (buf, "texture: %.100s (%dx%d)",
244 filename, image->width, image->height);
247 /* setup parameters for texturing */
248 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
249 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
251 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
252 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
253 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
254 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
255 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
260 setup_texture(ModeInfo * mi)
262 /* planetstruct *gp = &planets[MI_SCREEN(mi)];*/
264 glEnable(GL_TEXTURE_2D);
268 !strcmp(which_image, "BUILTIN"))
269 setup_xpm_texture (mi, earth_xpm);
271 setup_file_texture (mi, which_image);
273 check_gl_error("texture initialization");
275 /* Need to flip the texture top for bottom for some reason. */
276 glMatrixMode (GL_TEXTURE);
278 glMatrixMode (GL_MODELVIEW);
283 init_stars (ModeInfo *mi)
286 GLfloat max_size = 3;
288 int steps = max_size / inc;
289 int width = MI_WIDTH(mi);
290 int height = MI_HEIGHT(mi);
292 planetstruct *gp = &planets[MI_SCREEN(mi)];
293 Bool wire = MI_IS_WIREFRAME(mi);
296 glEnable (GL_POINT_SMOOTH);
298 gp->starlist = glGenLists(1);
299 glNewList(gp->starlist, GL_COMPILE);
301 glScalef (1.0/width, 1.0/height, 1);
303 for (j = 1; j <= steps; j++)
305 glPointSize(inc * j);
307 for (i = 0 ; i < NUM_STARS / steps; i++)
309 glColor3f (0.6 + frand(0.3),
312 glVertex2f ((GLfloat) (random() % width),
313 (GLfloat) (random() % height));
319 check_gl_error("stars initialization");
324 draw_stars (ModeInfo * mi)
326 int width = MI_WIDTH(mi);
327 int height = MI_HEIGHT(mi);
329 planetstruct *gp = &planets[MI_SCREEN(mi)];
331 /* Sadly, this causes a stall of the graphics pipeline (as would the
332 equivalent calls to glGet*.) But there's no way around this, short
333 of having each caller set up the specific display matrix we need
334 here, which would kind of defeat the purpose of centralizing this
337 glPushAttrib(GL_TRANSFORM_BIT | /* for matrix contents */
338 GL_ENABLE_BIT | /* for various glDisable calls */
339 GL_CURRENT_BIT | /* for glColor3f() */
340 GL_LIST_BIT); /* for glListBase() */
342 check_gl_error ("glPushAttrib");
344 /* disable lighting and texturing when drawing stars!
345 (glPopAttrib() restores these.)
347 glDisable(GL_TEXTURE_2D);
348 glDisable(GL_LIGHTING);
349 glDisable(GL_DEPTH_TEST);
351 /* glPopAttrib() does not restore matrix changes, so we must
352 push/pop the matrix stacks to be non-intrusive there.
354 glMatrixMode(GL_PROJECTION);
357 check_gl_error ("glPushMatrix");
360 /* Each matrix mode has its own stack, so we need to push/pop
362 glMatrixMode(GL_MODELVIEW);
365 check_gl_error ("glPushMatrix");
368 gluOrtho2D (0, width, 0, height);
369 check_gl_error ("gluOrtho2D");
372 glScalef (width, height, 1);
373 glCallList(gp->starlist);
374 check_gl_error ("drawing stars");
378 glMatrixMode(GL_PROJECTION);
382 /* clean up after our state changes */
384 check_gl_error ("glPopAttrib");
389 /* Set up lighting */
391 init_sun (ModeInfo * mi)
393 planetstruct *gp = &planets[MI_SCREEN(mi)];
395 GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
396 GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
397 GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
399 GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
400 GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
401 GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
405 double h = 0.1 + frand(0.8); /* east-west position - screen-side. */
406 double v = -0.3 + frand(0.6); /* north-south position */
408 if (h > 0.3 && h < 0.8) /* avoid having the sun at the camera */
409 h += (h > 0.5 ? 0.2 : -0.2);
411 gp->sunpos[0] = cos(h * M_PI);
412 gp->sunpos[1] = sin(h * M_PI);
413 gp->sunpos[2] = sin(v * M_PI);
414 gp->sunpos[3] = 0.00;
417 glEnable(GL_LIGHTING);
420 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
421 glLightfv (GL_LIGHT0, GL_AMBIENT, lamb);
422 glLightfv (GL_LIGHT0, GL_DIFFUSE, ldif);
423 glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
425 glMaterialfv (GL_FRONT, GL_AMBIENT, mamb);
426 glMaterialfv (GL_FRONT, GL_DIFFUSE, mdif);
427 glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
428 glMaterialf (GL_FRONT, GL_SHININESS, shiny);
432 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
433 glShadeModel(GL_SMOOTH);
435 check_gl_error("lighting");
439 #define RANDSIGN() ((random() & 1) ? 1 : -1)
442 pick_velocity (ModeInfo * mi)
445 planetstruct *gp = &planets[MI_SCREEN(mi)];
447 gp->box_width = 15.0;
448 gp->box_height = 15.0;
455 gp->dtx = (frand(0.4) + frand(0.3)) * RANDSIGN();
456 gp->dty = (frand(0.4) + frand(0.3)) * RANDSIGN();
457 gp->dtz = (frand(5.0) + frand(5.0)); /* the sun sets in the west */
459 gp->dx = (frand(0.2) + frand(0.2)) * RANDSIGN();
460 gp->dy = (frand(0.2) + frand(0.2)) * RANDSIGN();
461 gp->dz = (frand(0.2) + frand(0.2)) * RANDSIGN();
467 reshape_planet (ModeInfo *mi, int width, int height)
469 GLfloat h = (GLfloat) height / (GLfloat) width;
471 glViewport(0, 0, (GLint) width, (GLint) height);
472 glMatrixMode(GL_PROJECTION);
474 glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
475 glMatrixMode(GL_MODELVIEW);
477 glTranslatef(0.0, 0.0, -DIST);
479 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
484 planet_handle_event (ModeInfo *mi, XEvent *event)
486 planetstruct *gp = &planets[MI_SCREEN(mi)];
488 if (event->xany.type == ButtonPress &&
489 event->xbutton.button & Button1)
491 gp->button_down_p = True;
492 gltrackball_start (gp->trackball,
493 event->xbutton.x, event->xbutton.y,
494 MI_WIDTH (mi), MI_HEIGHT (mi));
497 else if (event->xany.type == ButtonRelease &&
498 event->xbutton.button & Button1)
500 gp->button_down_p = False;
503 else if (event->xany.type == MotionNotify &&
506 gltrackball_track (gp->trackball,
507 event->xmotion.x, event->xmotion.y,
508 MI_WIDTH (mi), MI_HEIGHT (mi));
517 init_planet (ModeInfo * mi)
520 int screen = MI_SCREEN(mi);
521 Bool wire = MI_IS_WIREFRAME(mi);
523 if (planets == NULL) {
524 if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
525 sizeof (planetstruct))) == NULL)
528 gp = &planets[screen];
532 if ((gp->glx_context = init_GL(mi)) != NULL) {
533 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
537 char *f = get_string_resource("imageForeground", "Foreground");
538 char *b = get_string_resource("imageBackground", "Background");
540 if (!f) f = strdup("white");
541 if (!b) b = strdup("black");
543 for (s = f + strlen(f)-1; s > f; s--)
544 if (*s == ' ' || *s == '\t')
546 for (s = b + strlen(b)-1; s > b; s--)
547 if (*s == ' ' || *s == '\t')
550 if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
552 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
555 if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
557 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
566 double spin_speed = 1.0;
567 double wander_speed = 0.05;
568 gp->rot = make_rotator (do_roll ? spin_speed : 0,
569 do_roll ? spin_speed : 0,
571 do_wander ? wander_speed : 0,
574 gp->trackball = gltrackball_init ();
581 glEnable (GL_LINE_SMOOTH);
593 glEnable(GL_DEPTH_TEST);
594 glEnable(GL_CULL_FACE);
597 gp->platelist = glGenLists(1);
598 glNewList (gp->platelist, GL_COMPILE);
601 glScalef (RADIUS, RADIUS, RADIUS);
602 glRotatef (90, 1, 0, 0);
603 unit_sphere (resolution, resolution, wire);
604 mi->polygon_count += resolution*resolution;
608 glDisable(GL_LIGHTING);
609 glScalef(1.01,1.01,1.01);
610 unit_sphere (12, 24, 1);
611 glEnable(GL_LIGHTING);
619 draw_planet (ModeInfo * mi)
621 planetstruct *gp = &planets[MI_SCREEN(mi)];
622 Display *display = MI_DISPLAY(mi);
623 Window window = MI_WINDOW(mi);
626 if (!gp->glx_context)
629 glDrawBuffer(GL_BACK);
630 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
632 glXMakeCurrent (display, window, *(gp->glx_context));
639 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
640 glTranslatef((x - 0.5) * 15,
644 gltrackball_rotate (gp->trackball);
646 glRotatef (90,1,0,0);
650 get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
651 glRotatef (x * 360, 1.0, 0.0, 0.0);
652 glRotatef (y * 360, 0.0, 1.0, 0.0);
655 glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
657 glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
658 if (do_rotate && !gp->button_down_p)
661 if (gp->z > 1) gp->z -= 1;
664 glCallList (gp->platelist);
667 if (mi->fps_p) do_fps (mi);
669 glXSwapBuffers(display, window);
674 release_planet (ModeInfo * mi)
676 if (planets != NULL) {
679 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
680 planetstruct *gp = &planets[screen];
682 if (gp->glx_context) {
683 /* Display lists MUST be freed while their glXContext is current. */
684 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
686 if (glIsList(gp->platelist))
687 glDeleteLists(gp->platelist, 1);
688 if (glIsList(gp->starlist))
689 glDeleteLists(gp->starlist, 1);
692 (void) free((void *) planets);