http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.01.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                     "*bounce:           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_BOUNCE  "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_bounce;
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   {"-bounce",  ".glplanet.bounce",  XrmoptionNoArg, (caddr_t) "true" },
108   {"+bounce",  ".glplanet.bounce",  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_bounce,   "bounce",  "Bounce",  DEF_BOUNCE,  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.xbm"
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 } planetstruct;
177
178
179 static planetstruct *planets = NULL;
180
181
182 static inline void
183 normalize(GLfloat v[3])
184 {
185         GLfloat     d = (GLfloat) sqrt((double) (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));
186
187         if (d != 0) {
188                 v[0] /= d;
189                 v[1] /= d;
190                 v[2] /= d;
191         } else {
192                 v[0] = v[1] = v[2] = 0;
193         }
194 }
195
196
197 /* Set up and enable texturing on our object */
198 static void
199 setup_xbm_texture (char *bits, int width, int height,
200                                    XColor *fgc, XColor *bgc)
201 {
202   unsigned int fg = (((fgc->red  >> 8) << 16) |
203                                          ((fgc->green >> 8) << 8) |
204                                          ((fgc->blue >> 8)));
205   unsigned int bg = (((bgc->red  >> 8) << 16) |
206                                          ((bgc->green >> 8) << 8) |
207                                          ((bgc->blue >> 8)));
208
209   unsigned char *data = (unsigned char *)
210         malloc ((width * height * 24) / 8);
211   unsigned char *out = data;
212   int x, y;
213
214   for (y = 0; y < height; y++)
215         for (x = 0; x < width; x++)
216           {
217                 unsigned char byte = bits [(y * (width / 8) + (x / 8))];
218                 unsigned char bit = (byte & (1 << (x % 8))) >> (x % 8);
219                 unsigned int word = (bit ? bg : fg);
220                 *out++ = (word & 0xFF0000) >> 16;
221                 *out++ = (word & 0x00FF00) >> 8;
222                 *out++ = (word & 0x0000FF);
223           }
224
225   clear_gl_error();
226   glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0,
227                            GL_RGB, GL_UNSIGNED_BYTE, data);
228   check_gl_error("texture");
229
230   /* setup parameters for texturing */
231   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
232   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
233   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
234   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
235   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
236   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
237 }
238
239
240 static void
241 setup_file_texture (ModeInfo *mi, char *filename)
242 {
243   Display *dpy = mi->dpy;
244   Visual *visual = mi->xgwa.visual;
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, GL_UNSIGNED_BYTE, image->data);
253   check_gl_error("texture");
254
255   /* setup parameters for texturing */
256   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
257   glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
258
259   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
260   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
261   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
262   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
263   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
264 }
265
266
267 static void
268 setup_texture(ModeInfo * mi)
269 {
270   planetstruct *gp = &planets[MI_SCREEN(mi)];
271   if (!which_image ||
272           !*which_image ||
273           !strcmp(which_image, "BUILTIN"))
274         setup_xbm_texture (earth_bits, earth_width, earth_height,
275                                            &gp->fg, &gp->bg);
276   else
277         setup_file_texture (mi, which_image);
278 }
279
280
281 /* Set up and enable lighting */
282 static void
283 setup_light(void)
284 {
285   /* set a number of parameters which make the scene look much nicer */
286   glEnable(GL_BLEND);
287   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
288   glShadeModel(GL_SMOOTH);
289 }
290
291
292 /* Set up and enable face culling so we don't see the inside of the sphere */
293 static void
294 setup_face(void)
295 {
296   glEnable(GL_CULL_FACE);
297   glCullFace(GL_BACK); 
298 }
299
300
301 #if 0
302 /* Function for determining points on the surface of the sphere */
303 static void inline ParametricSphere(float theta, float rho, GLfloat *vector)
304 {
305   vector[0] = -sin(theta) * sin(rho);
306   vector[1] = cos(theta) * sin(rho);
307   vector[2] = cos(rho);
308
309 #if DO_HELIX
310   vector[0] = -(1- cos(theta)) * cos(rho); 
311   vector[1] = -(1- cos(theta)) * sin(rho); 
312   vector[2] = -(sin(theta) + rho); 
313 #endif /* DO_HELIX */
314
315         return;
316 }
317 #endif
318
319
320 /* lame way to generate some random stars */
321 void generate_stars(int width, int height)
322 {
323   int i, j;
324   int max_size = 3;
325   GLfloat inc = 0.5;
326   int steps = max_size / inc;
327
328   planetstruct *gp = &planets[MI_SCREEN(mi)];
329   
330   gp->starlist = glGenLists(1);
331   glNewList(gp->starlist, GL_COMPILE);
332
333   /* this hackery makes the viewport map one-to-one with Vertex arguments */
334   glMatrixMode(GL_PROJECTION);
335   glPushMatrix();
336   glMatrixMode(GL_PROJECTION);
337   glLoadIdentity();
338   gluOrtho2D(0, width, 0, height);
339   glMatrixMode(GL_MODELVIEW);
340   glLoadIdentity();
341
342   /* disable depth testing for the stars, so they don't obscure the planet */
343   glDisable(GL_DEPTH_TEST);
344   glEnable(GL_BLEND);
345   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
346   
347   glColor3f(1,1,1);
348
349   glEnable(GL_POINT_SMOOTH);
350
351   for (j = 1; j <= steps; j++)
352     {
353       glPointSize(inc * j);
354       glBegin(GL_POINTS);
355       for(i = 0 ; i < NUM_STARS / steps; i++)
356         {
357           glColor3f (0.6 + frand(0.3),
358                      0.6 + frand(0.3),
359                      0.6 + frand(0.3));
360           glVertex2f ((GLfloat) (random() % width),
361                       (GLfloat) (random() % height));
362         }
363       glEnd();
364     }
365
366   /* return to original PROJECT and MODELVIEW */
367   glMatrixMode(GL_PROJECTION);
368   glPopMatrix();
369   glMatrixMode(GL_MODELVIEW);
370
371
372   glEndList();
373
374 }
375
376 /* Set up lighting */
377 static void
378 init_sun (ModeInfo * mi)
379 {
380   GLfloat light[4];
381   light[0] = frand(2.0) - 1.0;
382   light[1] = frand(2.0) - 1.0;
383   light[2] = 1.0;
384   light[3] = 0;
385
386   glLightfv(GL_LIGHT0, GL_POSITION, light);
387 }
388
389
390 /* Initialization function for screen saver */
391 static void
392 pinit(ModeInfo * mi)
393 {
394   Bool wire = MI_IS_WIREFRAME(mi);
395   planetstruct *gp = &planets[MI_SCREEN(mi)];
396
397   if (wire) {
398         glEnable(GL_LINE_SMOOTH);
399         do_texture = False;
400   }
401
402   /* turn on various options we like */
403   if (do_texture)
404         setup_texture(mi);
405   if (do_light)
406         setup_light();
407
408   setup_face();
409
410   if (do_stars) {
411         glEnable(GL_POINT_SMOOTH);
412         generate_stars(MI_WIDTH(mi), MI_HEIGHT(mi));
413   }
414
415   gp->platelist=glGenLists(1);
416   glNewList(gp->platelist, GL_COMPILE);
417   glPushMatrix ();
418   glScalef (RADIUS, RADIUS, RADIUS);
419   unit_sphere (STACKS, SLICES, wire);
420   glPopMatrix ();
421   glEndList();
422
423   init_sun (mi);
424 }
425
426 static void
427 draw_sphere(ModeInfo * mi)
428 {
429   planetstruct *gp = &planets[MI_SCREEN(mi)];
430
431   glEnable(GL_DEPTH_TEST);
432
433   /* turn on the various attributes for making the sphere look nice */
434   if (do_texture)
435         glEnable(GL_TEXTURE_2D);
436
437   if (do_light)
438         {
439           glEnable(GL_LIGHTING);
440           glEnable(GL_LIGHT0);
441           glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
442           glEnable(GL_COLOR_MATERIAL);
443         }
444
445   glCallList(gp->platelist);
446
447 }
448
449
450 #define RANDSIGN() ((random() & 1) ? 1 : -1)
451
452 static void
453 pick_velocity (ModeInfo * mi)
454 {
455   planetstruct *gp = &planets[MI_SCREEN(mi)];
456
457   gp->box_width =  15.0;
458   gp->box_height = 15.0;
459   gp->box_depth =  5.0;
460
461   gp->tx = 0.0;
462   gp->ty = 0.0;
463   gp->tz = frand(360);
464
465   gp->dtx = (frand(0.4) + frand(0.3)) * RANDSIGN();
466   gp->dty = (frand(0.4) + frand(0.3)) * RANDSIGN();
467   gp->dtz = (frand(5.0) + frand(5.0));  /* the sun sets in the west */
468
469   gp->dx = (frand(0.2) + frand(0.2)) * RANDSIGN();
470   gp->dy = (frand(0.2) + frand(0.2)) * RANDSIGN();
471   gp->dz = (frand(0.2) + frand(0.2)) * RANDSIGN();
472 }
473
474
475 static void
476 rotate_and_move (ModeInfo * mi)
477 {
478   planetstruct *gp = &planets[MI_SCREEN(mi)];
479
480   if (do_roll)
481         {
482           gp->tx += gp->dtx;
483           while (gp->tx < 0)   gp->tx += 360;
484           while (gp->tx > 360) gp->tx -= 360;
485
486           gp->ty += gp->dty;
487           while (gp->ty < 0)   gp->ty += 360;
488           while (gp->ty > 360) gp->ty -= 360;
489         }
490
491   if (do_rotate)
492         {
493           gp->tz += gp->dtz;
494           while (gp->tz < 0)   gp->tz += 360;
495           while (gp->tz > 360) gp->tz -= 360;
496         }
497
498   if (do_bounce)
499         {
500       static int frame = 0;
501 #     define SINOID(SCALE,SIZE) \
502         ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
503       gp->xpos = SINOID(0.031, gp->box_width);
504       gp->ypos = SINOID(0.023, gp->box_height);
505       gp->zpos = SINOID(0.017, gp->box_depth);
506       frame++;
507         }
508 }
509
510
511 /* Standard reshape function */
512 void
513 reshape_planet(ModeInfo *mi, int width, int height)
514 {
515   GLfloat h = (GLfloat) height / (GLfloat) width;
516
517   glViewport(0, 0, (GLint) width, (GLint) height);
518   glMatrixMode(GL_PROJECTION);
519   glLoadIdentity();
520   glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
521   glMatrixMode(GL_MODELVIEW);
522   glLoadIdentity();
523   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
524   glTranslatef(0.0, 0.0, -DIST);
525 }
526
527
528 void
529 init_planet(ModeInfo * mi)
530 {
531   int         screen = MI_SCREEN(mi);
532
533   planetstruct *gp;
534
535   if (planets == NULL) {
536         if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
537                                                                                   sizeof (planetstruct))) == NULL)
538           return;
539   }
540   gp = &planets[screen];
541
542   pick_velocity (mi);
543
544   {
545         char *f = get_string_resource("imageForeground", "Foreground");
546         char *b = get_string_resource("imageBackground", "Background");
547         char *s;
548         if (!f) f = strdup("white");
549         if (!b) b = strdup("black");
550         
551         for (s = f + strlen(f)-1; s > f; s--)
552           if (*s == ' ' || *s == '\t')
553                 *s = 0;
554         for (s = b + strlen(b)-1; s > b; s--)
555           if (*s == ' ' || *s == '\t')
556                 *s = 0;
557
558     if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
559       {
560                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
561                 exit(1);
562       }
563     if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
564       {
565                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
566                 exit(1);
567       }
568
569         free (f);
570         free (b);
571   }
572
573
574   gp->window = MI_WINDOW(mi);
575   if ((gp->glx_context = init_GL(mi)) != NULL) {
576         reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
577         pinit(mi);
578   } else {
579         MI_CLEARWINDOW(mi);
580   }
581 }
582
583 void
584 draw_planet(ModeInfo * mi)
585 {
586   planetstruct *gp = &planets[MI_SCREEN(mi)];
587   Display    *display = MI_DISPLAY(mi);
588   Window      window = MI_WINDOW(mi);
589
590   if (!gp->glx_context)
591         return;
592
593   glDrawBuffer(GL_BACK);
594   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
595
596   glXMakeCurrent(display, window, *(gp->glx_context));
597
598
599   if (do_stars) {
600         /* protect our modelview matrix and attributes */
601         glPushMatrix();
602     glCallList(gp->starlist);
603         glPopMatrix();
604   }
605
606   /* protect our modelview matrix and attributes */
607   glPushMatrix();
608   glPushAttrib(GL_ALL_ATTRIB_BITS);
609   {
610         /* this pair of rotations seem to be necessary to orient the earth correctly */
611         glRotatef(90,0,0,1);
612         glRotatef(90,0,1,0);
613
614         glTranslatef(gp->xpos, gp->ypos, gp->zpos);
615         glRotatef(gp->tx, 1, 0, 0);
616         glRotatef(gp->ty, 0, 1, 0);
617         glRotatef(gp->tz, 0, 0, 1);
618         /* draw the sphere */
619         draw_sphere(mi);
620   }
621   glPopMatrix();
622   glPopAttrib();
623
624
625
626   if (mi->fps_p) do_fps (mi);
627   glFinish();
628   glXSwapBuffers(display, window);
629
630   rotate_and_move (mi);
631 }
632
633 void
634 release_planet(ModeInfo * mi)
635 {
636   if (planets != NULL) {
637         int         screen;
638
639         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
640           planetstruct *gp = &planets[screen];
641
642           if (gp->glx_context) {
643                 /* Display lists MUST be freed while their glXContext is current. */
644                 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
645
646                 if (glIsList(gp->platelist))
647                   glDeleteLists(gp->platelist, 1);
648                 if (glIsList(gp->starlist))
649                   glDeleteLists(gp->starlist, 1);
650           }
651         }
652         (void) free((void *) planets);
653         planets = NULL;
654   }
655   FreeAllGL(mi);
656 }
657
658
659 #endif
660