http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.02.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  * 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 planet_opts                                    xlockmore_opts
51 #define DEFAULTS        "*delay:                        15000   \n"     \
52                                         "*showFPS:                      False   \n" \
53                     "*rotate:           True    \n" \
54                     "*roll:             True    \n" \
55                     "*wander:           True    \n" \
56                                         "*wireframe:            False   \n"     \
57                                         "*light:                        True    \n"     \
58                                         "*texture:                      True    \n" \
59                                         "*stars:                        True    \n" \
60                                         "*image:                        BUILTIN \n" \
61                                         "*imageForeground:      Green   \n" \
62                                         "*imageBackground:      Blue    \n"
63
64 # include "xlockmore.h"                         /* from the xscreensaver distribution */
65 #else  /* !STANDALONE */
66 # include "xlock.h"                                     /* from the xlockmore distribution */
67 #endif /* !STANDALONE */
68
69 #ifdef USE_GL /* whole file */
70
71 #include "sphere.h"
72
73 #ifdef HAVE_XMU
74 # ifndef VMS
75 #  include <X11/Xmu/Drawing.h>
76 #else  /* VMS */
77 #  include <Xmu/Drawing.h>
78 # endif /* VMS */
79 #endif
80
81
82 #include <GL/glu.h>
83
84 #define DEF_ROTATE  "True"
85 #define DEF_ROLL    "True"
86 #define DEF_WANDER  "True"
87 #define DEF_TEXTURE "True"
88 #define DEF_STARS   "True"
89 #define DEF_LIGHT   "True"
90 #define DEF_IMAGE   "BUILTIN"
91
92 #undef countof
93 #define countof(x) (sizeof((x))/sizeof((*x)))
94
95 static int do_rotate;
96 static int do_roll;
97 static int do_wander;
98 static int do_texture;
99 static int do_stars;
100 static int do_light;
101 static char *which_image;
102 static XrmOptionDescRec opts[] = {
103   {"-rotate",  ".glplanet.rotate",  XrmoptionNoArg, (caddr_t) "true" },
104   {"+rotate",  ".glplanet.rotate",  XrmoptionNoArg, (caddr_t) "false" },
105   {"-roll",    ".glplanet.roll",    XrmoptionNoArg, (caddr_t) "true" },
106   {"+roll",    ".glplanet.roll",    XrmoptionNoArg, (caddr_t) "false" },
107   {"-wander",  ".glplanet.wander",  XrmoptionNoArg, (caddr_t) "true" },
108   {"+wander",  ".glplanet.wander",  XrmoptionNoArg, (caddr_t) "false" },
109   {"-texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "true" },
110   {"+texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "false" },
111   {"-stars",   ".glplanet.stars",   XrmoptionNoArg, (caddr_t) "true" },
112   {"+stars",   ".glplanet.stars",   XrmoptionNoArg, (caddr_t) "false" },
113   {"-light",   ".glplanet.light",   XrmoptionNoArg, (caddr_t) "true" },
114   {"+light",   ".glplanet.light",   XrmoptionNoArg, (caddr_t) "false" },
115   {"-image",   ".glplanet.image",  XrmoptionSepArg, (caddr_t) 0 },
116 };
117
118 static argtype vars[] = {
119   {(caddr_t *) &do_rotate,   "rotate",  "Rotate",  DEF_ROTATE,  t_Bool},
120   {(caddr_t *) &do_roll,     "roll",    "Roll",    DEF_ROLL,    t_Bool},
121   {(caddr_t *) &do_wander,   "wander",  "Wander",  DEF_WANDER,  t_Bool},
122   {(caddr_t *) &do_texture,  "texture", "Texture", DEF_TEXTURE, t_Bool},
123   {(caddr_t *) &do_stars,  "stars", "Stars", DEF_STARS, t_Bool},
124   {(caddr_t *) &do_light,    "light",   "Light",   DEF_LIGHT,   t_Bool},
125   {(caddr_t *) &which_image, "image",   "Image",   DEF_IMAGE,   t_String},
126 };
127
128 ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
129
130 #ifdef USE_MODULES
131 ModStruct   planet_description =
132 {"planet", "init_planet", "draw_planet", "release_planet",
133  "draw_planet", "init_planet", NULL, &planet_opts,
134  1000, 1, 2, 1, 4, 1.0, "",
135  "Animates texture mapped sphere (planet)", 0, NULL};
136 #endif
137
138 #include "../images/earth.xpm"
139 #include "xpm-ximage.h"
140
141
142 /*-
143  * slices and stacks are used in the sphere parameterization routine.
144  * more slices and stacks will increase the quality of the sphere,
145  * at the expense of rendering speed
146  */
147
148 #define NUM_STARS 1000
149 #define SLICES 32
150 #define STACKS 32
151
152 /* radius of the sphere- fairly arbitrary */
153 #define RADIUS 4
154
155 /* distance away from the sphere model */
156 #define DIST 40
157
158
159
160 /* structure for holding the planet data */
161 typedef struct {
162   GLuint platelist;
163   GLuint starlist;
164   int screen_width, screen_height;
165   GLXContext *glx_context;
166   Window window;
167
168   XColor fg, bg;
169
170   GLfloat tx, ty, tz;
171   GLfloat dtx, dty, dtz;
172   GLfloat xpos, ypos, zpos;
173   GLfloat dx, dy, dz;
174   GLfloat box_width, box_height, box_depth;
175
176   GLfloat sunpos[4];
177
178 } planetstruct;
179
180
181 static planetstruct *planets = NULL;
182
183
184 static inline void
185 normalize(GLfloat v[3])
186 {
187   GLfloat d = (GLfloat) sqrt((double) (v[0] * v[0] +
188                                        v[1] * v[1] +
189                                        v[2] * v[2]));
190   if (d != 0)
191     {
192       v[0] /= d;
193       v[1] /= d;
194       v[2] /= d;
195         }
196   else
197     {
198       v[0] = v[1] = v[2] = 0;
199         }
200 }
201
202
203 /* Set up and enable texturing on our object */
204 static void
205 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
206 {
207   XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
208                                   MI_COLORMAP (mi), xpm_data);
209   clear_gl_error();
210   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
211                image->width, image->height, 0,
212                GL_RGBA, GL_UNSIGNED_BYTE, image->data);
213   check_gl_error("texture");
214
215   /* setup parameters for texturing */
216   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
217   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
218   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
219   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
220   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
221   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
222 }
223
224
225 static void
226 setup_file_texture (ModeInfo *mi, char *filename)
227 {
228   Display *dpy = mi->dpy;
229   Visual *visual = mi->xgwa.visual;
230
231   Colormap cmap = mi->xgwa.colormap;
232   XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
233
234   clear_gl_error();
235   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
236                image->width, image->height, 0,
237                GL_RGBA, GL_UNSIGNED_BYTE, image->data);
238   check_gl_error("texture");
239
240   /* setup parameters for texturing */
241   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
242   glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
243
244   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
245   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
246   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
247   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
248   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
249 }
250
251
252 static void
253 setup_texture(ModeInfo * mi)
254 {
255 /*  planetstruct *gp = &planets[MI_SCREEN(mi)];*/
256
257   glEnable(GL_TEXTURE_2D);
258
259   if (!which_image ||
260           !*which_image ||
261           !strcmp(which_image, "BUILTIN"))
262         setup_xpm_texture (mi, earth_xpm);
263   else
264         setup_file_texture (mi, which_image);
265
266   check_gl_error("texture initialization");
267 }
268
269
270 void
271 init_stars (ModeInfo *mi)
272 {
273   int i, j;
274   GLfloat max_size = 3;
275   GLfloat inc = 0.5;
276   int steps = max_size / inc;
277   int width  = MI_WIDTH(mi);
278   int height = MI_HEIGHT(mi);
279
280   planetstruct *gp = &planets[MI_SCREEN(mi)];
281   Bool wire = MI_IS_WIREFRAME(mi);
282   
283   if (!wire)
284     glEnable (GL_POINT_SMOOTH);
285
286   gp->starlist = glGenLists(1);
287   glNewList(gp->starlist, GL_COMPILE);
288
289   glScalef (1.0/width, 1.0/height, 1);
290
291   for (j = 1; j <= steps; j++)
292     {
293       glPointSize(inc * j);
294       glBegin(GL_POINTS);
295       for (i = 0 ; i < NUM_STARS / steps; i++)
296         {
297           glColor3f (0.6 + frand(0.3),
298                      0.6 + frand(0.3),
299                      0.6 + frand(0.3));
300           glVertex2f ((GLfloat) (random() % width),
301                       (GLfloat) (random() % height));
302         }
303       glEnd();
304     }
305   glEndList();
306
307   check_gl_error("stars initialization");
308 }
309
310
311 void
312 draw_stars (ModeInfo * mi)
313 {
314   int width  = MI_WIDTH(mi);
315   int height = MI_HEIGHT(mi);
316
317   planetstruct *gp = &planets[MI_SCREEN(mi)];
318   
319   /* Sadly, this causes a stall of the graphics pipeline (as would the
320      equivalent calls to glGet*.)  But there's no way around this, short
321      of having each caller set up the specific display matrix we need
322      here, which would kind of defeat the purpose of centralizing this
323      code in one file.
324    */
325   glPushAttrib(GL_TRANSFORM_BIT |  /* for matrix contents */
326                GL_ENABLE_BIT |     /* for various glDisable calls */
327                GL_CURRENT_BIT |    /* for glColor3f() */
328                GL_LIST_BIT);       /* for glListBase() */
329   {
330     check_gl_error ("glPushAttrib");
331
332     /* disable lighting and texturing when drawing stars!
333        (glPopAttrib() restores these.)
334      */
335     glDisable(GL_TEXTURE_2D);
336     glDisable(GL_LIGHTING);
337     glDisable(GL_DEPTH_TEST);
338
339     /* glPopAttrib() does not restore matrix changes, so we must
340        push/pop the matrix stacks to be non-intrusive there.
341      */
342     glMatrixMode(GL_PROJECTION);
343     glPushMatrix();
344     {
345       check_gl_error ("glPushMatrix");
346       glLoadIdentity();
347
348       /* Each matrix mode has its own stack, so we need to push/pop
349          them separately. */
350       glMatrixMode(GL_MODELVIEW);
351       glPushMatrix();
352       {
353         check_gl_error ("glPushMatrix");
354         glLoadIdentity();
355
356         gluOrtho2D (0, width, 0, height);
357         check_gl_error ("gluOrtho2D");
358
359         /* Draw the stars */
360         glScalef (width, height, 1);
361         glCallList(gp->starlist);
362         check_gl_error ("drawing stars");
363       }
364       glPopMatrix();
365     }
366     glMatrixMode(GL_PROJECTION);
367     glPopMatrix();
368
369   }
370   /* clean up after our state changes */
371   glPopAttrib();
372   check_gl_error ("glPopAttrib");
373 }
374
375
376
377 /* Set up lighting */
378 static void
379 init_sun (ModeInfo * mi)
380 {
381   planetstruct *gp = &planets[MI_SCREEN(mi)];
382
383   GLfloat lamb[4] = { 0.0, 0.0, 0.0, 1.0 };
384   GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
385   GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
386
387   GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
388   GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
389   GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
390   GLfloat shiny = .4;
391
392   gp->sunpos[0] =  1.00 - frand(2.00);
393   gp->sunpos[1] =  0.25 - frand(0.50);
394   gp->sunpos[2] = -1.00;
395   gp->sunpos[3] =  0.00;
396
397   glEnable(GL_LIGHTING);
398   glEnable(GL_LIGHT0);
399
400   glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
401   glLightfv (GL_LIGHT0, GL_AMBIENT,  lamb);
402   glLightfv (GL_LIGHT0, GL_DIFFUSE,  ldif);
403   glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
404
405   glMaterialfv (GL_FRONT, GL_AMBIENT,  mamb);
406   glMaterialfv (GL_FRONT, GL_DIFFUSE,  mdif);
407   glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
408   glMaterialf  (GL_FRONT, GL_SHININESS, shiny);
409
410
411   glEnable(GL_BLEND);
412   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
413   glShadeModel(GL_SMOOTH);
414
415   check_gl_error("lighting");
416 }
417
418
419 #define RANDSIGN() ((random() & 1) ? 1 : -1)
420
421 static void
422 pick_velocity (ModeInfo * mi)
423 {
424   planetstruct *gp = &planets[MI_SCREEN(mi)];
425
426   gp->box_width =  15.0;
427   gp->box_height = 15.0;
428   gp->box_depth =  5.0;
429
430   gp->tx = 0.0;
431   gp->ty = 0.0;
432   gp->tz = frand(360);
433
434   gp->dtx = (frand(0.4) + frand(0.3)) * RANDSIGN();
435   gp->dty = (frand(0.4) + frand(0.3)) * RANDSIGN();
436   gp->dtz = (frand(5.0) + frand(5.0));  /* the sun sets in the west */
437
438   gp->dx = (frand(0.2) + frand(0.2)) * RANDSIGN();
439   gp->dy = (frand(0.2) + frand(0.2)) * RANDSIGN();
440   gp->dz = (frand(0.2) + frand(0.2)) * RANDSIGN();
441 }
442
443
444 static void
445 rotate_and_move (ModeInfo * mi)
446 {
447   planetstruct *gp = &planets[MI_SCREEN(mi)];
448
449   if (do_roll)
450         {
451           gp->tx += gp->dtx;
452           while (gp->tx < 0)   gp->tx += 360;
453           while (gp->tx > 360) gp->tx -= 360;
454
455           gp->ty += gp->dty;
456           while (gp->ty < 0)   gp->ty += 360;
457           while (gp->ty > 360) gp->ty -= 360;
458         }
459
460   if (do_rotate)
461         {
462           gp->tz += gp->dtz;
463           while (gp->tz < 0)   gp->tz += 360;
464           while (gp->tz > 360) gp->tz -= 360;
465         }
466
467   if (do_wander)
468         {
469       static int frame = 0;
470 #     define SINOID(SCALE,SIZE) \
471         ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
472       gp->xpos = SINOID(0.031, gp->box_width);
473       gp->ypos = SINOID(0.023, gp->box_height);
474       gp->zpos = SINOID(0.017, gp->box_depth);
475       frame++;
476         }
477 }
478
479
480 void
481 reshape_planet (ModeInfo *mi, int width, int height)
482 {
483   GLfloat h = (GLfloat) height / (GLfloat) width;
484
485   glViewport(0, 0, (GLint) width, (GLint) height);
486   glMatrixMode(GL_PROJECTION);
487   glLoadIdentity();
488   glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
489   glMatrixMode(GL_MODELVIEW);
490   glLoadIdentity();
491   glTranslatef(0.0, 0.0, -DIST);
492
493   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
494 }
495
496
497 void
498 init_planet (ModeInfo * mi)
499 {
500   planetstruct *gp;
501   int screen = MI_SCREEN(mi);
502   Bool wire = MI_IS_WIREFRAME(mi);
503
504   if (planets == NULL) {
505         if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
506                                                                                   sizeof (planetstruct))) == NULL)
507           return;
508   }
509   gp = &planets[screen];
510
511   pick_velocity (mi);
512
513   if ((gp->glx_context = init_GL(mi)) != NULL) {
514         reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
515   }
516
517   {
518         char *f = get_string_resource("imageForeground", "Foreground");
519         char *b = get_string_resource("imageBackground", "Background");
520         char *s;
521         if (!f) f = strdup("white");
522         if (!b) b = strdup("black");
523         
524         for (s = f + strlen(f)-1; s > f; s--)
525           if (*s == ' ' || *s == '\t')
526                 *s = 0;
527         for (s = b + strlen(b)-1; s > b; s--)
528           if (*s == ' ' || *s == '\t')
529                 *s = 0;
530
531     if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
532       {
533                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
534                 exit(1);
535       }
536     if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
537       {
538                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
539                 exit(1);
540       }
541
542         free (f);
543         free (b);
544   }
545
546   if (wire)
547     {
548       do_texture = False;
549       do_light = False;
550       glEnable (GL_LINE_SMOOTH);
551     }
552
553   if (do_texture)
554     setup_texture (mi);
555
556   if (do_light)
557         init_sun (mi);
558
559   if (do_stars)
560     init_stars (mi);
561
562   glEnable(GL_DEPTH_TEST);
563   glEnable(GL_CULL_FACE);
564   glCullFace(GL_BACK); 
565
566   gp->platelist = glGenLists(1);
567   glNewList (gp->platelist, GL_COMPILE);
568   glColor3f (1,1,1);
569   glPushMatrix ();
570   glScalef (RADIUS, RADIUS, RADIUS);
571   unit_sphere (STACKS, SLICES, wire);
572   glPopMatrix ();
573   glEndList();
574 }
575
576 void
577 draw_planet (ModeInfo * mi)
578 {
579   planetstruct *gp = &planets[MI_SCREEN(mi)];
580   Display    *display = MI_DISPLAY(mi);
581   Window      window = MI_WINDOW(mi);
582
583   if (!gp->glx_context)
584         return;
585
586   glDrawBuffer(GL_BACK);
587   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
588
589   glXMakeCurrent (display, window, *(gp->glx_context));
590
591   if (do_stars)
592     draw_stars (mi);
593
594   glPushMatrix();
595   glRotatef (90,1,0,0);
596   glTranslatef (gp->xpos, gp->ypos, gp->zpos);
597
598   glRotatef (gp->tx, 1, 0, 0);
599   glRotatef (gp->ty, 0, 1, 0);
600   glRotatef (gp->tz, 0, 0, 1);
601
602   glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
603
604   glCallList (gp->platelist);
605   glPopMatrix();
606
607   if (mi->fps_p) do_fps (mi);
608   glFinish();
609   glXSwapBuffers(display, window);
610
611   rotate_and_move (mi);
612 }
613
614
615 void
616 release_planet (ModeInfo * mi)
617 {
618   if (planets != NULL) {
619         int screen;
620
621         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
622           planetstruct *gp = &planets[screen];
623
624           if (gp->glx_context) {
625                 /* Display lists MUST be freed while their glXContext is current. */
626                 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
627
628                 if (glIsList(gp->platelist))
629                   glDeleteLists(gp->platelist, 1);
630                 if (glIsList(gp->starlist))
631                   glDeleteLists(gp->starlist, 1);
632           }
633         }
634         (void) free((void *) planets);
635         planets = NULL;
636   }
637   FreeAllGL(mi);
638 }
639
640
641 #endif
642