1 /* dymaxionmap --- Buckminster Fuller's unwrapped icosahedral globe.
2 * Copyright (c) 2016-2018 Jamie Zawinski.
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.
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.
17 #define LABEL_FONT "-*-helvetica-bold-r-normal-*-*-240-*-*-*-*-*-*"
20 #define DEFAULTS "*delay: 20000 \n" \
21 "*showFPS: False \n" \
22 "*wireframe: False \n" \
23 "*labelFont: " LABEL_FONT "\n"
24 # define release_planet 0
25 # include "xlockmore.h" /* from the xscreensaver distribution */
26 #else /* !STANDALONE */
27 # include "xlock.h" /* from the xlockmore distribution */
28 #endif /* !STANDALONE */
30 #ifdef USE_GL /* whole file */
35 #include "dymaxionmap-coords.h"
39 # include <X11/Xmu/Drawing.h>
41 # include <Xmu/Drawing.h>
45 #define DEF_ROTATE "True"
46 #define DEF_ROLL "True"
47 #define DEF_WANDER "True"
48 #define DEF_TEXTURE "True"
49 #define DEF_STARS "True"
50 #define DEF_GRID "True"
51 #define DEF_SPEED "1.0"
52 #define DEF_IMAGE "BUILTIN_FLAT"
53 #define DEF_IMAGE2 "NONE"
54 #define DEF_FRAMES "720"
57 #define countof(x) (sizeof((x))/sizeof((*x)))
60 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
62 #define RANDSIGN() ((random() & 1) ? 1 : -1)
66 static int do_texture;
71 static char *which_image;
72 static char *which_image2;
74 static XrmOptionDescRec opts[] = {
75 {"-speed", ".speed", XrmoptionSepArg, 0 },
76 {"-roll", ".roll", XrmoptionNoArg, "true" },
77 {"+roll", ".roll", XrmoptionNoArg, "false" },
78 {"-wander", ".wander", XrmoptionNoArg, "true" },
79 {"+wander", ".wander", XrmoptionNoArg, "false" },
80 {"-texture", ".texture", XrmoptionNoArg, "true" },
81 {"+texture", ".texture", XrmoptionNoArg, "false" },
82 {"-stars", ".stars", XrmoptionNoArg, "true" },
83 {"+stars", ".stars", XrmoptionNoArg, "false" },
84 {"-grid", ".grid", XrmoptionNoArg, "true" },
85 {"+grid", ".grid", XrmoptionNoArg, "false" },
86 {"-flat", ".image", XrmoptionNoArg, "BUILTIN_FLAT" },
87 {"-satellite",".image", XrmoptionNoArg, "BUILTIN_SAT" },
88 {"-image", ".image", XrmoptionSepArg, 0 },
89 {"-image2", ".image2", XrmoptionSepArg, 0 },
90 {"-frames", ".frames", XrmoptionSepArg, 0 },
93 static argtype vars[] = {
94 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
95 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
96 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
97 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
98 {&do_stars, "stars", "Stars", DEF_STARS, t_Bool},
99 {&do_grid, "grid", "Grid", DEF_GRID, t_Bool},
100 {&which_image, "image", "Image", DEF_IMAGE, t_String},
101 {&which_image2,"image2", "Image2", DEF_IMAGE2, t_String},
102 {&frames, "frames", "Frames", DEF_FRAMES, t_Int},
105 ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
108 ModStruct planet_description =
109 {"planet", "init_planet", "draw_planet", NULL,
110 "draw_planet", "init_planet", "free_planet", &planet_opts,
111 1000, 1, 2, 1, 4, 1.0, "",
112 "Buckminster Fuller's unwrapped icosahedral globe", 0, NULL};
116 __extension__ /* don't warn about "string length is greater than the length
117 ISO C89 compilers are required to support" when including
118 the following XPM file... */
121 #include "images/gen/earth_flat_png.h"
122 #include "images/gen/earth_png.h"
123 #include "images/gen/earth_night_png.h"
124 #include "images/gen/ground_png.h"
126 #include "ximage-loader.h"
128 #include "gltrackball.h"
132 GLXContext *glx_context;
136 trackball_state *trackball;
138 enum { STARTUP, FLAT, FOLD,
139 ICO, STEL_IN, AXIS, SPIN, STEL, STEL_OUT,
140 ICO2, UNFOLD } state;
143 texture_font_data *font_data;
144 int loading_sw, loading_sh;
146 XImage *day, *night, *dusk, *cvt;
147 XImage **images; /* One image for each frame of time-of-day. */
150 double current_frame;
157 static planetstruct *planets = NULL;
164 # ifdef GETTIMEOFDAY_TWO_ARGS
166 gettimeofday(&now, &tzp);
171 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
175 /* Draw faint latitude and longitude lines into the RGBA XImage.
178 add_grid_lines (XImage *image)
182 for (i = 0; i < 24; i++)
184 int x = (i + 0.5) * image->width / (double) 24;
186 for (y = 0; y < image->height; y++)
188 unsigned long rgba = XGetPixel (image, x, y);
189 int r = (rgba >> 24) & 0xFF;
190 int g = (rgba >> 16) & 0xFF;
191 int b = (rgba >> 8) & 0xFF;
192 int a = (rgba >> 0) & 0xFF;
193 int off2 = (((r + g + b) / 3) < 0x7F ? off : -off);
194 r = MAX (0, MIN (0xFF, r + off2));
195 g = MAX (0, MIN (0xFF, g + off2));
196 b = MAX (0, MIN (0xFF, b + off2));
197 XPutPixel (image, x, y, (r << 24) | (g << 16) | (b << 8) | a);
201 for (i = 1; i < 11; i++)
203 int y = i * image->height / (double) 12;
205 for (x = 0; x < image->width; x++)
207 unsigned long rgba = XGetPixel (image, x, y);
208 int r = (rgba >> 24) & 0xFF;
209 int g = (rgba >> 16) & 0xFF;
210 int b = (rgba >> 8) & 0xFF;
211 int a = (rgba >> 0) & 0xFF;
212 int off2 = (((r + g + b) / 3) < 0x7F ? off : -off);
213 r = MAX (0, MIN (0xFF, r + off2));
214 g = MAX (0, MIN (0xFF, g + off2));
215 b = MAX (0, MIN (0xFF, b + off2));
216 XPutPixel (image, x, y, (r << 24) | (g << 16) | (b << 8) | a);
223 adjust_brightness (XImage *image, double amount)
225 uint32_t *in = (uint32_t *) image->data;
227 int end = image->height * image->bytes_per_line / 4;
228 for (i = 0; i < end; i++)
231 /* #### Why is this ABGR instead of RGBA? */
232 uint32_t a = (p >> 24) & 0xFF;
233 uint32_t g = (p >> 16) & 0xFF;
234 uint32_t b = (p >> 8) & 0xFF;
235 uint32_t r = (p >> 0) & 0xFF;
236 r = MAX(0, MIN(0xFF, (long) (r * amount)));
237 g = MAX(0, MIN(0xFF, (long) (g * amount)));
238 b = MAX(0, MIN(0xFF, (long) (b * amount)));
239 p = (a << 24) | (g << 16) | (b << 8) | r;
246 vector_angle (XYZ a, XYZ b)
248 double La = sqrt (a.x*a.x + a.y*a.y + a.z*a.z);
249 double Lb = sqrt (b.x*b.x + b.y*b.y + b.z*b.z);
252 if (La == 0 || Lb == 0) return 0;
253 if (a.x == b.x && a.y == b.y && a.z == b.z) return 0;
255 /* dot product of two vectors is defined as:
256 La * Lb * cos(angle between vectors)
257 and is also defined as:
258 ax*bx + ay*by + az*bz
260 La * Lb * cos(angle) = ax*bx + ay*by + az*bz
261 cos(angle) = (ax*bx + ay*by + az*bz) / (La * Lb)
262 angle = acos ((ax*bx + ay*by + az*bz) / (La * Lb));
264 cc = (a.x*b.x + a.y*b.y + a.z*b.z) / (La * Lb);
265 if (cc > 1) cc = 1; /* avoid fp rounding error (1.000001 => sqrt error) */
272 /* Creates a grayscale image encoding the day/night terminator for a random
276 create_daylight_mask (Display *dpy, Visual *v, int w, int h)
278 XImage *image = XCreateImage (dpy, v, 8, ZPixmap, 0, 0, w, h, 8, 0);
281 double axial_tilt = frand(23.4) / (180/M_PI) * RANDSIGN();
282 double dusk = M_PI * 0.035;
284 sun.y = cos (axial_tilt);
285 sun.z = sin (axial_tilt);
287 image->data = (char *) malloc (image->height * image->bytes_per_line);
289 for (y = 0; y < image->height; y++)
291 double lat = -M_PI_2 + (M_PI * (y / (double) image->height));
292 double cosL = cos(lat);
293 double sinL = sin(lat);
294 for (x = 0; x < image->width; x++)
296 double lon = -M_PI_2 + (M_PI * 2 * (x / (double) image->width));
300 v.x = cos(lon) * cosL;
301 v.y = sin(lon) * cosL;
303 a = vector_angle (sun, v);
305 a = (a < -dusk ? 1 : a >= dusk ? 0 : (dusk - a) / (dusk * 2));
306 p = 0xFF & (unsigned long) (a * 0xFF);
307 XPutPixel (image, x, y, p);
315 load_images (ModeInfo *mi)
317 planetstruct *gp = &planets[MI_SCREEN(mi)];
320 if (which_image && !strcmp (which_image, "BUILTIN_FLAT"))
322 which_image = strdup("BUILTIN_FLAT");
323 which_image2 = strdup("BUILTIN_FLAT");
325 else if (which_image && !strcmp (which_image, "BUILTIN_SAT"))
327 which_image = strdup("BUILTIN_DAY");
328 which_image2 = strdup("BUILTIN_NIGHT");
331 if (!which_image) which_image = strdup("");
332 if (!which_image2) which_image2 = strdup("");
334 for (i = 0; i < 2; i++)
336 char *s = (i == 0 ? which_image : which_image2);
338 if (!strcmp (s, "BUILTIN_DAY"))
339 image = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
340 earth_png, sizeof(earth_png));
341 else if (!strcmp (s, "BUILTIN_NIGHT"))
342 image = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
343 earth_night_png,sizeof(earth_night_png));
344 else if (!strcmp (s, "BUILTIN") ||
345 !strcmp (s, "BUILTIN_FLAT") ||
346 (i == 0 && !strcmp (s, "")))
347 image = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
348 earth_flat_png, sizeof(earth_flat_png));
349 else if (!strcmp (s, "NONE"))
352 image = file_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi), s);
356 /* if (image) fprintf (stderr, "%s: %d: loaded %s\n", progname, i, s); */
364 if (gp->night && !gp->day)
365 gp->day = gp->night, gp->night = 0;
367 gp->nimages = frames;
368 gp->current_frame = random() % gp->nimages;
373 if (gp->nimages < 2 && gp->night)
375 XDestroyImage (gp->night);
385 if (gp->day) add_grid_lines (gp->day);
386 if (gp->night) add_grid_lines (gp->night);
389 if (gp->day && gp->night && !gp->dusk)
391 if (gp->day->width != gp->night->width ||
392 gp->day->height != gp->night->height)
394 fprintf (stderr, "%s: day and night images must be the same size"
395 " (%dx%d vs %dx%d)\n", progname,
396 gp->day->width, gp->day->height,
397 gp->night->width, gp->night->height);
400 gp->dusk = create_daylight_mask (MI_DISPLAY (mi), MI_VISUAL (mi),
401 gp->day->width, gp->day->height);
404 /* Make the day image brighter, because that's easier than doing it
406 adjust_brightness (gp->day, 1.4);
408 if (!strcmp (which_image, which_image2))
409 /* If day and night are the same image, make night way darker. */
410 adjust_brightness (gp->night, 0.2);
412 /* Otherwise make it just a little darker. */
413 adjust_brightness (gp->night, 0.7);
416 gp->images = (XImage **) calloc (gp->nimages, sizeof(*gp->images));
418 /* Create 'cvt', a map that projects each pixel from Equirectangular to
419 Dymaxion. It is 2x the width/height of the source images. We iterate
420 by half pixel to make sure we hit every pixel in 'out'. It would be
421 cleaner to iterate over 'out' instead of over 'in' but
422 dymaxionmap-coords.c only goes forward. This is... not super fast.
426 double H = 3 * sqrt(3)/2;
428 int w = gp->day->width;
429 int h = gp->day->height;
432 gp->cvt = XCreateImage (MI_DISPLAY(mi), MI_VISUAL(mi), 32, ZPixmap, 0, 0,
433 gp->day->width*2, gp->day->height*2, 32, 0);
434 gp->cvt->data = (char *)
435 malloc (gp->cvt->height * gp->cvt->bytes_per_line);
436 out = (uint32_t *) gp->cvt->data;
438 for (y2 = 0; y2 < h*2; y2++)
440 double y = (double) y2/2;
441 double lat = -90 + (180 * (y / (double) h));
442 for (x2 = 0; x2 < w*2; x2++)
444 double x = (double) x2/2;
445 double lon = -180 + (360 * x / w);
447 dymaxion_convert (lon, lat, &ox, &oy);
448 ox = w - (w * ox / W);
451 *out++ = (((((uint32_t) ox) & 0xFFFF) << 16) |
452 ((((uint32_t) oy) & 0xFFFF)));
457 /* A 128 GB iPhone 6s dies at around 540 frames, ~1 GB of XImages.
458 A 16 GB iPad Air 2 dies at around 320 frames, ~640 MB.
459 Caching on mobile doesn't matter much: we can just run at 100% CPU.
461 On some systems it would be more efficient to cache the images inside
462 a texture on the GPU instead of moving it from RAM to GPU every few
463 frames; but on other systems, we'd just run out of GPU memory instead. */
465 unsigned long cache_size = (gp->day->width * gp->day->height * 4 *
468 unsigned long max = 320 * 1024 * 1024L; /* 320 MB */
470 unsigned long max = 2 * 1024 * 1024 * 1024L; /* 2 GB */
472 gp->cache_p = (cache_size < max);
478 cache_current_frame (ModeInfo *mi)
480 planetstruct *gp = &planets[MI_SCREEN(mi)];
481 XImage *blended, *dymaxion;
482 int i, x, y, w, h, end, xoff;
483 uint32_t *day, *night, *cvt, *out;
486 if (gp->images[(int) gp->current_frame])
490 ? gp->dusk->width * ((double) gp->current_frame / gp->nimages)
500 /* Blend the foreground and background images through the dusk map.
502 blended = XCreateImage (MI_DISPLAY(mi), MI_VISUAL(mi),
503 gp->day->depth, ZPixmap, 0, 0, w, h, 32, 0);
504 if (!blended) abort();
505 blended->data = (char *) malloc (h * blended->bytes_per_line);
506 if (!blended->data) abort();
508 end = blended->height * blended->bytes_per_line / 4;
509 day = (uint32_t *) gp->day->data;
510 night = (uint32_t *) gp->night->data;
511 dusk = (uint8_t *) gp->dusk->data;
512 out = (uint32_t *) blended->data;
514 for (i = 0; i < end; i++)
517 uint32_t n = *night++;
520 double r = dusk[y * w + ((x + xoff) % w)] / 256.0;
522 # define ADD(M) (((unsigned long) \
523 ((((d >> M) & 0xFF) * r) + \
524 (((n >> M) & 0xFF) * r2))) \
526 /* #### Why is this ABGR instead of RGBA? */
527 *out++ = (0xFF << 24) | ADD(16) | ADD(8) | ADD(0);
532 /* Convert blended Equirectangular to Dymaxion through the 'cvt' map.
534 dymaxion = XCreateImage (MI_DISPLAY(mi), MI_VISUAL(mi),
535 gp->day->depth, ZPixmap, 0, 0, w, h, 32, 0);
536 dymaxion->data = (char *) calloc (h, dymaxion->bytes_per_line);
538 day = (uint32_t *) blended->data;
539 out = (uint32_t *) dymaxion->data;
540 cvt = (uint32_t *) gp->cvt->data;
542 for (y = 0; y < h*2; y++)
543 for (x = 0; x < w*2; x++)
545 unsigned long m = *cvt++;
546 unsigned long dx = (m >> 16) & 0xFFFF;
547 unsigned long dy = m & 0xFFFF;
548 unsigned long p = day[(y>>1) * w + (x>>1)];
549 unsigned long p2 = out[dy * w + dx];
551 /* RGBA nonzero alpha: initialized. Average with existing,
552 otherwise the grid lines look terrible. */
553 p = (((((p>>24) & 0xFF) + ((p2>>24) & 0xFF)) >> 1) << 24 |
554 ((((p>>16) & 0xFF) + ((p2>>16) & 0xFF)) >> 1) << 16 |
555 ((((p>> 8) & 0xFF) + ((p2>> 8) & 0xFF)) >> 1) << 8 |
556 ((((p>> 0) & 0xFF) + ((p2>> 0) & 0xFF)) >> 1) << 0);
557 out[dy * w + dx] = p;
560 /* Fill in the triangles that are not a part of The World with the
561 color of the ocean to avoid texture-tearing on the folded edges.
563 out = (uint32_t *) dymaxion->data;
564 end = dymaxion->height * dymaxion->bytes_per_line / 4;
566 double lat = -48.44, lon = -123.39; /* R'Lyeh */
567 int x = (lon + 180) * blended->width / 360.0;
568 int y = (lat + 90) * blended->height / 180.0;
569 unsigned long ocean = XGetPixel (gp->day, x, y);
570 for (i = 0; i < end; i++)
573 if (! (p & 0xFF000000)) /* AGBR */
579 if (blended != gp->day)
580 XDestroyImage (blended);
582 gp->images[(int) gp->current_frame] = dymaxion;
584 if (!gp->cache_p) /* Keep only one image around; recompute every time. */
586 i = ((int) gp->current_frame) - 1;
587 if (i < 0) i = gp->nimages - 1;
590 XDestroyImage (gp->images[i]);
598 setup_texture (ModeInfo * mi)
600 planetstruct *gp = &planets[MI_SCREEN(mi)];
603 glGenTextures (1, &gp->tex1);
604 glBindTexture (GL_TEXTURE_2D, gp->tex1);
606 /* Must be after glBindTexture */
607 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
608 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
609 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
610 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
611 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
615 glGenTextures (1, &gp->tex2);
616 glBindTexture (GL_TEXTURE_2D, gp->tex2);
618 /* Must be after glBindTexture */
619 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
620 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
621 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
622 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
623 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
625 /* The underground image can go on flat, without the dymaxion transform. */
626 ground = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
627 ground_png, sizeof(ground_png));
629 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
630 /* glPixelStorei(GL_UNPACK_ROW_LENGTH, ground->width); */
631 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
632 ground->width, ground->height, 0,
633 GL_RGBA, GL_UNSIGNED_BYTE, ground->data);
634 check_gl_error ("ground texture");
635 XDestroyImage (ground);
640 init_stars (ModeInfo *mi)
642 planetstruct *gp = &planets[MI_SCREEN(mi)];
644 int width = MI_WIDTH(mi);
645 int height = MI_HEIGHT(mi);
646 int size = (width > height ? width : height);
647 int nstars = size * size / 80;
650 int steps = max_size / inc;
653 if (MI_WIDTH(mi) > 2560) { /* Retina displays */
658 gp->starlist = glGenLists(1);
659 glNewList(gp->starlist, GL_COMPILE);
660 for (j = 1; j <= steps; j++)
662 glPointSize(inc * j * scale);
664 for (i = 0; i < nstars / steps; i++)
667 GLfloat r = 0.15 + frand(0.3);
668 GLfloat g = r + frand(d) - d;
669 GLfloat b = r + frand(d) - d;
671 GLfloat x = frand(1)-0.5;
672 GLfloat y = frand(1)-0.5;
673 GLfloat z = ((random() & 1)
675 : (BELLRAND(1)-0.5)/12); /* milky way */
676 d = sqrt (x*x + y*y + z*z);
681 glVertex3f (x, y, z);
688 check_gl_error("stars initialization");
693 reshape_planet (ModeInfo *mi, int width, int height)
695 GLfloat h = (GLfloat) height / (GLfloat) width;
697 glViewport(0, 0, (GLint) width, (GLint) height);
698 glMatrixMode(GL_PROJECTION);
700 glFrustum(-1.0, 1.0, -h, h, 5.0, 200.0);
701 glMatrixMode(GL_MODELVIEW);
703 glTranslatef(0.0, 0.0, -40);
705 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
707 int o = (int) current_device_rotation();
708 if (o != 0 && o != 180 && o != -180)
713 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
718 do_normal2 (ModeInfo *mi, Bool frontp, XYZ a, XYZ b, XYZ c)
721 ? calc_normal (a, b, c)
722 : calc_normal (b, a, c));
723 glNormal3f (n.x, n.y, n.z);
726 if (frontp && MI_IS_WIREFRAME(mi))
729 glVertex3f ((a.x + b.x + c.x) / 3,
730 (a.y + b.y + c.y) / 3,
731 (a.z + b.z + c.z) / 3);
732 glVertex3f ((a.x + b.x + c.x) / 3 + n.x,
733 (a.y + b.y + c.y) / 3 + n.y,
734 (a.z + b.z + c.z) / 3 + n.z);
742 triangle0 (ModeInfo *mi, Bool frontp, GLfloat stel_ratio, int facemask,
745 /* Render a triangle as six sub-triangles.
746 Facemask bits 0-5 indicate which sub-triangle to draw.
759 B ----------------------- C
763 Bool wire = MI_IS_WIREFRAME(mi);
764 GLfloat h = sqrt(3) / 2;
765 GLfloat h2 = sqrt(h*h - (h/2)*(h/2)) - 0.5;
766 XYZ A, B, C, D, E, F, G;
767 XYZ tA, tB, tC, tD, tE, tF, tG;
770 A.x = 0; A.y = h; A.z = 0;
771 B.x = -0.5, B.y = 0; B.z = 0;
772 C.x = 0.5, C.y = 0; C.z = 0;
773 D.x = 0; D.y = h/3; D.z = 0;
774 E.x = -h2; E.y = h/2; E.z = 0;
775 F.x = h2; F.y = h/2; F.z = 0;
776 G.x = 0; G.y = 0; G.z = 0;
778 /* When tweaking object XY to stellate, don't change texture coordinates. */
779 tA = A; tB = B; tC = C; tD = D; tE = E; tF = F; tG = G;
781 /* Eyeballed this to find the depth of stellation that seems to most
782 approximate a sphere.
784 D.z = 0.193 * stel_ratio;
786 /* We want to raise E, F and G as well but we can't just shift Z:
787 we need to keep them on the same vector from the center of the sphere,
788 which means also changing F and G's X and Y.
790 E.z = F.z = G.z = 0.132 * stel_ratio;
792 double magic_x = 0.044;
793 double magic_y = 0.028;
795 G.y -= sqrt (magic_x*magic_x + magic_y*magic_y) * stel_ratio;
796 E.x -= magic_x * stel_ratio;
797 E.y += magic_y * stel_ratio;
798 F.x += magic_x * stel_ratio;
799 F.y += magic_y * stel_ratio;
806 ta = tE; tb = tD; tc = tA;
807 do_normal2 (mi, frontp, a, b, c);
808 glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
809 glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
810 glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
811 glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
818 ta = tD; tb = tF; tc = tA;
819 do_normal2 (mi, frontp, a, b, c);
820 glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
821 glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
822 glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
823 glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
830 ta = tD; tb = tC; tc = tF;
831 do_normal2 (mi, frontp, a, b, c);
832 glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
833 glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
834 glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
835 glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
842 ta = tG; tb = tC; tc = tD;
843 do_normal2 (mi, frontp, a, b, c);
844 glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
845 glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
846 glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
847 glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
854 ta = tB; tb = tG; tc = tD;
855 do_normal2 (mi, frontp, a, b, c);
856 glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
857 glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
858 glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
859 glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
866 ta = tB; tb = tD; tc = tE;
867 do_normal2 (mi, frontp, a, b, c);
868 glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
869 glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
870 glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
871 glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
878 ta = tE; tb = tD; tc = tA;
879 do_normal2 (mi, frontp, a, b, c);
880 glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
881 glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z);
882 glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z);
883 glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z);
901 /* The segments, numbered arbitrarily from the top left:
904 \ 0 / \ / \3> | \ 5 /
905 \ / 1 \ / 2 \| ..|4 \ /-6-..
906 ___________\/______\/______\/______\/______\
908 |7 / \ 9 / \ 11 / \ 13 / \ 15 / \
909 | / 8 \ / 10 \ / 12 \ / 14 \ / 16 \
910 |/______\/______\/______\/______\/______\
912 \ 17 / \ 18 / / \ 20 / \
913 \ / \ / / 19 \ / 21 \
914 \/ \/ /______\/______\
916 Each triangle can be connected to at most two other triangles.
917 We start from the middle, #12, and work our way to the edges.
920 (Note that dymaxionmap-coords.c uses a different numbering system.)
923 triangle (ModeInfo *mi, int which, Bool frontp,
924 GLfloat fold_ratio, GLfloat stel_ratio)
926 planetstruct *gp = &planets[MI_SCREEN(mi)];
927 const GLfloat fg[] = { 1, 1, 1, 1 };
928 const GLfloat bg[] = { 0.3, 0.3, 0.3, 1 };
930 GLfloat max = acos (sqrt(5)/3);
931 GLfloat rot = -max * fold_ratio / (M_PI/180);
932 Bool wire = MI_IS_WIREFRAME(mi);
937 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fg);
940 case 3: /* One third of the face. */
941 triangle0 (mi, frontp, stel_ratio, 1<<3 | 1<<4, corners);
943 case 4: /* Two thirds of the face: convex. */
944 triangle0 (mi, frontp, stel_ratio, 1<<1 | 1<<2 | 1<<3 | 1<<4, corners);
946 case 6: /* One half of the face. */
947 triangle0 (mi, frontp, stel_ratio, 1<<1 | 1<<2 | 1<<3, corners);
949 case 7: /* One half of the face. */
950 triangle0 (mi, frontp, stel_ratio, 1<<2 | 1<<3 | 1<<4, corners);
952 default: /* Full face. */
953 triangle0 (mi, frontp, stel_ratio, 0x3F, corners);
961 sprintf (tag, "%d", which);
963 glTranslatef (-0.1, 0.2, 0);
964 glScalef (0.005, 0.005, 0.005);
965 print_texture_string (gp->font_data, tag);
971 /* The connection hierarchy of the faces starting at the middle, #12. */
974 case 1: a = 0; b = -1; break;
975 case 2: a = -1; b = 3; break;
977 case 4: a = -1; b = 5; break;
978 case 5: a = -1; b = 6; break;
981 case 8: a = 17; b = 7; break;
982 case 9: a = 8; b = -1; break;
983 case 10: a = 18; b = 9; break;
984 case 11: a = 10; b = 1; break;
985 case 12: a = 11; b = 13; break;
986 case 13: a = 2; b = 14; break;
987 case 14: a = 15; b = 20; break;
988 case 15: a = 4; b = 16; break;
993 case 20: a = 21; b = 19; break;
995 default: abort(); break;
1001 glTranslatef (-0.5, 0, 0); /* Move model matrix to upper left */
1002 glRotatef (60, 0, 0, 1);
1003 glTranslatef ( 0.5, 0, 0);
1005 glMatrixMode(GL_TEXTURE);
1006 /* glPushMatrix(); */
1007 glTranslatef (-0.5, 0, 0); /* Move texture matrix the same way */
1008 glRotatef (60, 0, 0, 1);
1009 glTranslatef ( 0.5, 0, 0);
1011 glMatrixMode(GL_MODELVIEW);
1013 glRotatef (rot, 1, 0, 0);
1014 triangle (mi, a, frontp, fold_ratio, stel_ratio);
1016 /* This should just be a PopMatrix on the TEXTURE stack, but
1017 fucking iOS has GL_MAX_TEXTURE_STACK_DEPTH == 4! WTF!
1018 So we have to undo our rotations and translations manually.
1020 glMatrixMode(GL_TEXTURE);
1021 /* glPopMatrix(); */
1022 glTranslatef (-0.5, 0, 0);
1023 glRotatef (-60, 0, 0, 1);
1024 glTranslatef (0.5, 0, 0);
1026 glMatrixMode(GL_MODELVIEW);
1033 glTranslatef (0.5, 0, 0); /* Move model matrix to upper right */
1034 glRotatef (-60, 0, 0, 1);
1035 glTranslatef (-0.5, 0, 0);
1037 glMatrixMode(GL_TEXTURE);
1038 /* glPushMatrix(); */
1039 glTranslatef (0.5, 0, 0); /* Move texture matrix the same way */
1040 glRotatef (-60, 0, 0, 1);
1041 glTranslatef (-0.5, 0, 0);
1043 glMatrixMode(GL_MODELVIEW);
1045 glRotatef (rot, 1, 0, 0);
1046 triangle (mi, b, frontp, fold_ratio, stel_ratio);
1048 /* See above. Grr. */
1049 glMatrixMode(GL_TEXTURE);
1050 /* glPopMatrix(); */
1051 glTranslatef (0.5, 0, 0);
1052 glRotatef (60, 0, 0, 1);
1053 glTranslatef (-0.5, 0, 0);
1055 glMatrixMode(GL_MODELVIEW);
1060 /* Draw a border around the edge of the world.
1062 if (!wire && frontp && stel_ratio == 0 && fold_ratio < 0.95)
1065 GLfloat c[] = { 0, 0.2, 0.5, 1 };
1066 c[3] = 1-fold_ratio;
1070 case 0: edges = 1<<0 | 1<<2; break;
1071 case 1: edges = 1<<2; break;
1072 case 2: edges = 1<<0; break;
1073 case 3: edges = 1<<3 | 1<<4; break;
1074 case 4: edges = 1<<3 | 1<<5; break;
1075 case 5: edges = 1<<0 | 1<<6; break;
1076 case 6: edges = 1<<2 | 1<<7; break;
1077 case 16: edges = 1<<0 | 1<<2; break;
1078 case 21: edges = 1<<0 | 1<<2; break;
1079 case 19: edges = 1<<0 | 1<<2; break;
1080 case 12: edges = 1<<1; break;
1081 case 18: edges = 1<<0 | 1<<2; break;
1082 case 17: edges = 1<<0 | 1<<2; break;
1083 case 7: edges = 1<<8 | 1<<9; break;
1084 case 9: edges = 1<<2; break;
1088 glDisable (GL_TEXTURE_2D);
1089 glDisable (GL_LIGHTING);
1095 glVertex3f (corners[0].x, corners[0].y, corners[0].z);
1096 glVertex3f (corners[1].x, corners[1].y, corners[1].z);
1100 glVertex3f (corners[1].x, corners[1].y, corners[1].z);
1101 glVertex3f (corners[2].x, corners[2].y, corners[2].z);
1105 glVertex3f (corners[2].x, corners[2].y, corners[2].z);
1106 glVertex3f (corners[0].x, corners[0].y, corners[0].z);
1110 glVertex3f (corners[1].x, corners[1].y, corners[1].z);
1111 glVertex3f (corners[3].x, corners[3].y, corners[3].z);
1115 glVertex3f (corners[3].x, corners[3].y, corners[3].z);
1116 glVertex3f (corners[2].x, corners[2].y, corners[2].z);
1120 glVertex3f (corners[3].x, corners[3].y, corners[3].z);
1121 glVertex3f (corners[0].x, corners[0].y, corners[0].z);
1125 glVertex3f (corners[0].x, corners[0].y, corners[0].z);
1126 glVertex3f (corners[5].x, corners[5].y, corners[5].z);
1130 glVertex3f (corners[0].x, corners[0].y, corners[0].z);
1131 glVertex3f (corners[6].x, corners[6].y, corners[6].z);
1135 glVertex3f (corners[1].x, corners[1].y, corners[1].z);
1136 glVertex3f (corners[5].x, corners[5].y, corners[5].z);
1140 glVertex3f (corners[5].x, corners[5].y, corners[5].z);
1141 glVertex3f (corners[2].x, corners[2].y, corners[2].z);
1144 glEnable (GL_TEXTURE_2D);
1145 glEnable (GL_LIGHTING);
1151 draw_triangles (ModeInfo *mi, GLfloat fold_ratio, GLfloat stel_ratio)
1153 planetstruct *gp = &planets[MI_SCREEN(mi)];
1154 Bool wire = MI_IS_WIREFRAME(mi);
1155 GLfloat h = sqrt(3) / 2;
1158 glTranslatef (0, -h/3, 0); /* Center on face 12 */
1160 /* When closed, center on midpoint of icosahedron. Eyeballed this. */
1161 glTranslatef (0, 0, fold_ratio * 0.754);
1163 glFrontFace (GL_CCW);
1165 /* Adjust the texture matrix so that it has the same coordinate space
1168 glMatrixMode(GL_TEXTURE);
1172 GLfloat texh = 3 * h;
1174 GLfloat midy = 3 * c;
1175 glScalef (1/texw, -1/texh, 1);
1176 glTranslatef (midx, midy, 0);
1178 glMatrixMode(GL_MODELVIEW);
1185 glDisable (GL_TEXTURE_2D);
1186 else if (do_texture)
1188 glEnable (GL_TEXTURE_2D);
1189 glBindTexture (GL_TEXTURE_2D, gp->tex1);
1192 glDisable (GL_TEXTURE_2D);
1194 triangle (mi, 12, True, fold_ratio, stel_ratio);
1199 glDisable (GL_TEXTURE_2D);
1200 else if (do_texture)
1202 glEnable (GL_TEXTURE_2D);
1203 glBindTexture (GL_TEXTURE_2D, gp->tex2);
1206 glDisable (GL_TEXTURE_2D);
1208 glFrontFace (GL_CW);
1210 triangle (mi, 12, False, fold_ratio, 0);
1212 glMatrixMode(GL_TEXTURE);
1214 glMatrixMode(GL_MODELVIEW);
1219 align_axis (ModeInfo *mi, int undo)
1221 /* Rotate so that an axis is lined up with the north and south poles
1222 on the map, which are not in the center of their faces, or any
1223 other easily computable spot. */
1230 glRotatef (-r2, 0, 1, 0);
1231 glRotatef ( r2, 1, 0, 0);
1232 glRotatef (-r1, 1, 0, 0);
1236 glRotatef (r1, 1, 0, 0);
1237 glRotatef (-r2, 1, 0, 0);
1238 glRotatef ( r2, 0, 1, 0);
1244 draw_axis (ModeInfo *mi)
1247 glDisable (GL_TEXTURE_2D);
1248 glDisable (GL_LIGHTING);
1252 glTranslatef (0.34, 0.39, -0.61);
1255 glScalef (s, s, s); /* tighten up the enclosing sphere */
1258 glColor3f (0.5, 0.5, 0);
1260 glRotatef (90, 1, 0, 0); /* unit_sphere is off by 90 */
1261 glRotatef (9.5, 0, 1, 0); /* line up the time zones */
1262 glFrontFace (GL_CCW);
1263 unit_sphere (12, 24, True);
1265 glVertex3f(0, -2, 0);
1266 glVertex3f(0, 2, 0);
1276 planet_handle_event (ModeInfo *mi, XEvent *event)
1278 planetstruct *gp = &planets[MI_SCREEN(mi)];
1280 if (gltrackball_event_handler (event, gp->trackball,
1281 MI_WIDTH (mi), MI_HEIGHT (mi),
1282 &gp->button_down_p))
1284 else if (event->xany.type == KeyPress)
1288 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1289 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
1292 double cf = gp->current_frame;
1294 /* Switch between the satellite and flat map, preserving position. */
1295 if (gp->day) XDestroyImage (gp->day);
1296 if (gp->night) XDestroyImage (gp->night);
1297 if (gp->cvt) XDestroyImage (gp->cvt);
1302 for (i = 0; i < gp->nimages; i++)
1303 if (gp->images[i]) XDestroyImage (gp->images[i]);
1307 which_image = strdup (!strcmp (which_image, "BUILTIN_DAY")
1308 ? "BUILTIN_FLAT" : "BUILTIN_DAY");
1309 which_image2 = strdup (!strcmp (which_image2, "BUILTIN_NIGHT")
1310 ? "BUILTIN_FLAT" : "BUILTIN_NIGHT");
1312 gp->current_frame = cf;
1314 switch (gp->state) {
1315 case FLAT: case ICO: case STEL: case AXIS: case ICO2:
1331 init_planet (ModeInfo * mi)
1334 int screen = MI_SCREEN(mi);
1335 Bool wire = MI_IS_WIREFRAME(mi);
1337 MI_INIT (mi, planets);
1338 gp = &planets[screen];
1340 if ((gp->glx_context = init_GL(mi)) != NULL) {
1341 reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1344 gp->state = STARTUP;
1346 gp->font_data = load_texture_font (mi->dpy, "labelFont");
1347 gp->delay = MI_DELAY(mi);
1350 double spin_speed = 0.1;
1351 double wander_speed = 0.002;
1352 gp->rot = make_rotator (do_roll ? spin_speed : 0,
1353 do_roll ? spin_speed : 0,
1355 do_wander ? wander_speed : 0,
1357 gp->rot2 = make_rotator (0, 0, 0, 0, wander_speed, False);
1358 gp->trackball = gltrackball_init (True);
1370 glEnable (GL_DEPTH_TEST);
1371 glEnable (GL_NORMALIZE);
1372 glEnable (GL_CULL_FACE);
1376 GLfloat pos[4] = {1, 1, 1, 0};
1377 GLfloat amb[4] = {0, 0, 0, 1};
1378 GLfloat dif[4] = {1, 1, 1, 1};
1379 GLfloat spc[4] = {0, 1, 1, 1};
1380 glEnable(GL_LIGHTING);
1381 glEnable(GL_LIGHT0);
1382 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1383 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
1384 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
1385 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1393 return cos ((r/2 + 1) * M_PI) + 1; /* Smooth curve up, end at slope 1. */
1398 ease_ratio (GLfloat r)
1400 GLfloat ease = 0.35;
1401 if (r <= 0) return 0;
1402 else if (r >= 1) return 1;
1403 else if (r <= ease) return ease * ease_fn (r / ease);
1404 else if (r > 1-ease) return 1 - ease * ease_fn ((1 - r) / ease);
1410 draw_planet (ModeInfo * mi)
1412 planetstruct *gp = &planets[MI_SCREEN(mi)];
1413 int wire = MI_IS_WIREFRAME(mi);
1414 Display *dpy = MI_DISPLAY(mi);
1415 Window window = MI_WINDOW(mi);
1416 long delay = gp->delay;
1419 if (!gp->glx_context)
1422 glDrawBuffer(GL_BACK);
1423 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1425 glXMakeCurrent (dpy, window, *(gp->glx_context));
1427 mi->polygon_count = 0;
1429 if (! gp->button_down_p)
1430 switch (gp->state) {
1431 case STARTUP: gp->ratio += speed * 0.01; break;
1432 case FLAT: gp->ratio += speed * 0.005 *
1433 /* Stay flat longer if animating day and night. */
1434 (gp->nimages <= 1 ? 1 : 0.3);
1436 case FOLD: gp->ratio += speed * 0.01; break;
1437 case ICO: gp->ratio += speed * 0.01; break;
1438 case STEL_IN: gp->ratio += speed * 0.05; break;
1439 case STEL: gp->ratio += speed * 0.01; break;
1440 case STEL_OUT: gp->ratio += speed * 0.07; break;
1441 case ICO2: gp->ratio += speed * 0.07; break;
1442 case AXIS: gp->ratio += speed * 0.02; break;
1443 case SPIN: gp->ratio += speed * 0.005; break;
1444 case UNFOLD: gp->ratio += speed * 0.01; break;
1448 if (gp->ratio > 1.0)
1451 switch (gp->state) {
1452 case STARTUP: gp->state = FLAT; break;
1453 case FLAT: gp->state = FOLD; break;
1454 case FOLD: gp->state = ICO; break;
1455 case ICO: gp->state = STEL_IN; break;
1456 case STEL_IN: gp->state = STEL; break;
1459 int i = (random() << 9) % 7;
1460 gp->state = (i < 3 ? STEL_OUT :
1461 i < 6 ? SPIN : AXIS);
1464 case AXIS: gp->state = STEL_OUT; break;
1465 case SPIN: gp->state = STEL_OUT; break;
1466 case STEL_OUT: gp->state = ICO2; break;
1467 case ICO2: gp->state = UNFOLD; break;
1468 case UNFOLD: gp->state = FLAT; break;
1473 glEnable(GL_LINE_SMOOTH);
1474 glEnable(GL_POINT_SMOOTH);
1475 glEnable(GL_DEPTH_TEST);
1476 glEnable(GL_CULL_FACE);
1477 glCullFace(GL_BACK);
1481 gltrackball_rotate (gp->trackball);
1482 glRotatef (current_device_rotation(), 0, 0, 1);
1484 # ifdef HAVE_MOBILE /* Fill more of the screen. */
1486 int size = MI_WIDTH(mi) < MI_HEIGHT(mi)
1487 ? MI_WIDTH(mi) : MI_HEIGHT(mi);
1488 GLfloat s = (size > 768 ? 1.4 : /* iPad */
1491 if (MI_WIDTH(mi) < MI_HEIGHT(mi))
1492 glRotatef (90, 0, 0, 1);
1496 if (gp->state != STARTUP)
1498 get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
1502 glTranslatef(x, y, z);
1505 if (do_roll && gp->state != STARTUP)
1508 get_position (gp->rot2, &x, &y, 0, !gp->button_down_p);
1509 glRotatef (max/2 - x*max, 1, 0, 0);
1510 glRotatef (max/2 - y*max, 0, 1, 0);
1515 glDisable(GL_TEXTURE_2D);
1516 glDisable(GL_LIGHTING);
1518 glScalef (60, 60, 60);
1519 glRotatef (90, 1, 0, 0);
1520 glRotatef (35, 1, 0, 0);
1521 glCallList (gp->starlist);
1522 mi->polygon_count += gp->starcount;
1524 glClear(GL_DEPTH_BUFFER_BIT);
1529 glEnable (GL_LIGHTING);
1530 glEnable (GL_BLEND);
1531 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1535 glEnable(GL_TEXTURE_2D);
1537 if (do_texture /* && !gp->button_down_p */)
1540 int prev = gp->current_frame;
1542 /* By default, advance terminator by about an hour every 5 seconds. */
1543 gp->current_frame += 0.1 * speed * (gp->nimages / 360.0);
1544 while (gp->current_frame >= gp->nimages)
1545 gp->current_frame -= gp->nimages;
1546 i = gp->current_frame;
1548 /* Load the current image into the texture.
1550 if (i != prev || !gp->images[i])
1552 double start = double_time();
1553 cache_current_frame (mi);
1555 glBindTexture (GL_TEXTURE_2D, gp->tex1);
1557 /* Must be after glBindTexture */
1558 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1559 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1560 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1561 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1562 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1564 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
1565 gp->images[i]->width,
1566 gp->images[i]->height, 0,
1567 GL_RGBA, GL_UNSIGNED_BYTE,
1568 gp->images[i]->data);
1569 check_gl_error ("texture");
1571 /* If caching the image took a bunch of time, deduct that from
1572 our per-frame delay to keep the timing a little smoother. */
1573 delay -= 1000000 * (double_time() - start);
1574 if (delay < 0) delay = 0;
1578 glTranslatef (-0.5, -0.4, 0);
1579 glScalef (2.6, 2.6, 2.6);
1582 GLfloat fold_ratio = 0;
1583 GLfloat stel_ratio = 0;
1584 switch (gp->state) {
1585 case FOLD: fold_ratio = gp->ratio; break;
1586 case UNFOLD: fold_ratio = 1 - gp->ratio; break;
1587 case ICO: case ICO2: fold_ratio = 1; break;
1588 case STEL: case AXIS: case SPIN: fold_ratio = 1; stel_ratio = 1; break;
1589 case STEL_IN: fold_ratio = 1; stel_ratio = gp->ratio; break;
1590 case STEL_OUT: fold_ratio = 1; stel_ratio = 1 - gp->ratio; break;
1591 case STARTUP: /* Tilt in from flat */
1592 glRotatef (-90 * ease_ratio (1 - gp->ratio), 1, 0, 0);
1598 # ifdef HAVE_MOBILE /* Enlarge the icosahedron a bit to make it more visible */
1600 GLfloat s = 1 + 1.3 * ease_ratio (fold_ratio);
1605 if (gp->state == SPIN)
1608 glRotatef (ease_ratio (gp->ratio) * 360 * 3, 0, 0, 1);
1612 draw_triangles (mi, ease_ratio (fold_ratio), ease_ratio (stel_ratio));
1614 if (gp->state == AXIS)
1620 if (mi->fps_p) do_fps (mi);
1622 glXSwapBuffers(dpy, window);
1624 MI_DELAY(mi) = delay;
1629 free_planet (ModeInfo * mi)
1631 planetstruct *gp = &planets[MI_SCREEN(mi)];
1634 if (gp->day) XDestroyImage (gp->day);
1635 if (gp->night) XDestroyImage (gp->night);
1636 if (gp->dusk) XDestroyImage (gp->dusk);
1637 if (gp->cvt) XDestroyImage (gp->cvt);
1639 for (i = 0; i < gp->nimages; i++)
1640 if (gp->images[i]) XDestroyImage (gp->images[i]);
1643 if (gp->glx_context) {
1644 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(gp->glx_context));
1646 if (glIsList(gp->starlist))
1647 glDeleteLists(gp->starlist, 1);
1652 XSCREENSAVER_MODULE_2 ("DymaxionMap", dymaxionmap, planet)