f2080b303af96aa6b258f2b46bd5419745cc0c61
[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,
217                /* GL_UNSIGNED_BYTE, */
218                GL_UNSIGNED_INT_8_8_8_8_REV,
219                image->data);
220   sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
221   check_gl_error(buf);
222
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);
230 }
231
232
233 static void
234 setup_file_texture (ModeInfo *mi, char *filename)
235 {
236   Display *dpy = mi->dpy;
237   Visual *visual = mi->xgwa.visual;
238   char buf[1024];
239
240   Colormap cmap = mi->xgwa.colormap;
241   XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
242
243   clear_gl_error();
244   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
245                image->width, image->height, 0,
246                GL_RGBA,
247                /* GL_UNSIGNED_BYTE, */
248                GL_UNSIGNED_INT_8_8_8_8_REV,
249                image->data);
250   sprintf (buf, "texture: %.100s (%dx%d)",
251            filename, image->width, image->height);
252   check_gl_error(buf);
253
254   /* setup parameters for texturing */
255   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
256   glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
257
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);
263 }
264
265
266 static void
267 setup_texture(ModeInfo * mi)
268 {
269 /*  planetstruct *gp = &planets[MI_SCREEN(mi)];*/
270
271   glEnable(GL_TEXTURE_2D);
272
273   if (!which_image ||
274           !*which_image ||
275           !strcmp(which_image, "BUILTIN"))
276         setup_xpm_texture (mi, earth_xpm);
277   else
278         setup_file_texture (mi, which_image);
279
280   check_gl_error("texture initialization");
281
282   /* Need to flip the texture top for bottom for some reason. */
283   glMatrixMode (GL_TEXTURE);
284   glScalef (1, -1, 1);
285   glMatrixMode (GL_MODELVIEW);
286 }
287
288
289 void
290 init_stars (ModeInfo *mi)
291 {
292   int i, j;
293   GLfloat max_size = 3;
294   GLfloat inc = 0.5;
295   int steps = max_size / inc;
296   int width  = MI_WIDTH(mi);
297   int height = MI_HEIGHT(mi);
298
299   planetstruct *gp = &planets[MI_SCREEN(mi)];
300   Bool wire = MI_IS_WIREFRAME(mi);
301   
302   if (!wire)
303     glEnable (GL_POINT_SMOOTH);
304
305   gp->starlist = glGenLists(1);
306   glNewList(gp->starlist, GL_COMPILE);
307
308   glScalef (1.0/width, 1.0/height, 1);
309
310   for (j = 1; j <= steps; j++)
311     {
312       glPointSize(inc * j);
313       glBegin(GL_POINTS);
314       for (i = 0 ; i < NUM_STARS / steps; i++)
315         {
316           glColor3f (0.6 + frand(0.3),
317                      0.6 + frand(0.3),
318                      0.6 + frand(0.3));
319           glVertex2f ((GLfloat) (random() % width),
320                       (GLfloat) (random() % height));
321         }
322       glEnd();
323     }
324   glEndList();
325
326   check_gl_error("stars initialization");
327 }
328
329
330 void
331 draw_stars (ModeInfo * mi)
332 {
333   int width  = MI_WIDTH(mi);
334   int height = MI_HEIGHT(mi);
335
336   planetstruct *gp = &planets[MI_SCREEN(mi)];
337   
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
342      code in one file.
343    */
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() */
348   {
349     check_gl_error ("glPushAttrib");
350
351     /* disable lighting and texturing when drawing stars!
352        (glPopAttrib() restores these.)
353      */
354     glDisable(GL_TEXTURE_2D);
355     glDisable(GL_LIGHTING);
356     glDisable(GL_DEPTH_TEST);
357
358     /* glPopAttrib() does not restore matrix changes, so we must
359        push/pop the matrix stacks to be non-intrusive there.
360      */
361     glMatrixMode(GL_PROJECTION);
362     glPushMatrix();
363     {
364       check_gl_error ("glPushMatrix");
365       glLoadIdentity();
366
367       /* Each matrix mode has its own stack, so we need to push/pop
368          them separately. */
369       glMatrixMode(GL_MODELVIEW);
370       glPushMatrix();
371       {
372         check_gl_error ("glPushMatrix");
373         glLoadIdentity();
374
375         gluOrtho2D (0, width, 0, height);
376         check_gl_error ("gluOrtho2D");
377
378         /* Draw the stars */
379         glScalef (width, height, 1);
380         glCallList(gp->starlist);
381         check_gl_error ("drawing stars");
382       }
383       glPopMatrix();
384     }
385     glMatrixMode(GL_PROJECTION);
386     glPopMatrix();
387
388   }
389   /* clean up after our state changes */
390   glPopAttrib();
391   check_gl_error ("glPopAttrib");
392 }
393
394
395
396 /* Set up lighting */
397 static void
398 init_sun (ModeInfo * mi)
399 {
400   planetstruct *gp = &planets[MI_SCREEN(mi)];
401
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 };
405
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 };
409   GLfloat shiny = .4;
410
411   {
412     double h =  0.1 + frand(0.8);   /* east-west position - screen-side. */
413     double v = -0.3 + frand(0.6);   /* north-south position */
414
415     if (h > 0.3 && h < 0.8)         /* avoid having the sun at the camera */
416       h += (h > 0.5 ? 0.2 : -0.2);
417
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;
422   }
423
424   glEnable(GL_LIGHTING);
425   glEnable(GL_LIGHT0);
426
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);
431
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);
436
437
438   glEnable(GL_BLEND);
439   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
440   glShadeModel(GL_SMOOTH);
441
442   check_gl_error("lighting");
443 }
444
445
446 #define RANDSIGN() ((random() & 1) ? 1 : -1)
447
448 void
449 reshape_planet (ModeInfo *mi, int width, int height)
450 {
451   GLfloat h = (GLfloat) height / (GLfloat) width;
452
453   glViewport(0, 0, (GLint) width, (GLint) height);
454   glMatrixMode(GL_PROJECTION);
455   glLoadIdentity();
456   glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
457   glMatrixMode(GL_MODELVIEW);
458   glLoadIdentity();
459   glTranslatef(0.0, 0.0, -DIST);
460
461   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
462 }
463
464
465 Bool
466 planet_handle_event (ModeInfo *mi, XEvent *event)
467 {
468   planetstruct *gp = &planets[MI_SCREEN(mi)];
469
470   if (event->xany.type == ButtonPress &&
471       event->xbutton.button & Button1)
472     {
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));
477       return True;
478     }
479   else if (event->xany.type == ButtonRelease &&
480            event->xbutton.button & Button1)
481     {
482       gp->button_down_p = False;
483       return True;
484     }
485   else if (event->xany.type == MotionNotify &&
486            gp->button_down_p)
487     {
488       gltrackball_track (gp->trackball,
489                          event->xmotion.x, event->xmotion.y,
490                          MI_WIDTH (mi), MI_HEIGHT (mi));
491       return True;
492     }
493
494   return False;
495 }
496
497
498 void
499 init_planet (ModeInfo * mi)
500 {
501   planetstruct *gp;
502   int screen = MI_SCREEN(mi);
503   Bool wire = MI_IS_WIREFRAME(mi);
504
505   if (planets == NULL) {
506         if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
507                                                                                   sizeof (planetstruct))) == NULL)
508           return;
509   }
510   gp = &planets[screen];
511
512   if ((gp->glx_context = init_GL(mi)) != NULL) {
513         reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
514   }
515
516   {
517         char *f = get_string_resource("imageForeground", "Foreground");
518         char *b = get_string_resource("imageBackground", "Background");
519         char *s;
520         if (!f) f = strdup("white");
521         if (!b) b = strdup("black");
522         
523         for (s = f + strlen(f)-1; s > f; s--)
524           if (*s == ' ' || *s == '\t')
525                 *s = 0;
526         for (s = b + strlen(b)-1; s > b; s--)
527           if (*s == ' ' || *s == '\t')
528                 *s = 0;
529
530     if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
531       {
532                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
533                 exit(1);
534       }
535     if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
536       {
537                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
538                 exit(1);
539       }
540
541         free (f);
542         free (b);
543   }
544
545   {
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,
550                             0, 1,
551                             do_wander ? wander_speed : 0,
552                             True);
553     gp->z = frand (1.0);
554     gp->trackball = gltrackball_init ();
555   }
556
557   if (wire)
558     {
559       do_texture = False;
560       do_light = False;
561       glEnable (GL_LINE_SMOOTH);
562     }
563
564   if (do_texture)
565     setup_texture (mi);
566
567   if (do_light)
568         init_sun (mi);
569
570   if (do_stars)
571     init_stars (mi);
572
573   glEnable(GL_DEPTH_TEST);
574   glEnable(GL_CULL_FACE);
575   glCullFace(GL_BACK); 
576
577   /* construct the polygons of the planet
578    */
579   gp->platelist = glGenLists(1);
580   glNewList (gp->platelist, GL_COMPILE);
581   glColor3f (1,1,1);
582   glPushMatrix ();
583   glScalef (RADIUS, RADIUS, RADIUS);
584   glRotatef (90, 1, 0, 0);
585   unit_sphere (resolution, resolution, wire);
586   mi->polygon_count += resolution*resolution;
587   glPopMatrix ();
588   glEndList();
589
590   /* construct the polygons of the latitude/longitude/axis lines.
591    */
592   gp->latlonglist = glGenLists(1);
593   glNewList (gp->latlonglist, GL_COMPILE);
594   glPushMatrix ();
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);
602   glBegin(GL_LINES);
603   glVertex3f(0, -2, 0);
604   glVertex3f(0,  2, 0);
605   glEnd();
606   if (do_light)   glEnable(GL_LIGHTING);
607   if (do_texture) glEnable(GL_TEXTURE_2D);
608   glPopMatrix ();
609   glEndList();
610 }
611
612 void
613 draw_planet (ModeInfo * mi)
614 {
615   planetstruct *gp = &planets[MI_SCREEN(mi)];
616   Display    *display = MI_DISPLAY(mi);
617   Window      window = MI_WINDOW(mi);
618   double x, y, z;
619
620   if (!gp->glx_context)
621         return;
622
623   glDrawBuffer(GL_BACK);
624   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
625
626   glXMakeCurrent (display, window, *(gp->glx_context));
627
628   if (do_stars)
629     draw_stars (mi);
630
631   glPushMatrix();
632
633   get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
634   glTranslatef((x - 0.5) * 15,
635                (y - 0.5) * 15,
636                (z - 0.5) * 8);
637
638   gltrackball_rotate (gp->trackball);
639
640   glRotatef (90,1,0,0);
641
642   if (do_roll)
643     {
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);
647     }
648
649   glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
650
651   glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
652   if (do_rotate && !gp->button_down_p)
653     {
654       gp->z -= 0.01;     /* the sun sets in the west */
655       if (gp->z < 0) gp->z += 1;
656     }
657
658   glCallList (gp->platelist);
659   if (gp->button_down_p)
660     glCallList (gp->latlonglist);
661   glPopMatrix();
662
663   if (mi->fps_p) do_fps (mi);
664   glFinish();
665   glXSwapBuffers(display, window);
666 }
667
668
669 void
670 release_planet (ModeInfo * mi)
671 {
672   if (planets != NULL) {
673         int screen;
674
675         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
676           planetstruct *gp = &planets[screen];
677
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));
681
682                 if (glIsList(gp->platelist))
683                   glDeleteLists(gp->platelist, 1);
684                 if (glIsList(gp->starlist))
685                   glDeleteLists(gp->starlist, 1);
686           }
687         }
688         (void) free((void *) planets);
689         planets = NULL;
690   }
691   FreeAllGL(mi);
692 }
693
694
695 #endif
696