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