955325d261ecce8908099207cd52269d1c46ddba
[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   {&do_rotate,   "rotate",  "Rotate",  DEF_ROTATE,  t_Bool},
126   {&do_roll,     "roll",    "Roll",    DEF_ROLL,    t_Bool},
127   {&do_wander,   "wander",  "Wander",  DEF_WANDER,  t_Bool},
128   {&do_texture,  "texture", "Texture", DEF_TEXTURE, t_Bool},
129   {&do_stars,    "stars",   "Stars",   DEF_STARS,   t_Bool},
130   {&do_light,    "light",   "Light",   DEF_LIGHT,   t_Bool},
131   {&which_image, "image",   "Image",   DEF_IMAGE,   t_String},
132   {&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 # ifdef __GNUC__
146   __extension__  /* don't warn about "string length is greater than the length
147                     ISO C89 compilers are required to support" when including
148                     the following XPM file... */
149 # endif
150 #include "../images/earth.xpm"
151
152 #include "xpm-ximage.h"
153 #include "rotator.h"
154 #include "gltrackball.h"
155
156
157 /*-
158  * slices and stacks are used in the sphere parameterization routine.
159  * more slices and stacks will increase the quality of the sphere,
160  * at the expense of rendering speed
161  */
162
163 #define NUM_STARS 1000
164
165 /* radius of the sphere- fairly arbitrary */
166 #define RADIUS 4
167
168 /* distance away from the sphere model */
169 #define DIST 40
170
171
172
173 /* structure for holding the planet data */
174 typedef struct {
175   GLuint platelist;
176   GLuint latlonglist;
177   GLuint starlist;
178   int screen_width, screen_height;
179   GLXContext *glx_context;
180   Window window;
181   XColor fg, bg;
182   GLfloat sunpos[4];
183   double z;
184   rotator *rot;
185   trackball_state *trackball;
186   Bool button_down_p;
187 } planetstruct;
188
189
190 static planetstruct *planets = NULL;
191
192
193 static inline void
194 normalize(GLfloat v[3])
195 {
196   GLfloat d = (GLfloat) sqrt((double) (v[0] * v[0] +
197                                        v[1] * v[1] +
198                                        v[2] * v[2]));
199   if (d != 0)
200     {
201       v[0] /= d;
202       v[1] /= d;
203       v[2] /= d;
204         }
205   else
206     {
207       v[0] = v[1] = v[2] = 0;
208         }
209 }
210
211
212 /* Set up and enable texturing on our object */
213 static void
214 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
215 {
216   XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
217                                   MI_COLORMAP (mi), xpm_data);
218   char buf[1024];
219   clear_gl_error();
220   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
221                image->width, image->height, 0,
222                GL_RGBA,
223                /* GL_UNSIGNED_BYTE, */
224                GL_UNSIGNED_INT_8_8_8_8_REV,
225                image->data);
226   sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
227   check_gl_error(buf);
228
229   /* setup parameters for texturing */
230   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
231   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
232   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
233   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
234   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
235   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
236 }
237
238
239 static void
240 setup_file_texture (ModeInfo *mi, char *filename)
241 {
242   Display *dpy = mi->dpy;
243   Visual *visual = mi->xgwa.visual;
244   char buf[1024];
245
246   Colormap cmap = mi->xgwa.colormap;
247   XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
248
249   clear_gl_error();
250   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
251                image->width, image->height, 0,
252                GL_RGBA,
253                /* GL_UNSIGNED_BYTE, */
254                GL_UNSIGNED_INT_8_8_8_8_REV,
255                image->data);
256   sprintf (buf, "texture: %.100s (%dx%d)",
257            filename, image->width, image->height);
258   check_gl_error(buf);
259
260   /* setup parameters for texturing */
261   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
262   glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
263
264   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
265   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
266   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
267   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
268   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
269 }
270
271
272 static void
273 setup_texture(ModeInfo * mi)
274 {
275 /*  planetstruct *gp = &planets[MI_SCREEN(mi)];*/
276
277   glEnable(GL_TEXTURE_2D);
278
279   if (!which_image ||
280           !*which_image ||
281           !strcmp(which_image, "BUILTIN"))
282         setup_xpm_texture (mi, earth_xpm);
283   else
284         setup_file_texture (mi, which_image);
285
286   check_gl_error("texture initialization");
287
288   /* Need to flip the texture top for bottom for some reason. */
289   glMatrixMode (GL_TEXTURE);
290   glScalef (1, -1, 1);
291   glMatrixMode (GL_MODELVIEW);
292 }
293
294
295 void
296 init_stars (ModeInfo *mi)
297 {
298   int i, j;
299   GLfloat max_size = 3;
300   GLfloat inc = 0.5;
301   int steps = max_size / inc;
302   int width  = MI_WIDTH(mi);
303   int height = MI_HEIGHT(mi);
304
305   planetstruct *gp = &planets[MI_SCREEN(mi)];
306   Bool wire = MI_IS_WIREFRAME(mi);
307   
308   if (!wire)
309     glEnable (GL_POINT_SMOOTH);
310
311   gp->starlist = glGenLists(1);
312   glNewList(gp->starlist, GL_COMPILE);
313
314   glScalef (1.0/width, 1.0/height, 1);
315
316   for (j = 1; j <= steps; j++)
317     {
318       glPointSize(inc * j);
319       glBegin(GL_POINTS);
320       for (i = 0 ; i < NUM_STARS / steps; i++)
321         {
322           glColor3f (0.6 + frand(0.3),
323                      0.6 + frand(0.3),
324                      0.6 + frand(0.3));
325           glVertex2f ((GLfloat) (random() % width),
326                       (GLfloat) (random() % height));
327         }
328       glEnd();
329     }
330   glEndList();
331
332   check_gl_error("stars initialization");
333 }
334
335
336 void
337 draw_stars (ModeInfo * mi)
338 {
339   int width  = MI_WIDTH(mi);
340   int height = MI_HEIGHT(mi);
341
342   planetstruct *gp = &planets[MI_SCREEN(mi)];
343   
344   /* Sadly, this causes a stall of the graphics pipeline (as would the
345      equivalent calls to glGet*.)  But there's no way around this, short
346      of having each caller set up the specific display matrix we need
347      here, which would kind of defeat the purpose of centralizing this
348      code in one file.
349    */
350   glPushAttrib(GL_TRANSFORM_BIT |  /* for matrix contents */
351                GL_ENABLE_BIT |     /* for various glDisable calls */
352                GL_CURRENT_BIT |    /* for glColor3f() */
353                GL_LIST_BIT);       /* for glListBase() */
354   {
355     check_gl_error ("glPushAttrib");
356
357     /* disable lighting and texturing when drawing stars!
358        (glPopAttrib() restores these.)
359      */
360     glDisable(GL_TEXTURE_2D);
361     glDisable(GL_LIGHTING);
362     glDisable(GL_DEPTH_TEST);
363
364     /* glPopAttrib() does not restore matrix changes, so we must
365        push/pop the matrix stacks to be non-intrusive there.
366      */
367     glMatrixMode(GL_PROJECTION);
368     glPushMatrix();
369     {
370       check_gl_error ("glPushMatrix");
371       glLoadIdentity();
372
373       /* Each matrix mode has its own stack, so we need to push/pop
374          them separately. */
375       glMatrixMode(GL_MODELVIEW);
376       glPushMatrix();
377       {
378         check_gl_error ("glPushMatrix");
379         glLoadIdentity();
380
381         gluOrtho2D (0, width, 0, height);
382         check_gl_error ("gluOrtho2D");
383
384         /* Draw the stars */
385         glScalef (width, height, 1);
386         glCallList(gp->starlist);
387         check_gl_error ("drawing stars");
388       }
389       glPopMatrix();
390     }
391     glMatrixMode(GL_PROJECTION);
392     glPopMatrix();
393
394   }
395   /* clean up after our state changes */
396   glPopAttrib();
397   check_gl_error ("glPopAttrib");
398 }
399
400
401
402 /* Set up lighting */
403 static void
404 init_sun (ModeInfo * mi)
405 {
406   planetstruct *gp = &planets[MI_SCREEN(mi)];
407
408   GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
409   GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
410   GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
411
412   GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
413   GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
414   GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
415   GLfloat shiny = .4;
416
417   {
418     double h =  0.1 + frand(0.8);   /* east-west position - screen-side. */
419     double v = -0.3 + frand(0.6);   /* north-south position */
420
421     if (h > 0.3 && h < 0.8)         /* avoid having the sun at the camera */
422       h += (h > 0.5 ? 0.2 : -0.2);
423
424     gp->sunpos[0] = cos(h * M_PI);
425     gp->sunpos[1] = sin(h * M_PI);
426     gp->sunpos[2] = sin(v * M_PI);
427     gp->sunpos[3] =  0.00;
428   }
429
430   glEnable(GL_LIGHTING);
431   glEnable(GL_LIGHT0);
432
433   glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
434   glLightfv (GL_LIGHT0, GL_AMBIENT,  lamb);
435   glLightfv (GL_LIGHT0, GL_DIFFUSE,  ldif);
436   glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
437
438   glMaterialfv (GL_FRONT, GL_AMBIENT,  mamb);
439   glMaterialfv (GL_FRONT, GL_DIFFUSE,  mdif);
440   glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
441   glMaterialf  (GL_FRONT, GL_SHININESS, shiny);
442
443
444   glEnable(GL_BLEND);
445   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
446   glShadeModel(GL_SMOOTH);
447
448   check_gl_error("lighting");
449 }
450
451
452 #define RANDSIGN() ((random() & 1) ? 1 : -1)
453
454 void
455 reshape_planet (ModeInfo *mi, int width, int height)
456 {
457   GLfloat h = (GLfloat) height / (GLfloat) width;
458
459   glViewport(0, 0, (GLint) width, (GLint) height);
460   glMatrixMode(GL_PROJECTION);
461   glLoadIdentity();
462   glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
463   glMatrixMode(GL_MODELVIEW);
464   glLoadIdentity();
465   glTranslatef(0.0, 0.0, -DIST);
466
467   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
468 }
469
470
471 Bool
472 planet_handle_event (ModeInfo *mi, XEvent *event)
473 {
474   planetstruct *gp = &planets[MI_SCREEN(mi)];
475
476   if (event->xany.type == ButtonPress &&
477       event->xbutton.button & Button1)
478     {
479       gp->button_down_p = True;
480       gltrackball_start (gp->trackball,
481                          event->xbutton.x, event->xbutton.y,
482                          MI_WIDTH (mi), MI_HEIGHT (mi));
483       return True;
484     }
485   else if (event->xany.type == ButtonRelease &&
486            event->xbutton.button & Button1)
487     {
488       gp->button_down_p = False;
489       return True;
490     }
491   else if (event->xany.type == MotionNotify &&
492            gp->button_down_p)
493     {
494       gltrackball_track (gp->trackball,
495                          event->xmotion.x, event->xmotion.y,
496                          MI_WIDTH (mi), MI_HEIGHT (mi));
497       return True;
498     }
499
500   return False;
501 }
502
503
504 void
505 init_planet (ModeInfo * mi)
506 {
507   planetstruct *gp;
508   int screen = MI_SCREEN(mi);
509   Bool wire = MI_IS_WIREFRAME(mi);
510
511   if (planets == NULL) {
512         if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
513                                                                                   sizeof (planetstruct))) == NULL)
514           return;
515   }
516   gp = &planets[screen];
517
518   if ((gp->glx_context = init_GL(mi)) != NULL) {
519         reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
520   }
521
522   {
523         char *f = get_string_resource("imageForeground", "Foreground");
524         char *b = get_string_resource("imageBackground", "Background");
525         char *s;
526         if (!f) f = strdup("white");
527         if (!b) b = strdup("black");
528         
529         for (s = f + strlen(f)-1; s > f; s--)
530           if (*s == ' ' || *s == '\t')
531                 *s = 0;
532         for (s = b + strlen(b)-1; s > b; s--)
533           if (*s == ' ' || *s == '\t')
534                 *s = 0;
535
536     if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
537       {
538                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
539                 exit(1);
540       }
541     if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
542       {
543                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
544                 exit(1);
545       }
546
547         free (f);
548         free (b);
549   }
550
551   {
552     double spin_speed   = 1.0;
553     double wander_speed = 0.05;
554     gp->rot = make_rotator (do_roll ? spin_speed : 0,
555                             do_roll ? spin_speed : 0,
556                             0, 1,
557                             do_wander ? wander_speed : 0,
558                             True);
559     gp->z = frand (1.0);
560     gp->trackball = gltrackball_init ();
561   }
562
563   if (wire)
564     {
565       do_texture = False;
566       do_light = False;
567       glEnable (GL_LINE_SMOOTH);
568     }
569
570   if (do_texture)
571     setup_texture (mi);
572
573   if (do_light)
574         init_sun (mi);
575
576   if (do_stars)
577     init_stars (mi);
578
579   glEnable(GL_DEPTH_TEST);
580   glEnable(GL_CULL_FACE);
581   glCullFace(GL_BACK); 
582
583   /* construct the polygons of the planet
584    */
585   gp->platelist = glGenLists(1);
586   glNewList (gp->platelist, GL_COMPILE);
587   glColor3f (1,1,1);
588   glPushMatrix ();
589   glScalef (RADIUS, RADIUS, RADIUS);
590   glRotatef (90, 1, 0, 0);
591   unit_sphere (resolution, resolution, wire);
592   mi->polygon_count += resolution*resolution;
593   glPopMatrix ();
594   glEndList();
595
596   /* construct the polygons of the latitude/longitude/axis lines.
597    */
598   gp->latlonglist = glGenLists(1);
599   glNewList (gp->latlonglist, GL_COMPILE);
600   glPushMatrix ();
601   if (do_texture) glDisable (GL_TEXTURE_2D);
602   if (do_light)   glDisable (GL_LIGHTING);
603   glColor3f (0.1, 0.3, 0.1);
604   glScalef (RADIUS, RADIUS, RADIUS);
605   glScalef (1.01, 1.01, 1.01);
606   glRotatef (90, 1, 0, 0);
607   unit_sphere (12, 24, 1);
608   glBegin(GL_LINES);
609   glVertex3f(0, -2, 0);
610   glVertex3f(0,  2, 0);
611   glEnd();
612   if (do_light)   glEnable(GL_LIGHTING);
613   if (do_texture) glEnable(GL_TEXTURE_2D);
614   glPopMatrix ();
615   glEndList();
616 }
617
618 void
619 draw_planet (ModeInfo * mi)
620 {
621   planetstruct *gp = &planets[MI_SCREEN(mi)];
622   Display    *display = MI_DISPLAY(mi);
623   Window      window = MI_WINDOW(mi);
624   double x, y, z;
625
626   if (!gp->glx_context)
627         return;
628
629   glDrawBuffer(GL_BACK);
630   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
631
632   glXMakeCurrent (display, window, *(gp->glx_context));
633
634   if (do_stars)
635     draw_stars (mi);
636
637   glPushMatrix();
638
639   get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
640   glTranslatef((x - 0.5) * 15,
641                (y - 0.5) * 15,
642                (z - 0.5) * 8);
643
644   gltrackball_rotate (gp->trackball);
645
646   glRotatef (90,1,0,0);
647
648   if (do_roll)
649     {
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);
653     }
654
655   glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
656
657   glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
658   if (do_rotate && !gp->button_down_p)
659     {
660       gp->z -= 0.01;     /* the sun sets in the west */
661       if (gp->z < 0) gp->z += 1;
662     }
663
664   glCallList (gp->platelist);
665   if (gp->button_down_p)
666     glCallList (gp->latlonglist);
667   glPopMatrix();
668
669   if (mi->fps_p) do_fps (mi);
670   glFinish();
671   glXSwapBuffers(display, window);
672 }
673
674
675 void
676 release_planet (ModeInfo * mi)
677 {
678   if (planets != NULL) {
679         int screen;
680
681         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
682           planetstruct *gp = &planets[screen];
683
684           if (gp->glx_context) {
685                 /* Display lists MUST be freed while their glXContext is current. */
686                 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
687
688                 if (glIsList(gp->platelist))
689                   glDeleteLists(gp->platelist, 1);
690                 if (glIsList(gp->starlist))
691                   glDeleteLists(gp->starlist, 1);
692           }
693         }
694         (void) free((void *) planets);
695         planets = NULL;
696   }
697   FreeAllGL(mi);
698 }
699
700
701 #endif
702