From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / dymaxionmap.c
1 /* dymaxionmap --- Buckminster Fuller's unwrapped icosahedral globe.
2  * Copyright (c) 2016 Jamie Zawinski.
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
17 #define LABEL_FONT "-*-helvetica-bold-r-normal-*-*-240-*-*-*-*-*-*"
18
19 #ifdef STANDALONE
20 #define DEFAULTS    "*delay:            20000   \n" \
21                     "*showFPS:          False   \n" \
22                     "*wireframe:        False   \n" \
23                     "*labelFont:  " LABEL_FONT "\n"
24 # define refresh_planet 0
25 # include "xlockmore.h"             /* from the xscreensaver distribution */
26 #else  /* !STANDALONE */
27 # include "xlock.h"                 /* from the xlockmore distribution */
28 #endif /* !STANDALONE */
29
30 #ifdef USE_GL /* whole file */
31
32 #include "sphere.h"
33 #include "normals.h"
34 #include "texfont.h"
35
36 #ifdef HAVE_XMU
37 # ifndef VMS
38 #  include <X11/Xmu/Drawing.h>
39 #else  /* VMS */
40 #  include <Xmu/Drawing.h>
41 # endif /* VMS */
42 #endif
43
44 #define DEF_ROTATE  "True"
45 #define DEF_ROLL    "True"
46 #define DEF_WANDER  "True"
47 #define DEF_TEXTURE "True"
48 #define DEF_STARS   "True"
49 #define DEF_SPEED   "1.0"
50 #define DEF_IMAGE   "BUILTIN"
51
52 #undef countof
53 #define countof(x) (sizeof((x))/sizeof((*x)))
54
55 #undef BELLRAND
56 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
57
58 static int do_roll;
59 static int do_wander;
60 static int do_texture;
61 static int do_stars;
62 static GLfloat speed;
63 static char *which_image;
64
65 static XrmOptionDescRec opts[] = {
66   {"-speed",   ".dymaxionmap.speed",   XrmoptionSepArg, 0 },
67   {"-roll",    ".dymaxionmap.roll",    XrmoptionNoArg, "true" },
68   {"+roll",    ".dymaxionmap.roll",    XrmoptionNoArg, "false" },
69   {"-wander",  ".dymaxionmap.wander",  XrmoptionNoArg, "true" },
70   {"+wander",  ".dymaxionmap.wander",  XrmoptionNoArg, "false" },
71   {"-texture", ".dymaxionmap.texture", XrmoptionNoArg, "true" },
72   {"+texture", ".dymaxionmap.texture", XrmoptionNoArg, "false" },
73   {"-stars",   ".dymaxionmap.stars",   XrmoptionNoArg, "true" },
74   {"+stars",   ".dymaxionmap.stars",   XrmoptionNoArg, "false" },
75   {"-image",   ".dymaxionmap.image",  XrmoptionSepArg, 0 },
76 };
77
78 static argtype vars[] = {
79   {&speed,       "speed",   "Speed",   DEF_SPEED,   t_Float},
80   {&do_roll,     "roll",    "Roll",    DEF_ROLL,    t_Bool},
81   {&do_wander,   "wander",  "Wander",  DEF_WANDER,  t_Bool},
82   {&do_texture,  "texture", "Texture", DEF_TEXTURE, t_Bool},
83   {&do_stars,    "stars",   "Stars",   DEF_STARS,   t_Bool},
84   {&which_image, "image",   "Image",   DEF_IMAGE,   t_String},
85 };
86
87 ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
88
89 #ifdef USE_MODULES
90 ModStruct   planet_description =
91 {"planet", "init_planet", "draw_planet", "release_planet",
92  "draw_planet", "init_planet", NULL, &planet_opts,
93  1000, 1, 2, 1, 4, 1.0, "",
94  "Buckminster Fuller's unwrapped icosahedral globe", 0, NULL};
95 #endif
96
97 # ifdef __GNUC__
98   __extension__  /* don't warn about "string length is greater than the length
99                     ISO C89 compilers are required to support" when including
100                     the following XPM file... */
101 # endif
102 #include "../images/dymaxionmap.xpm"
103 #include "../images/ground.xpm"
104
105 #include "xpm-ximage.h"
106 #include "rotator.h"
107 #include "gltrackball.h"
108
109
110 typedef struct {
111   GLXContext *glx_context;
112   GLuint starlist;
113   int starcount;
114   rotator *rot;
115   trackball_state *trackball;
116   Bool button_down_p;
117   enum { STARTUP, FLAT, FOLD, 
118          ICO, STEL_IN, AXIS, SPIN, STEL, STEL_OUT,
119          ICO2, UNFOLD } state;
120   GLfloat ratio;
121   GLuint tex1, tex2;
122   texture_font_data *font_data;
123 } planetstruct;
124
125
126 static planetstruct *planets = NULL;
127
128
129 /* Set up and enable texturing on our object */
130 static void
131 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
132 {
133   XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
134                                   MI_COLORMAP (mi), xpm_data);
135   char buf[1024];
136   clear_gl_error();
137   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
138                image->width, image->height, 0,
139                GL_RGBA,
140                /* GL_UNSIGNED_BYTE, */
141                GL_UNSIGNED_INT_8_8_8_8_REV,
142                image->data);
143   sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
144   check_gl_error(buf);
145
146   /* setup parameters for texturing */
147   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
148   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
149   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
150   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
151   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
152   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
153 }
154
155
156 static void
157 setup_file_texture (ModeInfo *mi, char *filename)
158 {
159   Display *dpy = mi->dpy;
160   Visual *visual = mi->xgwa.visual;
161   char buf[1024];
162
163   Colormap cmap = mi->xgwa.colormap;
164   XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
165   clear_gl_error();
166   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
167                image->width, image->height, 0,
168                GL_RGBA,
169                /* GL_UNSIGNED_BYTE, */
170                GL_UNSIGNED_INT_8_8_8_8_REV,
171                image->data);
172   sprintf (buf, "texture: %.100s (%dx%d)",
173            filename, image->width, image->height);
174   check_gl_error(buf);
175
176   /* setup parameters for texturing */
177   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
178   glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
179
180   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
181   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
182   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
183   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
184   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
185 }
186
187
188 static void
189 setup_texture(ModeInfo * mi)
190 {
191   planetstruct *gp = &planets[MI_SCREEN(mi)];
192
193   if (!which_image ||
194       !*which_image ||
195       !strcmp(which_image, "BUILTIN"))
196     {
197       glGenTextures (1, &gp->tex1);
198       glBindTexture (GL_TEXTURE_2D, gp->tex1);
199       setup_xpm_texture (mi, dymaxionmap_xpm);
200     }
201   else
202     {
203       glGenTextures (1, &gp->tex1);
204       glBindTexture (GL_TEXTURE_2D, gp->tex1);
205       setup_file_texture (mi, which_image);
206     }
207
208   glGenTextures (1, &gp->tex2);
209   glBindTexture (GL_TEXTURE_2D, gp->tex2);
210   setup_xpm_texture (mi, ground);
211
212   check_gl_error("texture initialization");
213
214   /* Need to flip the texture top for bottom for some reason. */
215   glMatrixMode (GL_TEXTURE);
216   glScalef (1, -1, 1);
217   glMatrixMode (GL_MODELVIEW);
218 }
219
220
221 static void
222 init_stars (ModeInfo *mi)
223 {
224   planetstruct *gp = &planets[MI_SCREEN(mi)];
225   int i, j;
226   int width  = MI_WIDTH(mi);
227   int height = MI_HEIGHT(mi);
228   int size = (width > height ? width : height);
229   int nstars = size * size / 80;
230   int max_size = 3;
231   GLfloat inc = 0.5;
232   int steps = max_size / inc;
233
234   gp->starlist = glGenLists(1);
235   glNewList(gp->starlist, GL_COMPILE);
236   for (j = 1; j <= steps; j++)
237     {
238       glPointSize(inc * j);
239       glBegin (GL_POINTS);
240       for (i = 0; i < nstars / steps; i++)
241         {
242           GLfloat d = 0.1;
243           GLfloat r = 0.15 + frand(0.3);
244           GLfloat g = r + frand(d) - d;
245           GLfloat b = r + frand(d) - d;
246
247           GLfloat x = frand(1)-0.5;
248           GLfloat y = frand(1)-0.5;
249           GLfloat z = ((random() & 1)
250                        ? frand(1)-0.5
251                        : (BELLRAND(1)-0.5)/12);   /* milky way */
252           d = sqrt (x*x + y*y + z*z);
253           x /= d;
254           y /= d;
255           z /= d;
256           glColor3f (r, g, b);
257           glVertex3f (x, y, z);
258           gp->starcount++;
259         }
260       glEnd ();
261     }
262   glEndList ();
263
264   check_gl_error("stars initialization");
265 }
266
267
268 ENTRYPOINT void
269 reshape_planet (ModeInfo *mi, int width, int height)
270 {
271   GLfloat h = (GLfloat) height / (GLfloat) width;
272
273   glViewport(0, 0, (GLint) width, (GLint) height);
274   glMatrixMode(GL_PROJECTION);
275   glLoadIdentity();
276   glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
277   glMatrixMode(GL_MODELVIEW);
278   glLoadIdentity();
279   glTranslatef(0.0, 0.0, -40);
280
281 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
282   {
283     int o = (int) current_device_rotation();
284     if (o != 0 && o != 180 && o != -180)
285       glScalef (h, h, h);
286   }
287 # endif
288
289   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
290 }
291
292
293 static void
294 do_normal2 (Bool frontp, XYZ a, XYZ b, XYZ c)
295 {
296   if (frontp)
297     do_normal (a.x, a.y, a.z,
298                b.x, b.y, b.z,
299                c.x, c.y, c.z);
300   else
301     do_normal (b.x, b.y, b.z,
302                a.x, a.y, a.z,
303                c.x, c.y, c.z);
304 }
305
306
307 static void
308 triangle0 (ModeInfo *mi, Bool frontp, GLfloat stel_ratio, int bitmask)
309 {
310   /* Render a triangle as six sub-triangles:
311
312                 A
313                / \
314               / | \
315              /  |  \
316             / 0 | 1 \
317         E  /_   |   _\  F
318           /  \_ | _/  \
319          / 5   \D/   2 \
320         /    /  |  \    \
321        /   / 4  | 3  \   \
322       /  /      |       \ \
323    B ----------------------- C
324                 G
325    */
326
327   Bool wire = MI_IS_WIREFRAME(mi);
328   GLfloat h = sqrt(3) / 2;
329   GLfloat h2 = sqrt(h*h - (h/2)*(h/2)) - 0.5;
330   XYZ  A,  B,  C,  D,  E,  F,  G;
331   XYZ tA, tB, tC, tD, tE, tF, tG;
332   XYZ  a,  b,  c;
333   XYZ ta, tb, tc;
334   A.x =  0;   A.y = h;   A.z = 0;
335   B.x = -0.5, B.y = 0;   B.z = 0;
336   C.x =  0.5, C.y = 0;   C.z = 0;
337   D.x =  0;   D.y = h/3; D.z = 0;
338   E.x = -h2;  E.y = h/2; E.z = 0;
339   F.x =  h2;  F.y = h/2; F.z = 0;
340   G.x =  0;   G.y = 0;   G.z = 0;
341
342   /* When tweaking object XY to stellate, don't change texture coordinates. */
343   tA = A; tB = B; tC = C; tD = D; tE = E; tF = F; tG = G;
344
345   /* Eyeballed this to find the depth of stellation that seems to most
346      approximate a sphere.
347    */
348   D.z = 0.193 * stel_ratio;
349
350   /* We want to raise E, F and G as well but we can't just shift Z:
351      we need to keep them on the same vector from the center of the sphere,
352      which means also changing F and G's X and Y.
353   */
354   E.z = F.z = G.z = 0.132 * stel_ratio;
355   {
356     double magic_x = 0.044;
357     double magic_y = 0.028;
358     /* G.x stays 0 */
359     G.y -= sqrt (magic_x*magic_x + magic_y*magic_y) * stel_ratio;
360     E.x -= magic_x * stel_ratio;
361     E.y += magic_y * stel_ratio;
362     F.x += magic_x * stel_ratio;
363     F.y += magic_y * stel_ratio;
364   }
365
366
367   if (bitmask & 1<<0)
368     {
369       glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
370       a  =  E;  b =  D;  c =  A;
371       ta = tE; tb = tD; tc = tA;
372       do_normal2 (frontp, a, b, c);
373       glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
374       glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
375       glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
376       glEnd();
377       mi->polygon_count++;
378     }
379   if (bitmask & 1<<1)
380     {
381       glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
382       a  =  D;  b =  F;  c =  A;
383       ta = tD; tb = tF; tc = tA;
384       do_normal2 (frontp, a, b, c);
385       glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
386       glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
387       glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
388       glEnd();
389       mi->polygon_count++;
390     }
391   if (bitmask & 1<<2)
392     {
393       glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
394       a  =  D;  b =  C;  c =  F;
395       ta = tD; tb = tC; tc = tF;
396       do_normal2 (frontp, a, b, c);
397       glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
398       glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
399       glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
400       glEnd();
401       mi->polygon_count++;
402     }
403   if (bitmask & 1<<3)
404     {
405       glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
406       a  =  G;  b =  C;  c =  D;
407       ta = tG; tb = tC; tc = tD;
408       do_normal2 (frontp, a, b, c);
409       glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
410       glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
411       glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
412       glEnd();
413       mi->polygon_count++;
414     }
415   if (bitmask & 1<<4)
416     {
417       glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
418       a  =  B;  b =  G;  c =  D;
419       ta = tB; tb = tG; tc = tD;
420       do_normal2 (frontp, a, b, c);
421       glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
422       glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
423       glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
424       glEnd();
425       mi->polygon_count++;
426     }
427   if (bitmask & 1<<5)
428     {
429       glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
430       a  =  B;  b =  D;  c =  E;
431       ta = tB; tb = tD; tc = tE;
432       do_normal2 (frontp, a, b, c);
433       glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
434       glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
435       glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
436       glEnd();
437       mi->polygon_count++;
438     }
439   if (bitmask & 1<<6)
440     {
441       glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
442       a  =  E;  b =  D;  c =  A;
443       ta = tE; tb = tD; tc = tA;
444       do_normal2 (frontp, a, b, c);
445       glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
446       glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
447       glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
448       glEnd();
449       mi->polygon_count++;
450     }
451 }
452
453
454 /* The segments, numbered arbitrarily from the top left:
455              ________         _      ________
456              \      /\      /\ \    |\      /
457               \ 0  /  \    /  \3>   | \ 5  /
458                \  / 1  \  / 2  \| ..|4 \  /-6-..
459      ___________\/______\/______\/______\/______\
460     |   /\      /\      /\      /\      /\   
461     |7 /  \ 9  /  \ 11 /  \ 13 /  \ 15 /  \  
462     | / 8  \  / 10 \  / 12 \  / 14 \  / 16 \ 
463     |/______\/______\/______\/______\/______\
464      \      /\      /       /\      /\
465       \ 17 /  \ 18 /       /  \ 20 /  \
466        \  /    \  /       / 19 \  / 21 \
467         \/      \/       /______\/______\
468
469    Each triangle can be connected to at most two other triangles.
470    We start from the middle, #12, and work our way to the edges.
471    Its centroid is 0,0.
472  */
473 static void
474 triangle (ModeInfo *mi, int which, Bool frontp, 
475           GLfloat fold_ratio, GLfloat stel_ratio)
476 {
477   planetstruct *gp = &planets[MI_SCREEN(mi)];
478   const GLfloat fg[3] = { 1, 1, 1 };
479   const GLfloat bg[3] = { 0.3, 0.3, 0.3 };
480   int a = -1, b = -1;
481   GLfloat max = acos (sqrt(5)/3);
482   GLfloat rot = -max * fold_ratio / (M_PI/180);
483   Bool wire = MI_IS_WIREFRAME(mi);
484
485   if (wire)
486     glColor3fv (fg);
487
488   switch (which) {
489   case 3:                               /* One third of the face. */
490     triangle0 (mi, frontp, stel_ratio, 1<<3 | 1<<4);
491     break;
492   case 4:                               /* Two thirds of the face: convex. */
493     triangle0 (mi, frontp, stel_ratio, 1<<1 | 1<<2 | 1<<3 | 1<<4);
494     break;
495   case 6:                               /* One half of the face. */
496     triangle0 (mi, frontp, stel_ratio, 1<<1 | 1<<2 | 1<<3);
497     break;
498   case 7:                               /* One half of the face. */
499     triangle0 (mi, frontp, stel_ratio, 1<<2 | 1<<3 | 1<<4);
500     break;
501   default:                              /* Full face. */
502     triangle0 (mi, frontp, stel_ratio, 0x3F);
503     break;
504   }
505
506   if (wire)
507     {
508       char tag[20];
509       glColor3fv (bg);
510       sprintf (tag, "%d", which);
511       glPushMatrix();
512       glTranslatef (-0.1, 0.2, 0);
513       glScalef (0.005, 0.005, 0.005);
514       print_texture_string (gp->font_data, tag);
515       glPopMatrix();
516       mi->polygon_count++;
517     }
518
519
520   /* The connection hierarchy of the faces starting at the middle, #12. */
521   switch (which) {
522   case  0: break;
523   case  1: a =  0; b = -1; break;
524   case  2: a = -1; b =  3; break;
525   case  3: break;
526   case  4: a = -1; b =  5; break;
527   case  5: a = -1; b =  6; break;
528   case  7: break;
529   case  6: break;
530   case  8: a = 17; b =  7; break;
531   case  9: a =  8; b = -1; break;
532   case 10: a = 18; b =  9; break;
533   case 11: a = 10; b =  1; break;
534   case 12: a = 11; b = 13; break;
535   case 13: a =  2; b = 14; break;
536   case 14: a = 15; b = 20; break;
537   case 15: a =  4; b = 16; break;
538   case 16: break;
539   case 17: break;
540   case 18: break;
541   case 19: break;
542   case 20: a = 21; b = 19; break;
543   case 21: break;
544   default: abort(); break;
545   }
546
547   if (a != -1)
548     {
549       glPushMatrix();
550       glTranslatef (-0.5, 0, 0);        /* Move model matrix to upper left */
551       glRotatef (60, 0, 0, 1);
552       glTranslatef ( 0.5, 0, 0);
553
554       glMatrixMode(GL_TEXTURE);
555       /* glPushMatrix(); */
556       glTranslatef (-0.5, 0, 0);        /* Move texture matrix the same way */
557       glRotatef (60, 0, 0, 1);
558       glTranslatef ( 0.5, 0, 0);
559
560       glMatrixMode(GL_MODELVIEW);
561
562       glRotatef (rot, 1, 0, 0);
563       triangle (mi, a, frontp, fold_ratio, stel_ratio);
564
565       /* This should just be a PopMatrix on the TEXTURE stack, but
566          fucking iOS has GL_MAX_TEXTURE_STACK_DEPTH == 4!  WTF!
567          So we have to undo our rotations and translations manually.
568        */
569       glMatrixMode(GL_TEXTURE);
570       /* glPopMatrix(); */
571       glTranslatef (-0.5, 0, 0);
572       glRotatef (-60, 0, 0, 1);
573       glTranslatef (0.5, 0, 0);
574
575       glMatrixMode(GL_MODELVIEW);
576       glPopMatrix();
577     }
578
579   if (b != -1)
580     {
581       glPushMatrix();
582       glTranslatef (0.5, 0, 0);         /* Move model matrix to upper right */
583       glRotatef (-60, 0, 0, 1);
584       glTranslatef (-0.5, 0, 0);
585
586       glMatrixMode(GL_TEXTURE);
587       /* glPushMatrix(); */
588       glTranslatef (0.5, 0, 0);         /* Move texture matrix the same way */
589       glRotatef (-60, 0, 0, 1);
590       glTranslatef (-0.5, 0, 0);
591
592       glMatrixMode(GL_MODELVIEW);
593
594       glRotatef (rot, 1, 0, 0);
595       triangle (mi, b, frontp, fold_ratio, stel_ratio);
596
597       /* See above. Grr. */
598       glMatrixMode(GL_TEXTURE);
599       /* glPopMatrix(); */
600       glTranslatef (0.5, 0, 0);
601       glRotatef (60, 0, 0, 1);
602       glTranslatef (-0.5, 0, 0);
603
604       glMatrixMode(GL_MODELVIEW);
605       glPopMatrix();
606     }
607 }
608
609
610 static void
611 draw_triangles (ModeInfo *mi, GLfloat fold_ratio, GLfloat stel_ratio)
612 {
613   planetstruct *gp = &planets[MI_SCREEN(mi)];
614   Bool wire = MI_IS_WIREFRAME(mi);
615   GLfloat h = sqrt(3) / 2;
616   GLfloat c = h / 3;
617
618   glTranslatef (0, -h/3, 0);  /* Center on face 12 */
619
620   /* When closed, center on midpoint of icosahedron. Eyeballed this. */
621   glTranslatef (0, 0, fold_ratio * 0.754);
622
623   glFrontFace (GL_CCW);
624
625   /* Adjust the texture matrix so that it has the same coordinate space
626      as the model. */
627
628   glMatrixMode(GL_TEXTURE);
629   glPushMatrix();
630   {
631     GLfloat texw = 5.5;
632     GLfloat texh = 3 * h;
633     GLfloat midx = 2.5;
634     GLfloat midy = 3 * c;
635     glScalef (1/texw, -1/texh, 1);
636     glTranslatef (midx, midy, 0);
637   }
638   glMatrixMode(GL_MODELVIEW);
639
640
641
642   /* Front faces */
643
644   if (wire)
645     glDisable (GL_TEXTURE_2D);
646   else if (do_texture)
647     {
648       glEnable (GL_TEXTURE_2D);
649       glBindTexture (GL_TEXTURE_2D, gp->tex1);
650     }
651   else
652     glDisable (GL_TEXTURE_2D);
653
654   triangle (mi, 12, True, fold_ratio, stel_ratio);
655
656   /* Back faces */
657
658   if (wire)
659     glDisable (GL_TEXTURE_2D);
660   else if (do_texture)
661     {
662       glEnable (GL_TEXTURE_2D);
663       glBindTexture (GL_TEXTURE_2D, gp->tex2);
664     }
665   else
666     glDisable (GL_TEXTURE_2D);
667
668   glFrontFace (GL_CW);
669
670   triangle (mi, 12, False, fold_ratio, 0);
671
672   glMatrixMode(GL_TEXTURE);
673   glPopMatrix();
674   glMatrixMode(GL_MODELVIEW);
675 }
676
677
678 static void
679 align_axis (ModeInfo *mi, int undo)
680 {
681   /* Rotate so that an axis is lined up with the north and south poles
682      on the map, which are not in the center of their faces, or any
683      other easily computable spot. */
684
685   GLfloat r1 = 20.5;
686   GLfloat r2 = 28.5;
687
688   if (undo)
689     {
690       glRotatef (-r2, 0, 1, 0);
691       glRotatef ( r2, 1, 0, 0);
692       glRotatef (-r1, 1, 0, 0);
693     }
694   else
695     {
696       glRotatef (r1, 1, 0, 0);
697       glRotatef (-r2, 1, 0, 0);
698       glRotatef ( r2, 0, 1, 0);
699     }
700 }
701
702
703 static void
704 draw_axis (ModeInfo *mi)
705 {
706   GLfloat s;
707   glDisable (GL_TEXTURE_2D);
708   glDisable (GL_LIGHTING);
709   glPushMatrix();
710
711   align_axis (mi, 0);
712   glTranslatef (0.34, 0.39, -0.61);
713
714   s = 0.96;
715   glScalef (s, s, s);   /* tighten up the enclosing sphere */
716
717   glColor3f (0.5, 0.5, 0);
718
719   glRotatef (90,  1, 0, 0);    /* unit_sphere is off by 90 */
720   glRotatef (9.5, 0, 1, 0);    /* line up the time zones */
721   glFrontFace (GL_CCW);
722   unit_sphere (12, 24, True);
723   glBegin(GL_LINES);
724   glVertex3f(0, -2, 0);
725   glVertex3f(0,  2, 0);
726   glEnd();
727
728   glPopMatrix();
729 }
730
731
732
733
734 ENTRYPOINT Bool
735 planet_handle_event (ModeInfo *mi, XEvent *event)
736 {
737   planetstruct *gp = &planets[MI_SCREEN(mi)];
738
739   if (gltrackball_event_handler (event, gp->trackball,
740                                  MI_WIDTH (mi), MI_HEIGHT (mi),
741                                  &gp->button_down_p))
742     return True;
743   else if (event->xany.type == KeyPress)
744     {
745       KeySym keysym;
746       char c = 0;
747       XLookupString (&event->xkey, &c, 1, &keysym, 0);
748       if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
749         {
750           switch (gp->state) {
751           case FLAT: case ICO: case STEL: case AXIS: case ICO2:
752             gp->ratio = 1;
753             break;
754           default:
755             break;
756           }
757           return True;
758         }
759     }
760
761   return False;
762 }
763
764
765 ENTRYPOINT void
766 init_planet (ModeInfo * mi)
767 {
768   planetstruct *gp;
769   int screen = MI_SCREEN(mi);
770   Bool wire = MI_IS_WIREFRAME(mi);
771
772   if (planets == NULL) {
773     if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
774                                           sizeof (planetstruct))) == NULL)
775       return;
776   }
777   gp = &planets[screen];
778
779   if ((gp->glx_context = init_GL(mi)) != NULL) {
780     reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
781   }
782
783   gp->state = STARTUP;
784   gp->ratio = 0;
785   gp->font_data = load_texture_font (mi->dpy, "labelFont");
786
787   {
788     double spin_speed   = 0.1;
789     double wander_speed = 0.002;
790     gp->rot = make_rotator (do_roll ? spin_speed : 0,
791                             do_roll ? spin_speed : 0,
792                             0, 1,
793                             do_wander ? wander_speed : 0,
794                             False);
795     gp->trackball = gltrackball_init (True);
796   }
797
798   if (wire)
799     do_texture = False;
800
801   if (do_texture)
802     setup_texture (mi);
803
804   if (do_stars)
805     init_stars (mi);
806
807   glEnable (GL_DEPTH_TEST);
808   glEnable (GL_NORMALIZE);
809   glEnable (GL_CULL_FACE);
810
811   if (!wire)
812     {
813       GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
814       GLfloat amb[4] = {0.4, 0.4, 0.4, 1.0};
815       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
816       GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
817
818       glEnable(GL_LIGHTING);
819       glEnable(GL_LIGHT0);
820       glLightfv(GL_LIGHT0, GL_POSITION, pos);
821       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
822       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
823       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
824     }
825 }
826
827
828 static GLfloat
829 ease_fn (GLfloat r)
830 {
831   return cos ((r/2 + 1) * M_PI) + 1; /* Smooth curve up, end at slope 1. */
832 }
833
834
835 static GLfloat
836 ease_ratio (GLfloat r)
837 {
838   GLfloat ease = 0.35;
839   if      (r <= 0)     return 0;
840   else if (r >= 1)     return 1;
841   else if (r <= ease)  return     ease * ease_fn (r / ease);
842   else if (r > 1-ease) return 1 - ease * ease_fn ((1 - r) / ease);
843   else                 return r;
844 }
845
846
847 ENTRYPOINT void
848 draw_planet (ModeInfo * mi)
849 {
850   planetstruct *gp = &planets[MI_SCREEN(mi)];
851   int wire = MI_IS_WIREFRAME(mi);
852   Display *dpy = MI_DISPLAY(mi);
853   Window window = MI_WINDOW(mi);
854   double x, y, z;
855
856   if (!gp->glx_context)
857     return;
858
859   glDrawBuffer(GL_BACK);
860   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
861
862   glXMakeCurrent (dpy, window, *(gp->glx_context));
863
864   mi->polygon_count = 0;
865
866   if (! gp->button_down_p)
867     switch (gp->state) {
868     case STARTUP:  gp->ratio += speed * 0.01;  break;
869     case FLAT:     gp->ratio += speed * 0.005; break;
870     case FOLD:     gp->ratio += speed * 0.01;  break;
871     case ICO:      gp->ratio += speed * 0.01;  break;
872     case STEL_IN:  gp->ratio += speed * 0.05;  break;
873     case STEL:     gp->ratio += speed * 0.01;  break;
874     case STEL_OUT: gp->ratio += speed * 0.07;  break;
875     case ICO2:     gp->ratio += speed * 0.07;  break;
876     case AXIS:     gp->ratio += speed * 0.02;  break;
877     case SPIN:     gp->ratio += speed * 0.005; break;
878     case UNFOLD:   gp->ratio += speed * 0.01;  break;
879     default:       abort();
880     }
881
882   if (gp->ratio > 1.0)
883     {
884       gp->ratio = 0;
885       switch (gp->state) {
886       case STARTUP:  gp->state = FLAT;     break;
887       case FLAT:     gp->state = FOLD;     break;
888       case FOLD:     gp->state = ICO;      break;
889       case ICO:      gp->state = STEL_IN;  break;
890       case STEL_IN:  gp->state = STEL;     break;
891       case STEL:
892         {
893           int i = (random() << 9) % 7;
894           gp->state = (i < 3 ? STEL_OUT :
895                        i < 6 ? SPIN : AXIS);
896         }
897         break;
898       case AXIS:     gp->state = STEL_OUT; break;
899       case SPIN:     gp->state = STEL_OUT; break;
900       case STEL_OUT: gp->state = ICO2;     break;
901       case ICO2:     gp->state = UNFOLD;   break;
902       case UNFOLD:   gp->state = FLAT;     break;
903       default:       abort();
904       }
905     }
906
907   glEnable(GL_LINE_SMOOTH);
908   glEnable(GL_POINT_SMOOTH);
909   glEnable(GL_DEPTH_TEST);
910   glEnable(GL_CULL_FACE);
911   glCullFace(GL_BACK); 
912
913   glPushMatrix();
914
915   gltrackball_rotate (gp->trackball);
916   glRotatef (current_device_rotation(), 0, 0, 1);
917
918   if (gp->state != STARTUP)
919     {
920       get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
921       x = (x - 0.5) * 3;
922       y = (y - 0.5) * 3;
923       z = 0;
924       glTranslatef(x, y, z);
925     }
926
927   if (do_roll && gp->state != STARTUP)
928     {
929       get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
930       glRotatef (x * 360, 1.0, 0.0, 0.0);
931       glRotatef (y * 360, 0.0, 1.0, 0.0);
932     }
933
934   if (do_stars)
935     {
936       glDisable(GL_TEXTURE_2D);
937       glDisable(GL_LIGHTING);
938       glPushMatrix();
939       glScalef (60, 60, 60);
940       glRotatef (90, 1, 0, 0);
941       glRotatef (35, 1, 0, 0);
942       glCallList (gp->starlist);
943       mi->polygon_count += gp->starcount;
944       glPopMatrix();
945       glClear(GL_DEPTH_BUFFER_BIT);
946     }
947
948   if (! wire)
949     glEnable (GL_LIGHTING);
950
951   if (do_texture)
952     glEnable(GL_TEXTURE_2D);
953
954   glScalef (2.6, 2.6, 2.6);
955
956   {
957     GLfloat fold_ratio = 0;
958     GLfloat stel_ratio = 0;
959     switch (gp->state) {
960     case FOLD:     fold_ratio =     gp->ratio; break;
961     case UNFOLD:   fold_ratio = 1 - gp->ratio; break;
962     case ICO: case ICO2: fold_ratio = 1; break;
963     case STEL: case AXIS: case SPIN: fold_ratio = 1; stel_ratio = 1; break;
964     case STEL_IN:  fold_ratio = 1; stel_ratio = gp->ratio; break;
965     case STEL_OUT: fold_ratio = 1; stel_ratio = 1 - gp->ratio; break;
966     case STARTUP:      /* Tilt in from flat */
967       glRotatef (-90 * ease_ratio (1 - gp->ratio), 1, 0, 0);
968       break;
969
970     default: break;
971     }
972
973 # ifdef HAVE_MOBILE  /* Enlarge the icosahedron a bit to make it more visible */
974     {
975       GLfloat s = 1 + 1.3 * ease_ratio (fold_ratio);
976       glScalef (s, s, s);
977     }
978 # endif
979
980     if (gp->state == SPIN)
981       {
982         align_axis (mi, 0);
983         glRotatef (ease_ratio (gp->ratio) * 360 * 3, 0, 0, 1);
984         align_axis (mi, 1);
985       }
986
987     draw_triangles (mi, ease_ratio (fold_ratio), ease_ratio (stel_ratio));
988
989     if (gp->state == AXIS)
990       draw_axis(mi);
991   }
992
993   glPopMatrix();
994
995   if (mi->fps_p) do_fps (mi);
996   glFinish();
997   glXSwapBuffers(dpy, window);
998 }
999
1000
1001 ENTRYPOINT void
1002 release_planet (ModeInfo * mi)
1003 {
1004   if (planets != NULL) {
1005     int screen;
1006
1007     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1008       planetstruct *gp = &planets[screen];
1009
1010       if (gp->glx_context) {
1011         /* Display lists MUST be freed while their glXContext is current. */
1012         /* but this gets a BadMatch error. -jwz */
1013         /*glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(gp->glx_context));*/
1014
1015         if (glIsList(gp->starlist))
1016           glDeleteLists(gp->starlist, 1);
1017       }
1018     }
1019     (void) free((void *) planets);
1020     planets = NULL;
1021   }
1022   FreeAllGL(mi);
1023 }
1024
1025
1026 XSCREENSAVER_MODULE_2 ("DymaxionMap", dymaxionmap, planet)
1027
1028 #endif