19523a59d91c1a71f08d578402131897e6de0da3
[xscreensaver] / hacks / glx / glplanet.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* glplanet --- 3D rotating planet, e.g., Earth.
3  *
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.
9  *
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.
15  *
16  * Revision History:
17  *
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.
20  *
21  * 9-Oct-98:  dek@cgl.ucsf.edu  Added stars.
22  *
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.
26  *
27  * 8-Oct-98: Released initial version of "glplanet"
28  * (David Konerding, dek@cgl.ucsf.edu)
29  *
30  * BUGS:
31  * -bounce is broken
32  * 
33  *   For even more spectacular results, grab the images from the "SSystem"
34  *   package (http://www.msu.edu/user/kamelkev/) and use its JPEGs!
35  */
36
37
38 /*-
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
41  */
42
43 #include <X11/Intrinsic.h>
44
45 #ifdef STANDALONE
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" \
55                     "*rotate:           True    \n" \
56                     "*roll:             True    \n" \
57                     "*wander:           True    \n" \
58                                         "*wireframe:            False   \n"     \
59                                         "*light:                        True    \n"     \
60                                         "*texture:                      True    \n" \
61                                         "*stars:                        True    \n" \
62                                         "*image:                        BUILTIN \n" \
63                                         "*imageForeground:      Green   \n" \
64                                         "*imageBackground:      Blue    \n"
65
66 # include "xlockmore.h"                         /* from the xscreensaver distribution */
67 #else  /* !STANDALONE */
68 # include "xlock.h"                                     /* from the xlockmore distribution */
69 #endif /* !STANDALONE */
70
71 #ifdef USE_GL /* whole file */
72
73 #include "sphere.h"
74
75 #ifdef HAVE_XMU
76 # ifndef VMS
77 #  include <X11/Xmu/Drawing.h>
78 #else  /* VMS */
79 #  include <Xmu/Drawing.h>
80 # endif /* VMS */
81 #endif
82
83
84 #include <GL/glu.h>
85
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"
94
95 #undef countof
96 #define countof(x) (sizeof((x))/sizeof((*x)))
97
98 static int do_rotate;
99 static int do_roll;
100 static int do_wander;
101 static int do_texture;
102 static int do_stars;
103 static int do_light;
104 static char *which_image;
105 static int resolution;
106
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 },
122 };
123
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},
133 };
134
135 ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
136
137 #ifdef USE_MODULES
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};
143 #endif
144
145 #include "../images/earth.xpm"
146 #include "xpm-ximage.h"
147 #include "rotator.h"
148 #include "gltrackball.h"
149
150
151 /*-
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
155  */
156
157 #define NUM_STARS 1000
158
159 /* radius of the sphere- fairly arbitrary */
160 #define RADIUS 4
161
162 /* distance away from the sphere model */
163 #define DIST 40
164
165
166
167 /* structure for holding the planet data */
168 typedef struct {
169   GLuint platelist;
170   GLuint latlonglist;
171   GLuint starlist;
172   int screen_width, screen_height;
173   GLXContext *glx_context;
174   Window window;
175   XColor fg, bg;
176   GLfloat sunpos[4];
177   double z;
178   rotator *rot;
179   trackball_state *trackball;
180   Bool button_down_p;
181 } planetstruct;
182
183
184 static planetstruct *planets = NULL;
185
186
187 static inline void
188 normalize(GLfloat v[3])
189 {
190   GLfloat d = (GLfloat) sqrt((double) (v[0] * v[0] +
191                                        v[1] * v[1] +
192                                        v[2] * v[2]));
193   if (d != 0)
194     {
195       v[0] /= d;
196       v[1] /= d;
197       v[2] /= d;
198         }
199   else
200     {
201       v[0] = v[1] = v[2] = 0;
202         }
203 }
204
205
206 /* Set up and enable texturing on our object */
207 static void
208 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
209 {
210   XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
211                                   MI_COLORMAP (mi), xpm_data);
212   char buf[1024];
213   clear_gl_error();
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);
218   check_gl_error(buf);
219
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);
227 }
228
229
230 static void
231 setup_file_texture (ModeInfo *mi, char *filename)
232 {
233   Display *dpy = mi->dpy;
234   Visual *visual = mi->xgwa.visual;
235   char buf[1024];
236
237   Colormap cmap = mi->xgwa.colormap;
238   XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
239
240   clear_gl_error();
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);
246   check_gl_error(buf);
247
248   /* setup parameters for texturing */
249   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
250   glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
251
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);
257 }
258
259
260 static void
261 setup_texture(ModeInfo * mi)
262 {
263 /*  planetstruct *gp = &planets[MI_SCREEN(mi)];*/
264
265   glEnable(GL_TEXTURE_2D);
266
267   if (!which_image ||
268           !*which_image ||
269           !strcmp(which_image, "BUILTIN"))
270         setup_xpm_texture (mi, earth_xpm);
271   else
272         setup_file_texture (mi, which_image);
273
274   check_gl_error("texture initialization");
275
276   /* Need to flip the texture top for bottom for some reason. */
277   glMatrixMode (GL_TEXTURE);
278   glScalef (1, -1, 1);
279   glMatrixMode (GL_MODELVIEW);
280 }
281
282
283 void
284 init_stars (ModeInfo *mi)
285 {
286   int i, j;
287   GLfloat max_size = 3;
288   GLfloat inc = 0.5;
289   int steps = max_size / inc;
290   int width  = MI_WIDTH(mi);
291   int height = MI_HEIGHT(mi);
292
293   planetstruct *gp = &planets[MI_SCREEN(mi)];
294   Bool wire = MI_IS_WIREFRAME(mi);
295   
296   if (!wire)
297     glEnable (GL_POINT_SMOOTH);
298
299   gp->starlist = glGenLists(1);
300   glNewList(gp->starlist, GL_COMPILE);
301
302   glScalef (1.0/width, 1.0/height, 1);
303
304   for (j = 1; j <= steps; j++)
305     {
306       glPointSize(inc * j);
307       glBegin(GL_POINTS);
308       for (i = 0 ; i < NUM_STARS / steps; i++)
309         {
310           glColor3f (0.6 + frand(0.3),
311                      0.6 + frand(0.3),
312                      0.6 + frand(0.3));
313           glVertex2f ((GLfloat) (random() % width),
314                       (GLfloat) (random() % height));
315         }
316       glEnd();
317     }
318   glEndList();
319
320   check_gl_error("stars initialization");
321 }
322
323
324 void
325 draw_stars (ModeInfo * mi)
326 {
327   int width  = MI_WIDTH(mi);
328   int height = MI_HEIGHT(mi);
329
330   planetstruct *gp = &planets[MI_SCREEN(mi)];
331   
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
336      code in one file.
337    */
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() */
342   {
343     check_gl_error ("glPushAttrib");
344
345     /* disable lighting and texturing when drawing stars!
346        (glPopAttrib() restores these.)
347      */
348     glDisable(GL_TEXTURE_2D);
349     glDisable(GL_LIGHTING);
350     glDisable(GL_DEPTH_TEST);
351
352     /* glPopAttrib() does not restore matrix changes, so we must
353        push/pop the matrix stacks to be non-intrusive there.
354      */
355     glMatrixMode(GL_PROJECTION);
356     glPushMatrix();
357     {
358       check_gl_error ("glPushMatrix");
359       glLoadIdentity();
360
361       /* Each matrix mode has its own stack, so we need to push/pop
362          them separately. */
363       glMatrixMode(GL_MODELVIEW);
364       glPushMatrix();
365       {
366         check_gl_error ("glPushMatrix");
367         glLoadIdentity();
368
369         gluOrtho2D (0, width, 0, height);
370         check_gl_error ("gluOrtho2D");
371
372         /* Draw the stars */
373         glScalef (width, height, 1);
374         glCallList(gp->starlist);
375         check_gl_error ("drawing stars");
376       }
377       glPopMatrix();
378     }
379     glMatrixMode(GL_PROJECTION);
380     glPopMatrix();
381
382   }
383   /* clean up after our state changes */
384   glPopAttrib();
385   check_gl_error ("glPopAttrib");
386 }
387
388
389
390 /* Set up lighting */
391 static void
392 init_sun (ModeInfo * mi)
393 {
394   planetstruct *gp = &planets[MI_SCREEN(mi)];
395
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 };
399
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 };
403   GLfloat shiny = .4;
404
405   {
406     double h =  0.1 + frand(0.8);   /* east-west position - screen-side. */
407     double v = -0.3 + frand(0.6);   /* north-south position */
408
409     if (h > 0.3 && h < 0.8)         /* avoid having the sun at the camera */
410       h += (h > 0.5 ? 0.2 : -0.2);
411
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;
416   }
417
418   glEnable(GL_LIGHTING);
419   glEnable(GL_LIGHT0);
420
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);
425
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);
430
431
432   glEnable(GL_BLEND);
433   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
434   glShadeModel(GL_SMOOTH);
435
436   check_gl_error("lighting");
437 }
438
439
440 #define RANDSIGN() ((random() & 1) ? 1 : -1)
441
442 void
443 reshape_planet (ModeInfo *mi, int width, int height)
444 {
445   GLfloat h = (GLfloat) height / (GLfloat) width;
446
447   glViewport(0, 0, (GLint) width, (GLint) height);
448   glMatrixMode(GL_PROJECTION);
449   glLoadIdentity();
450   glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
451   glMatrixMode(GL_MODELVIEW);
452   glLoadIdentity();
453   glTranslatef(0.0, 0.0, -DIST);
454
455   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
456 }
457
458
459 Bool
460 planet_handle_event (ModeInfo *mi, XEvent *event)
461 {
462   planetstruct *gp = &planets[MI_SCREEN(mi)];
463
464   if (event->xany.type == ButtonPress &&
465       event->xbutton.button & Button1)
466     {
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));
471       return True;
472     }
473   else if (event->xany.type == ButtonRelease &&
474            event->xbutton.button & Button1)
475     {
476       gp->button_down_p = False;
477       return True;
478     }
479   else if (event->xany.type == MotionNotify &&
480            gp->button_down_p)
481     {
482       gltrackball_track (gp->trackball,
483                          event->xmotion.x, event->xmotion.y,
484                          MI_WIDTH (mi), MI_HEIGHT (mi));
485       return True;
486     }
487
488   return False;
489 }
490
491
492 void
493 init_planet (ModeInfo * mi)
494 {
495   planetstruct *gp;
496   int screen = MI_SCREEN(mi);
497   Bool wire = MI_IS_WIREFRAME(mi);
498
499   if (planets == NULL) {
500         if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
501                                                                                   sizeof (planetstruct))) == NULL)
502           return;
503   }
504   gp = &planets[screen];
505
506   if ((gp->glx_context = init_GL(mi)) != NULL) {
507         reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
508   }
509
510   {
511         char *f = get_string_resource("imageForeground", "Foreground");
512         char *b = get_string_resource("imageBackground", "Background");
513         char *s;
514         if (!f) f = strdup("white");
515         if (!b) b = strdup("black");
516         
517         for (s = f + strlen(f)-1; s > f; s--)
518           if (*s == ' ' || *s == '\t')
519                 *s = 0;
520         for (s = b + strlen(b)-1; s > b; s--)
521           if (*s == ' ' || *s == '\t')
522                 *s = 0;
523
524     if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
525       {
526                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
527                 exit(1);
528       }
529     if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
530       {
531                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
532                 exit(1);
533       }
534
535         free (f);
536         free (b);
537   }
538
539   {
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,
544                             0, 1,
545                             do_wander ? wander_speed : 0,
546                             True);
547     gp->z = frand (1.0);
548     gp->trackball = gltrackball_init ();
549   }
550
551   if (wire)
552     {
553       do_texture = False;
554       do_light = False;
555       glEnable (GL_LINE_SMOOTH);
556     }
557
558   if (do_texture)
559     setup_texture (mi);
560
561   if (do_light)
562         init_sun (mi);
563
564   if (do_stars)
565     init_stars (mi);
566
567   glEnable(GL_DEPTH_TEST);
568   glEnable(GL_CULL_FACE);
569   glCullFace(GL_BACK); 
570
571   /* construct the polygons of the planet
572    */
573   gp->platelist = glGenLists(1);
574   glNewList (gp->platelist, GL_COMPILE);
575   glColor3f (1,1,1);
576   glPushMatrix ();
577   glScalef (RADIUS, RADIUS, RADIUS);
578   glRotatef (90, 1, 0, 0);
579   unit_sphere (resolution, resolution, wire);
580   mi->polygon_count += resolution*resolution;
581   glPopMatrix ();
582   glEndList();
583
584   /* construct the polygons of the latitude/longitude/axis lines.
585    */
586   gp->latlonglist = glGenLists(1);
587   glNewList (gp->latlonglist, GL_COMPILE);
588   glPushMatrix ();
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);
596   glBegin(GL_LINES);
597   glVertex3f(0, -2, 0);
598   glVertex3f(0,  2, 0);
599   glEnd();
600   if (do_light)   glEnable(GL_LIGHTING);
601   if (do_texture) glEnable(GL_TEXTURE_2D);
602   glPopMatrix ();
603   glEndList();
604 }
605
606 void
607 draw_planet (ModeInfo * mi)
608 {
609   planetstruct *gp = &planets[MI_SCREEN(mi)];
610   Display    *display = MI_DISPLAY(mi);
611   Window      window = MI_WINDOW(mi);
612   double x, y, z;
613
614   if (!gp->glx_context)
615         return;
616
617   glDrawBuffer(GL_BACK);
618   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
619
620   glXMakeCurrent (display, window, *(gp->glx_context));
621
622   if (do_stars)
623     draw_stars (mi);
624
625   glPushMatrix();
626
627   get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
628   glTranslatef((x - 0.5) * 15,
629                (y - 0.5) * 15,
630                (z - 0.5) * 8);
631
632   gltrackball_rotate (gp->trackball);
633
634   glRotatef (90,1,0,0);
635
636   if (do_roll)
637     {
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);
641     }
642
643   glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
644
645   glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
646   if (do_rotate && !gp->button_down_p)
647     {
648       gp->z -= 0.01;     /* the sun sets in the west */
649       if (gp->z < 0) gp->z += 1;
650     }
651
652   glCallList (gp->platelist);
653   if (gp->button_down_p)
654     glCallList (gp->latlonglist);
655   glPopMatrix();
656
657   if (mi->fps_p) do_fps (mi);
658   glFinish();
659   glXSwapBuffers(display, window);
660 }
661
662
663 void
664 release_planet (ModeInfo * mi)
665 {
666   if (planets != NULL) {
667         int screen;
668
669         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
670           planetstruct *gp = &planets[screen];
671
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));
675
676                 if (glIsList(gp->platelist))
677                   glDeleteLists(gp->platelist, 1);
678                 if (glIsList(gp->starlist))
679                   glDeleteLists(gp->starlist, 1);
680           }
681         }
682         (void) free((void *) planets);
683         planets = NULL;
684   }
685   FreeAllGL(mi);
686 }
687
688
689 #endif
690