http://ftp.x.org/contrib/applications/xscreensaver-2.34.tar.gz
[xscreensaver] / hacks / glx / glplanet.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* glplanet --- 3D rotating planet, e.g., Earth. */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)plate.c       4.07 97/11/24 xlockmore";
6
7 #endif
8
9 /*-
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * Revision History:
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  * TODO:
31  * 1) stars
32  * 3) better earth image
33  * 4) "exploding" planet mode-- the surface will expand and explode
34  * 5) Fix bug with annoying triangles moving on surface
35  *
36  *
37  *   For even more spectacular results, grab the images from the "SSysten"
38  *   package (http://www.msu.edu/user/kamelkev/) and do this:
39  *
40  *     cd ssystem-1.4/hires/
41  *     foreach f ( *.jpg )
42  *       djpeg $f | ppmquant 254 | ppmtoxpm > /tmp/$f:r.xpm
43  *     end
44  *
45  *     cd /tmp
46  *     foreach f ( *.xpm )
47  *       glplanet -image $f
48  *     end
49  */
50
51
52 /*-
53  * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
54  * otherwise caddr_t is not defined correctly
55  */
56
57 #include <X11/Intrinsic.h>
58
59 #ifdef STANDALONE
60 # define PROGCLASS                                              "Planet"
61 # define HACK_INIT                                              init_planet
62 # define HACK_DRAW                                              draw_planet
63 # define planet_opts                                    xlockmore_opts
64 #define DEFAULTS        "*delay:                        15000   \n"     \
65                     "*rotate:           True    \n" \
66                     "*roll:             True    \n" \
67                     "*bounce:           True    \n" \
68                                         "*wireframe:            False   \n"     \
69                                         "*light:                        True    \n"     \
70                                         "*texture:                      True    \n" \
71                                         "*stipple:                      False   \n" \
72                                         "*image:                        BUILTIN \n" \
73                                         "*imageForeground:      Green   \n" \
74                                         "*imageBackground:      Blue    \n"
75
76 # include "xlockmore.h"                         /* from the xscreensaver distribution */
77 #else  /* !STANDALONE */
78 # include "xlock.h"                                     /* from the xlockmore distribution */
79 #endif /* !STANDALONE */
80
81 #ifdef USE_GL /* whole file */
82
83 #ifdef HAVE_XPM
84 # include <X11/xpm.h>
85 # ifndef PIXEL_ALREADY_TYPEDEFED
86 #  define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */
87 # endif
88 #endif
89
90 #ifdef HAVE_XMU
91 # ifndef VMS
92 #  include <X11/Xmu/Drawing.h>
93 #else  /* VMS */
94 #  include <Xmu/Drawing.h>
95 # endif /* VMS */
96 #endif
97
98
99 #include <GL/glu.h>
100
101 #define DEF_ROTATE  "True"
102 #define DEF_ROLL    "True"
103 #define DEF_BOUNCE  "True"
104 #define DEF_TEXTURE "True"
105 #define DEF_LIGHT   "True"
106 #define DEF_STIPPLE "False"
107 #define DEF_IMAGE   "BUILTIN"
108
109 #undef countof
110 #define countof(x) (sizeof((x))/sizeof((*x)))
111
112 static int do_rotate;
113 static int do_roll;
114 static int do_bounce;
115 static int do_texture;
116 static int do_light;
117 static int do_stipple;
118 static char *which_image;
119 static XrmOptionDescRec opts[] = {
120   {"-rotate",  ".glplanet.rotate",  XrmoptionNoArg, (caddr_t) "true" },
121   {"+rotate",  ".glplanet.rotate",  XrmoptionNoArg, (caddr_t) "false" },
122   {"-roll",    ".glplanet.roll",    XrmoptionNoArg, (caddr_t) "true" },
123   {"+roll",    ".glplanet.roll",    XrmoptionNoArg, (caddr_t) "false" },
124   {"-bounce",  ".glplanet.bounce",  XrmoptionNoArg, (caddr_t) "true" },
125   {"+bounce",  ".glplanet.bounce",  XrmoptionNoArg, (caddr_t) "false" },
126   {"-texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "true" },
127   {"+texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "false" },
128   {"-light",   ".glplanet.light",   XrmoptionNoArg, (caddr_t) "true" },
129   {"+light",   ".glplanet.light",   XrmoptionNoArg, (caddr_t) "false" },
130   {"-stipple", ".glplanet.stipple", XrmoptionNoArg, (caddr_t) "true" },
131   {"+stipple", ".glplanet.stipple", XrmoptionNoArg, (caddr_t) "false" },
132   {"-image",   ".glplanet.image",  XrmoptionSepArg, (caddr_t) 0 },
133 };
134
135 static argtype vars[] = {
136   {(caddr_t *) &do_rotate,   "rotate",  "Rotate",  DEF_ROTATE,  t_Bool},
137   {(caddr_t *) &do_roll,     "roll",    "Roll",    DEF_ROLL,    t_Bool},
138   {(caddr_t *) &do_bounce,   "bounce",  "Bounce",  DEF_BOUNCE,  t_Bool},
139   {(caddr_t *) &do_texture,  "texture", "Texture", DEF_TEXTURE, t_Bool},
140   {(caddr_t *) &do_light,    "light",   "Light",   DEF_LIGHT,   t_Bool},
141   {(caddr_t *) &do_stipple,  "stipple", "Stipple", DEF_STIPPLE, t_Bool},
142   {(caddr_t *) &which_image, "image",   "Image",   DEF_IMAGE,   t_String},
143 };
144
145 ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
146
147 #ifdef USE_MODULES
148 ModStruct   planet_description =
149 {"planet", "init_planet", "draw_planet", "release_planet",
150  "draw_planet", "init_planet", NULL, &planet_opts,
151  1000, 1, 2, 1, 4, 1.0, "",
152  "Animates texture mapped sphere (planet)", 0, NULL};
153 #endif
154
155 #include "../images/earth.xbm"
156 #include "xpm-ximage.h"
157
158
159 /*-
160  * slices and stacks are used in the sphere parameterization routine.
161  * more slices and stacks will increase the quality of the sphere,
162  * at the expense of rendering speed
163  */
164
165 #define SLICES 25
166 #define STACKS 25
167 #define NUM_PLATES (STACKS * (SLICES+1))
168
169 /* radius of the sphere- fairly arbitrary */
170 #define RADIUS 5.
171
172
173
174 /*-
175  * structure for holding the data for an individual plate.
176  * RotationRate, Angle, Vector, Translation and Color
177  * are not currently used, but may be used in the future
178  */
179 typedef struct {
180   GLfloat RotationRate;
181   GLfloat Angle[4];
182   GLfloat Vector[3];
183   GLfloat Translation[3];
184   GLfloat Color[3];
185   GLuint platelist;
186 } plate;
187
188 /* structure for holding the planet data */
189 typedef struct {
190   plate plates[NUM_PLATES];
191   GLXContext *glx_context;
192   Window window;
193
194   XColor fg, bg;
195
196   GLfloat tx, ty, tz;
197   GLfloat dtx, dty, dtz;
198   GLfloat xpos, ypos, zpos;
199   GLfloat dx, dy, dz;
200   GLfloat box_width, box_height, box_depth;
201
202 } planetstruct;
203
204
205 static planetstruct *planets = NULL;
206
207
208 /* Set up and enable texturing on our object */
209 static void
210 setup_xbm_texture (char *bits, int width, int height,
211                                    XColor *fgc, XColor *bgc)
212 {
213   unsigned int fg = (((fgc->red  >> 8) << 16) |
214                                          ((fgc->green >> 8) << 8) |
215                                          ((fgc->blue >> 8)));
216   unsigned int bg = (((bgc->red  >> 8) << 16) |
217                                          ((bgc->green >> 8) << 8) |
218                                          ((bgc->blue >> 8)));
219
220   unsigned char *data = (unsigned char *)
221         malloc ((width * height * 24) / 8);
222   unsigned char *out = data;
223   int x, y;
224
225   for (y = 0; y < height; y++)
226         for (x = 0; x < width; x++)
227           {
228                 unsigned char byte = bits [(y * (width / 8) + (x / 8))];
229                 unsigned char bit = (byte & (1 << (x % 8))) >> (x % 8);
230                 unsigned int word = (bit ? bg : fg);
231                 *out++ = (word & 0xFF0000) >> 16;
232                 *out++ = (word & 0x00FF00) >> 8;
233                 *out++ = (word & 0x0000FF);
234           }
235
236   glEnable(GL_TEXTURE_2D);
237   glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0,
238                            GL_RGB, GL_UNSIGNED_BYTE, data);
239
240   /* setup parameters for texturing */
241   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
242   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
243   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
244   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
245   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
246   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
247 }
248
249
250 static void
251 setup_file_texture (ModeInfo *mi, char *filename)
252 {
253   Display *dpy = mi->dpy;
254   Visual *visual = mi->xgwa.visual;
255   Colormap cmap = mi->xgwa.colormap;
256
257 #ifdef HAVE_XPM
258   {
259         char **xpm_data = 0;
260         int result = XpmReadFileToData (filename, &xpm_data);
261         switch (result) {
262         case XpmSuccess:
263           {
264                 XImage *image = xpm_to_ximage (dpy, visual, cmap, xpm_data);
265
266                 glEnable(GL_TEXTURE_2D);
267                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
268                                          image->width, image->height, 0,
269                                          GL_RGBA, GL_UNSIGNED_BYTE, image->data);
270
271                 /* setup parameters for texturing */
272                 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
273                 glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
274
275                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
276                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
277                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
278                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
279                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
280                 return;
281           }
282           break;
283
284         case XpmOpenFailed:
285           fprintf (stderr, "%s: file %s doesn't exist.\n", progname, filename);
286           exit (-1);
287           break;
288
289         case XpmFileInvalid:
290           /* Fall through and try it as an XBM. */
291           break;
292
293         case XpmNoMemory:
294           fprintf (stderr, "%s: XPM: out of memory\n", progname);
295           exit (-1);
296           break;
297
298         default:
299           fprintf (stderr, "%s: XPM: unknown error code %d\n", progname, result);
300           exit (-1);
301           break;
302         }
303   }
304 #endif /* HAVE_XPM */
305
306 #ifdef HAVE_XMU
307   {
308         planetstruct *gp = &planets[MI_SCREEN(mi)];
309         unsigned int width = 0;
310         unsigned int height = 0;
311         unsigned char *data = 0;
312         int xhot, yhot;
313         int status = XmuReadBitmapDataFromFile (filename, &width, &height, &data,
314                                                                                         &xhot, &yhot);
315         if (status != Success)
316           {
317 # ifdef HAVE_XPM
318                 fprintf (stderr, "%s: not an XPM file: %s\n", progname, filename);
319 # endif
320                 fprintf (stderr, "%s: not an XBM file: %s\n", progname, filename);
321                 exit (1);
322           }
323
324         setup_xbm_texture (data, width, height, &gp->fg, &gp->bg);
325   }
326 #else  /* !XMU */
327
328 # ifdef HAVE_XPM
329   fprintf (stderr, "%s: not an XPM file: %s\n", progname, filename);
330 # endif
331   fprintf (stderr, "%s: your vendor doesn't ship the standard Xmu library.\n",
332                    progname);
333   fprintf (stderr, "%s: we can't load XBM files without it.\n",progname);
334   exit (1);
335 #endif /* !XMU */
336 }
337
338
339 static void
340 setup_texture(ModeInfo * mi)
341 {
342   planetstruct *gp = &planets[MI_SCREEN(mi)];
343   if (!which_image ||
344           !*which_image ||
345           !strcmp(which_image, "BUILTIN"))
346         setup_xbm_texture (earth_bits, earth_width, earth_height,
347                                            &gp->fg, &gp->bg);
348   else
349         setup_file_texture (mi, which_image);
350 }
351
352
353 /* Set up and enable lighting */
354 static void
355 setup_light(void)
356 {
357
358   glEnable(GL_DEPTH_TEST);
359   glEnable(GL_AUTO_NORMAL);
360   glEnable(GL_NORMALIZE);
361   glShadeModel(GL_SMOOTH);
362
363   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
364   glEnable(GL_LINE_SMOOTH);
365   glEnable(GL_BLEND);
366
367   glEnable(GL_LIGHTING);
368   glEnable(GL_LIGHT0);
369
370   glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
371   glEnable(GL_COLOR_MATERIAL);
372
373 }
374
375
376 /* a stipple pattern */
377 static GLubyte halftone[] =
378 {
379   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
380   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
381   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
382   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
383   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
384   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
385   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
386   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
387   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
388   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
389   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
390   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
391   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
392   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
393   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
394   0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
395 };
396
397 /* Set up and enable stippling */
398 static void
399 setup_stipple(void)
400 {
401   glEnable(GL_POLYGON_STIPPLE);
402   glPolygonStipple(halftone);
403 }
404
405
406 /* Set up and enable face culling so we don't see the inside of the sphere */
407 static void
408 setup_face(void)
409 {
410   glEnable(GL_CULL_FACE);
411   glCullFace(GL_BACK); 
412 }
413
414
415 /* Function for determining points on the surface of the sphere */
416 void ParametricSphere(float theta, float rho, float *vector)
417 {
418     vector[0] = -sin(theta) * sin(rho);
419     vector[1] = cos(theta) * sin(rho);
420     vector[2] = cos(rho);
421         return;
422 }
423
424
425 /* Initialization function for screen saver */
426 static void
427 pinit(ModeInfo * mi)
428 {
429   Bool wire = MI_IS_WIREFRAME(mi);
430   planetstruct *gp = &planets[MI_SCREEN(mi)];
431   int i, j, list, dllist;
432   int stacks=STACKS, slices=SLICES;
433   float radius=RADIUS;
434
435   float drho, dtheta;
436   float rho, theta;
437   float vector[3];
438   float ds, dt, t, s;;
439
440   if (wire)
441         do_texture = False;
442
443   /* turn on various options we like */
444   if (do_texture)
445         setup_texture(mi);
446   if (do_light)
447         setup_light();
448   if (do_stipple)
449         setup_stipple();
450
451   setup_face();
452
453   dllist=glGenLists(NUM_PLATES);
454
455   drho = M_PI / stacks;
456   dtheta = 2.0 * M_PI / slices;
457   ds = 1.0 / slices;
458   dt = 1.0 / stacks;
459   t = 0.0 ;
460   
461
462   /*-
463    * Generate a huge sphere with quadrilaterals.
464    * Each quad is stored in its own display list; this is so we can
465    * move the quads around later (not yet done).
466    * Quad vertices are determined using a parametric sphere function.
467    * For fun, you could generate practically any parameteric surface and
468    * map an image onto it. 
469    */
470
471   list = 0;
472   for(i=0; i<stacks; i++) {
473         rho = i * drho;
474         s = 0.0;
475         for(j=0; j<slices+1; j++) {
476           theta = j * dtheta;
477
478           gp->plates[i].Translation[0] = 0.;
479           gp->plates[i].Translation[1] = 0.;
480           gp->plates[i].Translation[2] = 0.;
481
482           gp->plates[i].RotationRate = 0.;
483           gp->plates[i].Angle[0] = 0.;
484           gp->plates[i].Angle[1] = 0.;
485           gp->plates[i].Angle[2] = 0.;
486           gp->plates[i].Angle[3] = 0.;
487
488           gp->plates[i].Color[0] = 1.;
489           gp->plates[i].Color[1] = 1.;
490           gp->plates[i].Color[2] = 1.;
491
492           gp->plates[list].platelist = dllist+list;
493           glNewList(gp->plates[list].platelist, GL_COMPILE);
494           glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
495
496           glColor3f(gp->plates[i].Color[0], gp->plates[i].Color[1], gp->plates[i].Color[2]);
497
498           glTexCoord2f(s,t);
499           ParametricSphere(theta, rho, vector);
500           glNormal3fv(vector);
501           glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
502
503           glTexCoord2f(s,t+dt);
504           ParametricSphere(theta, rho+drho, vector);
505           glNormal3fv(vector);
506           glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
507
508           glTexCoord2f(s+ds,t+dt);
509           ParametricSphere(theta + dtheta, rho+drho, vector);
510           glNormal3fv(vector);
511           glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
512
513           glTexCoord2f(s+ds, t);
514           ParametricSphere(theta + dtheta, rho, vector);
515           glNormal3fv(vector);
516           glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );
517
518           glEnd();
519           s = s + ds;
520
521           glEndList();
522
523           list++;
524         }
525         t = t + dt;
526   }
527
528
529  }
530
531 static void
532 draw(ModeInfo * mi)
533 {
534   int i;
535   planetstruct *gp = &planets[MI_SCREEN(mi)];
536
537   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
538
539   for (i=0; i < NUM_PLATES; i++) {
540         glPushMatrix();
541         /* currently, the angle and translation are 0, but later this can
542          * help us move the surface around */
543 #if 0
544         glRotatef(gp->plates[i].Angle[0],
545                           gp->plates[i].Angle[1],
546                           gp->plates[i].Angle[2],
547                           gp->plates[i].Angle[3]); 
548         glTranslatef(gp->plates[i].Translation[0],
549                                  gp->plates[i].Translation[1],
550                                  gp->plates[i].Translation[2]);
551 #endif
552         glCallList(gp->plates[i].platelist);
553         glPopMatrix();
554 #if 0
555         gp->plates[i].Angle[0] += gp->plates[i].RotationRate;
556 #endif
557   }
558 }
559
560
561 #define RANDSIGN() ((random() & 1) ? 1 : -1)
562
563 static void
564 pick_velocity (ModeInfo * mi)
565 {
566   planetstruct *gp = &planets[MI_SCREEN(mi)];
567
568   gp->box_width =  15.0;
569   gp->box_height = 15.0;
570   gp->box_depth =  60.0;
571
572   gp->tx = 0.0;
573   gp->ty = 0.0;
574   gp->tz = frand(360);
575
576   gp->dtx = (frand(0.4) + frand(0.3)) * RANDSIGN();
577   gp->dty = (frand(0.4) + frand(0.3)) * RANDSIGN();
578   gp->dtz = (frand(5.0) + frand(5.0));  /* the sun sets in the west */
579
580   gp->dx = (frand(0.2) + frand(0.2)) * RANDSIGN();
581   gp->dy = (frand(0.2) + frand(0.2)) * RANDSIGN();
582   gp->dz = (frand(0.2) + frand(0.2)) * RANDSIGN();
583 }
584
585
586 static void
587 rotate_and_move (ModeInfo * mi)
588 {
589   planetstruct *gp = &planets[MI_SCREEN(mi)];
590
591   if (do_roll)
592         {
593           gp->tx += gp->dtx;
594           while (gp->tx < 0)   gp->tx += 360;
595           while (gp->tx > 360) gp->tx -= 360;
596
597           gp->ty += gp->dty;
598           while (gp->ty < 0)   gp->ty += 360;
599           while (gp->ty > 360) gp->ty -= 360;
600         }
601
602   if (do_rotate)
603         {
604           gp->tz += gp->dtz;
605           while (gp->tz < 0)   gp->tz += 360;
606           while (gp->tz > 360) gp->tz -= 360;
607         }
608
609   if (do_bounce)
610         {
611           /* Move in the direction we had been moving in. */
612           gp->xpos += gp->dx;
613           gp->ypos += gp->dy;
614           gp->zpos += gp->dz;
615
616           /* Bounce. */
617           if (gp->xpos > gp->box_depth)
618                 gp->xpos = gp->box_depth, gp->dx = -gp->dx;
619           else if (gp->xpos < 0)
620                 gp->xpos = 0, gp->dx = -gp->dx;
621
622           if (gp->ypos > gp->box_width/2)
623                 gp->ypos = gp->box_width/2, gp->dy = -gp->dy;
624           else if (gp->ypos < -gp->box_width/2)
625                 gp->ypos = -gp->box_width/2, gp->dy = -gp->dy;
626
627           if (gp->zpos > gp->box_height/2)
628                 gp->zpos = gp->box_height/2, gp->dz = -gp->dz;
629           else if (gp->zpos < -gp->box_height/2)
630                 gp->zpos = -gp->box_height/2, gp->dz = -gp->dz;
631         }
632 }
633
634
635 /* Standard reshape function */
636 #define DIST 40
637 static void
638 reshape(int width, int height)
639 {
640   GLfloat light[4];
641   GLfloat h = (GLfloat) height / (GLfloat) width;
642
643   light[0] = -1;
644   light[1] = (int) (((random() % 3) & 0xFF) - 1);
645   light[2] = (int) (((random() % 3) & 0xFF) - 1);
646   light[3] = 0;
647
648   glViewport(0, 0, (GLint) width, (GLint) height);
649   glMatrixMode(GL_PROJECTION);
650   glLoadIdentity();
651   glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
652   glMatrixMode(GL_MODELVIEW);
653   glLoadIdentity();
654   glTranslatef(0.0, 0.0, -DIST);
655   /* some messiness for orienting the earth normally */
656   glRotatef(90,0,0,1);
657   glRotatef(90,0,1,0);
658   glLightfv(GL_LIGHT0, GL_POSITION, light);
659   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
660
661 }
662
663
664 void
665 init_planet(ModeInfo * mi)
666 {
667   int         screen = MI_SCREEN(mi);
668
669   planetstruct *gp;
670
671   if (planets == NULL) {
672         if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
673                                                                                   sizeof (planetstruct))) == NULL)
674           return;
675   }
676   gp = &planets[screen];
677
678   pick_velocity (mi);
679
680   {
681         char *f = get_string_resource("imageForeground", "Foreground");
682         char *b = get_string_resource("imageBackground", "Background");
683         char *s;
684         if (!f) f = strdup("white");
685         if (!b) b = strdup("black");
686         
687         for (s = f + strlen(f)-1; s > f; s--)
688           if (*s == ' ' || *s == '\t')
689                 *s = 0;
690         for (s = b + strlen(b)-1; s > b; s--)
691           if (*s == ' ' || *s == '\t')
692                 *s = 0;
693
694     if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
695       {
696                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
697                 exit(1);
698       }
699     if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
700       {
701                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
702                 exit(1);
703       }
704
705         free (f);
706         free (b);
707   }
708
709
710   gp->window = MI_WINDOW(mi);
711   if ((gp->glx_context = init_GL(mi)) != NULL) {
712         reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
713         pinit(mi);
714   } else {
715         MI_CLEARWINDOW(mi);
716   }
717 }
718
719 void
720 draw_planet(ModeInfo * mi)
721 {
722   planetstruct *gp = &planets[MI_SCREEN(mi)];
723   Display    *display = MI_DISPLAY(mi);
724   Window      window = MI_WINDOW(mi);
725
726   if (!gp->glx_context)
727         return;
728
729   glDrawBuffer(GL_BACK);
730
731   glXMakeCurrent(display, window, *(gp->glx_context));
732
733   glPushMatrix();
734   {
735         glTranslatef(gp->xpos, gp->ypos, gp->zpos);
736         glRotatef(gp->tx, 1, 0, 0);
737         glRotatef(gp->ty, 0, 1, 0);
738         glRotatef(gp->tz, 0, 0, 1);
739         draw(mi);
740   }
741   glPopMatrix();
742
743   glFinish();
744   glXSwapBuffers(display, window);
745
746   rotate_and_move (mi);
747 }
748
749 void
750 release_planet(ModeInfo * mi)
751 {
752   int i;
753   if (planets != NULL) {
754         int         screen;
755
756         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
757           planetstruct *gp = &planets[screen];
758
759           if (gp->glx_context) {
760                 /* Display lists MUST be freed while their glXContext is current. */
761                 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
762
763                 for (i=0; i < NUM_PLATES; i++) {
764                   if (glIsList(gp->plates[i].platelist))
765                         glDeleteLists(gp->plates[i].platelist, 1);
766                 }
767           }
768         }
769         (void) free((void *) planets);
770         planets = NULL;
771   }
772   FreeAllGL(mi);
773 }
774
775
776 #endif
777