From http://www.jwz.org/xscreensaver/xscreensaver-5.40.tar.gz
[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  * 10-Nov-14: jwz@jwz.org   Night map. Better stars.
19  * 16-Jan-02: jwz@jwz.org   gdk_pixbuf support.
20  * 21-Mar-01: jwz@jwz.org   Broke sphere routine out into its own file.
21  *
22  * 9-Oct-98:  dek@cgl.ucsf.edu  Added stars.
23  *
24  * 8-Oct-98:  jwz@jwz.org   Made the 512x512x1 xearth image be built in.
25  *                          Made it possible to load XPM or XBM files.
26  *                          Made the planet bounce and roll around.
27  *
28  * 8-Oct-98: Released initial version of "glplanet"
29  * (David Konerding, dek@cgl.ucsf.edu)
30  */
31
32
33 #ifdef STANDALONE
34 #define DEFAULTS        "*delay:                        20000   \n"     \
35                                         "*showFPS:                      False   \n" \
36                                         "*wireframe:            False   \n"     \
37                                         "*imageForeground:      Green   \n" \
38                                         "*imageBackground:      Blue    \n" \
39                                         "*suppressRotationAnimation: True\n" \
40
41 # define release_planet 0
42 # include "xlockmore.h"                         /* from the xscreensaver distribution */
43 #else  /* !STANDALONE */
44 # include "xlock.h"                                     /* from the xlockmore distribution */
45 #endif /* !STANDALONE */
46
47 #ifdef USE_GL /* whole file */
48
49 #include "sphere.h"
50
51 #ifdef HAVE_XMU
52 # ifndef VMS
53 #  include <X11/Xmu/Drawing.h>
54 #else  /* VMS */
55 #  include <Xmu/Drawing.h>
56 # endif /* VMS */
57 #endif
58
59 #define DEF_ROTATE  "True"
60 #define DEF_ROLL    "True"
61 #define DEF_WANDER  "True"
62 #define DEF_SPIN    "1.0"
63 #define DEF_TEXTURE "True"
64 #define DEF_STARS   "True"
65 #define DEF_RESOLUTION "128"
66 #define DEF_IMAGE   "BUILTIN"
67 #define DEF_IMAGE2  "BUILTIN"
68
69 #define BLENDED_TERMINATOR
70
71 #undef countof
72 #define countof(x) (sizeof((x))/sizeof((*x)))
73
74 #undef BELLRAND
75 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
76
77 static int do_rotate;
78 static int do_roll;
79 static int do_wander;
80 static int do_texture;
81 static int do_stars;
82 static char *which_image;
83 static char *which_image2;
84 static int resolution;
85 static GLfloat spin_arg;
86
87 static XrmOptionDescRec opts[] = {
88   {"-rotate",  ".rotate",  XrmoptionNoArg, "true" },
89   {"+rotate",  ".rotate",  XrmoptionNoArg, "false" },
90   {"-roll",    ".roll",    XrmoptionNoArg, "true" },
91   {"+roll",    ".roll",    XrmoptionNoArg, "false" },
92   {"-wander",  ".wander",  XrmoptionNoArg, "true" },
93   {"+wander",  ".wander",  XrmoptionNoArg, "false" },
94   {"-texture", ".texture", XrmoptionNoArg, "true" },
95   {"+texture", ".texture", XrmoptionNoArg, "false" },
96   {"-stars",   ".stars",   XrmoptionNoArg, "true" },
97   {"+stars",   ".stars",   XrmoptionNoArg, "false" },
98   {"-spin",    ".spin",    XrmoptionSepArg, 0 },
99   {"-image",   ".image",   XrmoptionSepArg, 0 },
100   {"-image2",  ".image2",  XrmoptionSepArg, 0 },
101   {"-resolution", ".resolution", XrmoptionSepArg, 0 },
102 };
103
104 static argtype vars[] = {
105   {&do_rotate,   "rotate",  "Rotate",  DEF_ROTATE,  t_Bool},
106   {&do_roll,     "roll",    "Roll",    DEF_ROLL,    t_Bool},
107   {&do_wander,   "wander",  "Wander",  DEF_WANDER,  t_Bool},
108   {&do_texture,  "texture", "Texture", DEF_TEXTURE, t_Bool},
109   {&do_stars,    "stars",   "Stars",   DEF_STARS,   t_Bool},
110   {&spin_arg,    "spin",    "Spin",    DEF_SPIN,    t_Float},
111   {&which_image, "image",   "Image",   DEF_IMAGE,   t_String},
112   {&which_image2,"image2",  "Image",   DEF_IMAGE2,  t_String},
113   {&resolution,  "resolution","Resolution", DEF_RESOLUTION, t_Int},
114 };
115
116 ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
117
118 #ifdef USE_MODULES
119 ModStruct   planet_description =
120 {"planet", "init_planet", "draw_planet", NULL,
121  "draw_planet", "init_planet", "free_planet", &planet_opts,
122  1000, 1, 2, 1, 4, 1.0, "",
123  "Animates texture mapped sphere (planet)", 0, NULL};
124 #endif
125
126 #include "images/gen/earth_png.h"
127 #include "images/gen/earth_night_png.h"
128
129 #include "ximage-loader.h"
130 #include "rotator.h"
131 #include "gltrackball.h"
132
133
134 /*-
135  * slices and stacks are used in the sphere parameterization routine.
136  * more slices and stacks will increase the quality of the sphere,
137  * at the expense of rendering speed
138  */
139
140 /* structure for holding the planet data */
141 typedef struct {
142   GLuint platelist;
143   GLuint shadowlist;
144   GLuint latlonglist;
145   GLuint starlist;
146   int starcount;
147   int screen_width, screen_height;
148   GLXContext *glx_context;
149   Window window;
150   GLfloat z;
151   GLfloat tilt;
152   rotator *rot;
153   trackball_state *trackball;
154   Bool button_down_p;
155   GLuint tex1, tex2;
156   int draw_axis;
157
158 } planetstruct;
159
160
161 static planetstruct *planets = NULL;
162
163
164 /* Set up and enable texturing on our object */
165 static void
166 setup_xpm_texture (ModeInfo *mi, const unsigned char *data, unsigned long size)
167 {
168   XImage *image = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
169                                         data, size);
170   char buf[1024];
171   clear_gl_error();
172   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
173   /* iOS invalid enum:
174   glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
175   */
176   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
177                image->width, image->height, 0,
178                GL_RGBA, GL_UNSIGNED_BYTE, image->data);
179   sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
180   check_gl_error(buf);
181 }
182
183
184 static Bool
185 setup_file_texture (ModeInfo *mi, char *filename)
186 {
187   Display *dpy = mi->dpy;
188   Visual *visual = mi->xgwa.visual;
189   char buf[1024];
190
191   XImage *image = file_to_ximage (dpy, visual, filename);
192   if (!image) return False;
193
194   clear_gl_error();
195   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
196   glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
197   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
198                image->width, image->height, 0,
199                GL_RGBA, GL_UNSIGNED_BYTE, image->data);
200   sprintf (buf, "texture: %.100s (%dx%d)",
201            filename, image->width, image->height);
202   check_gl_error(buf);
203   return True;
204 }
205
206
207 static void
208 setup_texture (ModeInfo * mi)
209 {
210   planetstruct *gp = &planets[MI_SCREEN(mi)];
211
212   glGenTextures (1, &gp->tex1);
213   glBindTexture (GL_TEXTURE_2D, gp->tex1);
214
215   /* Must be after glBindTexture */
216   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
217   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
218   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
219   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
220   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
221
222   if (!which_image ||
223           !*which_image ||
224           !strcmp(which_image, "BUILTIN"))
225     {
226     BUILTIN1:
227       setup_xpm_texture (mi, earth_png, sizeof(earth_png));
228     }
229   else
230     {
231       if (! setup_file_texture (mi, which_image))
232         goto BUILTIN1;
233     }
234
235   check_gl_error("texture 1 initialization");
236
237   glGenTextures (1, &gp->tex2);
238   glBindTexture (GL_TEXTURE_2D, gp->tex2);
239
240   /* Must be after glBindTexture */
241   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
242   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
243   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
244   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
245   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
246
247   if (!which_image2 ||
248           !*which_image2 ||
249           !strcmp(which_image2, "BUILTIN"))
250     {
251     BUILTIN2:
252       setup_xpm_texture (mi, earth_night_png, sizeof(earth_night_png));
253     }
254   else
255     {
256       if (! setup_file_texture (mi, which_image2))
257         goto BUILTIN2;
258     }
259
260   check_gl_error("texture 2 initialization");
261
262   /* Need to flip the texture top for bottom for some reason. */
263   glMatrixMode (GL_TEXTURE);
264   glScalef (1, -1, 1);
265   glMatrixMode (GL_MODELVIEW);
266 }
267
268
269 static void
270 init_stars (ModeInfo *mi)
271 {
272   planetstruct *gp = &planets[MI_SCREEN(mi)];
273   int i, j;
274   int width  = MI_WIDTH(mi);
275   int height = MI_HEIGHT(mi);
276   int size = (width > height ? width : height);
277   int nstars = size * size / 80;
278   int max_size = 3;
279   GLfloat inc = 0.5;
280   int steps = max_size / inc;
281   GLfloat scale = 1;
282
283   if (MI_WIDTH(mi) > 2560) {  /* Retina displays */
284     scale *= 2;
285     nstars /= 2;
286   }
287
288   gp->starlist = glGenLists(1);
289   glNewList(gp->starlist, GL_COMPILE);
290   for (j = 1; j <= steps; j++)
291     {
292       glPointSize(inc * j * scale);
293       glBegin (GL_POINTS);
294       for (i = 0; i < nstars / steps; i++)
295         {
296           GLfloat d = 0.1;
297           GLfloat r = 0.15 + frand(0.3);
298           GLfloat g = r + frand(d) - d;
299           GLfloat b = r + frand(d) - d;
300
301           GLfloat x = frand(1)-0.5;
302           GLfloat y = frand(1)-0.5;
303           GLfloat z = ((random() & 1)
304                        ? frand(1)-0.5
305                        : (BELLRAND(1)-0.5)/12);   /* milky way */
306           d = sqrt (x*x + y*y + z*z);
307           x /= d;
308           y /= d;
309           z /= d;
310           glColor3f (r, g, b);
311           glVertex3f (x, y, z);
312           gp->starcount++;
313         }
314       glEnd ();
315     }
316   glEndList ();
317
318   check_gl_error("stars initialization");
319 }
320
321
322 #ifdef BLENDED_TERMINATOR
323 static void
324 terminator_tube (ModeInfo *mi, int resolution)
325 {
326   Bool wire = MI_IS_WIREFRAME(mi);
327   GLfloat th;
328   GLfloat step = M_PI*2 / resolution;
329   GLfloat thickness = 0.1;  /* Dusk is about an hour wide. */
330   GLfloat c1[] = { 0, 0, 0, 1 };
331   GLfloat c2[] = { 0, 0, 0, 0 };
332
333   glPushMatrix();
334   if (wire)
335     {
336       c1[0] = c1[1] = 0.5;
337       c2[2] = 0.5;
338       glLineWidth (4);
339     }
340   glRotatef (90, 1, 0, 0);
341   glScalef (1.02, 1.02, 1.02);
342   glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
343   for (th = 0; th < M_PI*2 + step; th += step)
344     {
345       GLfloat x = cos(th);
346       GLfloat y = sin(th);
347       glColor4fv (c1);
348       if (!do_texture)
349         glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, c1);
350       glNormal3f (x, y, 0);
351       glVertex3f (x, y,  thickness);
352       glColor4fv (c2);
353       if (!do_texture)
354         glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, c2);
355       glVertex3f (x, y, -thickness);
356     }
357   glEnd();
358
359   /* There's a bit of a spike in the shading where the tube overlaps
360      the sphere, so extend the sphere a lot to try and avoid that. */
361 # if 0 /* Nope, that doesn't help. */
362   glColor4fv (c1);
363   if (!do_texture)
364     glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, c1);
365   glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
366   for (th = 0; th < M_PI*2 + step; th += step)
367     {
368       GLfloat x = cos(th);
369       GLfloat y = sin(th);
370       glNormal3f (x, y, 0);
371       glVertex3f (x, y, thickness);
372       glVertex3f (x, y, thickness + 10);
373     }
374   glEnd();
375
376   glColor4fv (c2);
377   if (!do_texture)
378     glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, c2);
379   glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
380   for (th = 0; th < M_PI*2 + step; th += step)
381     {
382       GLfloat x = cos(th);
383       GLfloat y = sin(th);
384       glNormal3f (x, y, 0);
385       glVertex3f (x, y, -thickness);
386       glVertex3f (x, y, -thickness - 10);
387     }
388   glEnd();
389 # endif /* 0 */
390
391   glPopMatrix();
392 }
393 #endif /* BLENDED_TERMINATOR */
394
395
396 ENTRYPOINT void
397 reshape_planet (ModeInfo *mi, int width, int height)
398 {
399   planetstruct *gp = &planets[MI_SCREEN(mi)];
400   GLfloat h = (GLfloat) height / (GLfloat) width;
401
402   glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
403
404   glViewport(0, 0, (GLint) width, (GLint) height);
405   glMatrixMode(GL_PROJECTION);
406   glLoadIdentity();
407   glFrustum(-1.0, 1.0, -h, h, 5.0, 200.0);
408   glMatrixMode(GL_MODELVIEW);
409   glLoadIdentity();
410   glTranslatef(0.0, 0.0, -40);
411
412 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
413   {
414     int o = (int) current_device_rotation();
415     if (o != 0 && o != 180 && o != -180)
416       glScalef (h, h, h);
417   }
418 # endif
419
420   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
421 }
422
423
424 ENTRYPOINT Bool
425 planet_handle_event (ModeInfo *mi, XEvent *event)
426 {
427   planetstruct *gp = &planets[MI_SCREEN(mi)];
428
429   if (gltrackball_event_handler (event, gp->trackball,
430                                  MI_WIDTH (mi), MI_HEIGHT (mi),
431                                  &gp->button_down_p))
432     return True;
433
434   return False;
435 }
436
437
438 ENTRYPOINT void
439 init_planet (ModeInfo * mi)
440 {
441   planetstruct *gp;
442   int screen = MI_SCREEN(mi);
443   Bool wire = MI_IS_WIREFRAME(mi);
444
445   MI_INIT (mi, planets);
446   gp = &planets[screen];
447
448   gp->window = MI_WINDOW(mi);
449
450   if ((gp->glx_context = init_GL(mi)) != NULL) {
451         reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
452   }
453
454   {
455         char *f = get_string_resource(mi->dpy, "imageForeground", "Foreground");
456         char *b = get_string_resource(mi->dpy, "imageBackground", "Background");
457         char *s;
458         if (!f) f = strdup("white");
459         if (!b) b = strdup("black");
460         
461         for (s = f + strlen(f)-1; s > f; s--)
462           if (*s == ' ' || *s == '\t')
463                 *s = 0;
464         for (s = b + strlen(b)-1; s > b; s--)
465           if (*s == ' ' || *s == '\t')
466                 *s = 0;
467
468         free (f);
469         free (b);
470   }
471
472   {
473     double spin_speed   = 0.1;
474     double wander_speed = 0.005;
475     gp->rot = make_rotator (do_roll ? spin_speed : 0,
476                             do_roll ? spin_speed : 0,
477                             0, 1,
478                             do_wander ? wander_speed : 0,
479                             True);
480     gp->z = frand (1.0);
481     gp->tilt = frand (23.4);
482     gp->trackball = gltrackball_init (True);
483   }
484
485   if (!wire && !do_texture)
486     {
487       GLfloat pos[4] = {1, 1, 1, 0};
488       GLfloat amb[4] = {0, 0, 0, 1};
489       GLfloat dif[4] = {1, 1, 1, 1};
490       GLfloat spc[4] = {0, 1, 1, 1};
491       glEnable(GL_LIGHTING);
492       glEnable(GL_LIGHT0);
493       glLightfv(GL_LIGHT0, GL_POSITION, pos);
494       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
495       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
496       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
497     }
498
499   if (wire)
500     do_texture = False;
501
502   if (do_texture)
503     setup_texture (mi);
504
505   if (do_stars)
506     init_stars (mi);
507
508   /* construct the polygons of the planet
509    */
510   gp->platelist = glGenLists(1);
511   glNewList (gp->platelist, GL_COMPILE);
512   glFrontFace(GL_CCW);
513   glPushMatrix();
514   glRotatef (90, 1, 0, 0);
515   unit_sphere (resolution, resolution, wire);
516   glPopMatrix();
517   glEndList();
518
519   gp->shadowlist = glGenLists(1);
520   glNewList (gp->shadowlist, GL_COMPILE);
521   glFrontFace(GL_CCW);
522
523   if (wire)
524     glColor4f (0.5, 0.5, 0, 1);
525 # ifdef BLENDED_TERMINATOR
526   else
527     {
528       GLfloat c[] = { 0, 0, 0, 1 };
529       glColor4fv (c);
530       if (!do_texture)
531         glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, c);
532     }
533 # endif
534
535   glPushMatrix();
536   glScalef (1.01, 1.01, 1.01);
537   unit_dome (resolution, resolution, wire);
538
539 # ifdef BLENDED_TERMINATOR
540   terminator_tube (mi, resolution);
541   if (!wire)
542     {
543       /* We have to draw the transparent side of the mask too, 
544          though I'm not sure why. */
545       GLfloat c[] = { 0, 0, 0, 0 };
546       glColor4fv (c);
547       if (!do_texture)
548         glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, c);
549       glRotatef (180, 1, 0, 0);
550       unit_dome (resolution, resolution, wire);
551     }
552 # endif
553
554   glPopMatrix();
555   glEndList();
556
557   /* construct the polygons of the latitude/longitude/axis lines.
558    */
559   gp->latlonglist = glGenLists(1);
560   glNewList (gp->latlonglist, GL_COMPILE);
561   glPushMatrix ();
562   glRotatef (90, 1, 0, 0);  /* unit_sphere is off by 90 */
563   glRotatef (8,  0, 1, 0);  /* line up the time zones */
564   unit_sphere (12, 24, 1);
565   unit_sphere (12, 24, 1);
566   glBegin(GL_LINES);
567   glVertex3f(0, -2, 0);
568   glVertex3f(0,  2, 0);
569   glEnd();
570   glPopMatrix ();
571   glEndList();
572 }
573
574
575 ENTRYPOINT void
576 draw_planet (ModeInfo * mi)
577 {
578   planetstruct *gp = &planets[MI_SCREEN(mi)];
579   int wire = MI_IS_WIREFRAME(mi);
580   Display *dpy = MI_DISPLAY(mi);
581   Window window = MI_WINDOW(mi);
582   double x, y, z;
583
584   if (!gp->glx_context)
585         return;
586
587   glDrawBuffer(GL_BACK);
588   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
589
590   glXMakeCurrent (dpy, window, *(gp->glx_context));
591
592   mi->polygon_count = 0;
593
594   if (gp->button_down_p)
595     gp->draw_axis = 60;
596   else if (!gp->draw_axis && !(random() % 1000))
597     gp->draw_axis = 60 + (random() % 90);
598
599   if (do_rotate && !gp->button_down_p)
600     {
601       gp->z -= 0.001 * spin_arg;     /* the sun sets in the west */
602       if (gp->z < 0) gp->z += 1;
603     }
604
605   glEnable(GL_LINE_SMOOTH);
606   glEnable(GL_POINT_SMOOTH);
607   glEnable(GL_DEPTH_TEST);
608   glEnable(GL_CULL_FACE);
609   glCullFace(GL_BACK); 
610
611   glPushMatrix();
612
613   get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
614   x = (x - 0.5) * 6;
615   y = (y - 0.5) * 6;
616   z = (z - 0.5) * 3;
617   glTranslatef(x, y, z);
618
619   gltrackball_rotate (gp->trackball);
620
621   if (do_roll)
622     {
623       get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
624       glRotatef (x * 360, 1.0, 0.0, 0.0);
625       glRotatef (y * 360, 0.0, 1.0, 0.0);
626     }
627   else
628     glRotatef (current_device_rotation(), 0, 0, 1);
629
630   if (do_stars)
631     {
632       glDisable(GL_TEXTURE_2D);
633       glPushMatrix();
634       glScalef (60, 60, 60);
635       glRotatef (90, 1, 0, 0);
636       glRotatef (35, 1, 0, 0);
637       glCallList (gp->starlist);
638       mi->polygon_count += gp->starcount;
639       glPopMatrix();
640       glClear(GL_DEPTH_BUFFER_BIT);
641     }
642
643   glRotatef (90, 1, 0, 0);
644   glRotatef (35, 1, 0, 0);
645   glRotatef (10, 0, 1, 0);
646   glRotatef (120, 0, 0, 1);
647
648   glScalef (3, 3, 3);
649
650 # ifdef HAVE_MOBILE
651   glScalef (2, 2, 2);
652 # endif
653
654   if (wire)
655     glColor3f (0, 0, 0.5);
656   else if (do_texture)
657     {
658       glColor4f (1, 1, 1, 1);
659       glEnable (GL_TEXTURE_2D);
660       glBindTexture (GL_TEXTURE_2D, gp->tex1);
661     }
662   else
663     {
664       GLfloat c[] = { 0, 0.5, 0, 1 };
665       glColor4fv (c);
666       glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, c);
667     }
668
669   glPushMatrix();
670   glRotatef (gp->z * 360, 0, 0, 1);
671   glCallList (gp->platelist);
672   mi->polygon_count += resolution*resolution;
673   glPopMatrix();
674
675   if (wire)
676     {
677       glPushMatrix();
678       glRotatef (gp->tilt, 1, 0, 0);
679       glColor3f(1, 0, 0);
680       glLineWidth(4);
681       glCallList (gp->shadowlist);
682       glLineWidth(1);
683       mi->polygon_count += resolution*(resolution/2);
684       glPopMatrix();
685     }
686
687   else if (!do_texture || gp->tex2)
688     {
689       /* Originally we just used GL_LIGHT0 to produce the day/night sides of
690          the planet, but that always looked crappy, even with a vast number of
691          polygons, because the day/night terminator didn't exactly line up with
692          the polygon edges.
693        */
694
695 #ifndef BLENDED_TERMINATOR
696
697       /* Method 1, use the depth buffer as a stencil.
698
699          - Draw the full "day" sphere;
700          - Clear the depth buffer;
701          - Draw a rotated/tilted half-sphere into the depth buffer only,
702            on the Eastern hemisphere, putting non-zero depth only on the
703            sunlit side;
704          - Draw the full "night" sphere, which will clip to dark parts only.
705
706          That lets us divide the sphere into the two maps, and the dividing
707          line can be at any angle, regardless of polygon layout.
708
709          The half-sphere is scaled slightly larger to avoid polygon fighting,
710          since those triangles won't exactly line up because of the rotation.
711
712          The downside of this is that the day/night terminator is 100% sharp.
713       */
714       glClear (GL_DEPTH_BUFFER_BIT);
715       glColorMask (0, 0, 0, 0);
716       glDisable (GL_TEXTURE_2D);
717       glPushMatrix();
718       glRotatef (gp->tilt, 1, 0, 0);
719       glScalef (1.01, 1.01, 1.01);
720       glCallList (gp->shadowlist);              /* Fill in depth on sunlit side */
721       mi->polygon_count += resolution*(resolution/2);
722       glPopMatrix();
723       glColorMask (1, 1, 1, 1);
724
725       if (do_texture)
726         {
727           glEnable (GL_TEXTURE_2D);
728           glBindTexture (GL_TEXTURE_2D, gp->tex2);
729         }
730       else
731         {
732           GLfloat c[] = { 0, 0, 0.5, 1 };
733           glColor4fv (c);
734           if (! do_texture)
735             glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, c);
736         }
737
738       glPushMatrix();
739       glRotatef (gp->z * 360, 0, 0, 1);
740       glCallList (gp->platelist);               /* Fill in color on night side */
741       mi->polygon_count += resolution*resolution;
742       glPopMatrix();
743
744 #else /* BLENDED_TERMINATOR */
745
746       /* Method 2, use the alpha buffer as a stencil.
747          - Draw the full "day" sphere;
748          - Clear the depth buffer; 
749          - Clear the alpha buffer;
750          - Draw a rotated/tilted half-sphere into the alpha buffer only,
751            on the Eastern hemisphere, putting non-zero alpha only on the
752            sunlit side;
753          - Also draw a fuzzy terminator ring into the alpha buffer;
754          - Clear the depth buffer again; 
755          - Draw the full "night" sphere, which will blend to dark parts only.
756        */
757       glColorMask (0, 0, 0, 1);
758       glClear (GL_COLOR_BUFFER_BIT);
759       glClear (GL_DEPTH_BUFFER_BIT);
760       glDisable (GL_TEXTURE_2D);
761
762       glPushMatrix();
763       glRotatef (gp->tilt, 1, 0, 0);
764       glScalef (1.01, 1.01, 1.01);
765       glCallList (gp->shadowlist);              /* Fill in alpha on sunlit side */
766       mi->polygon_count += resolution*(resolution/2);
767       glPopMatrix();
768
769       glClear (GL_DEPTH_BUFFER_BIT);
770
771       glColorMask (1, 1, 1, 1);
772       {
773         GLfloat c[] = { 1, 1, 1, 1 };
774         glColor4fv (c);
775         if (! do_texture)
776           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, c);
777       }
778       glEnable (GL_BLEND);
779       glBlendFunc (GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
780
781       if (do_texture)
782         {
783           glEnable (GL_TEXTURE_2D);
784           glBindTexture (GL_TEXTURE_2D, gp->tex2);
785         }
786       else
787         {
788           GLfloat c[] = { 0, 0, 0.5, 1 };
789           glColor4fv (c);
790           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, c);
791           glEnable (GL_LIGHTING);
792         }
793
794       glPushMatrix();
795       glRotatef (gp->z * 360, 0, 0, 1);
796       glCallList (gp->platelist);               /* Fill in color on night side */
797       mi->polygon_count += resolution*resolution;
798       glPopMatrix();
799       glDisable (GL_BLEND);
800       glBlendFunc (GL_ONE, GL_ZERO);
801
802 #endif /* BLENDED_TERMINATOR */
803     }
804
805   if (gp->draw_axis)
806     {
807       glPushMatrix();
808       glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
809       glScalef (1.02, 1.02, 1.02);
810       glDisable (GL_TEXTURE_2D);
811       glDisable (GL_LIGHTING);
812       glDisable (GL_LINE_SMOOTH);
813       glColor3f (0.1, 0.3, 0.1);
814       glCallList (gp->latlonglist);
815       mi->polygon_count += 24*24;
816       glPopMatrix();
817       if (!wire && !do_texture)
818         glEnable (GL_LIGHTING);
819       if (gp->draw_axis) gp->draw_axis--;
820     }
821   glPopMatrix();
822
823   if (mi->fps_p) do_fps (mi);
824   glFinish();
825   glXSwapBuffers(dpy, window);
826 }
827
828
829 ENTRYPOINT void
830 free_planet (ModeInfo * mi)
831 {
832   planetstruct *gp = &planets[MI_SCREEN(mi)];
833
834   if (gp->glx_context) {
835         glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
836
837         if (glIsList(gp->platelist))
838           glDeleteLists(gp->platelist, 1);
839         if (glIsList(gp->starlist))
840           glDeleteLists(gp->starlist, 1);
841   }
842 }
843
844
845 XSCREENSAVER_MODULE_2 ("GLPlanet", glplanet, planet)
846
847 #endif