From d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 16 Oct 2018 10:08:48 +0200 Subject: Original 5.40 --- hacks/glx/dymaxionmap.c | 1654 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1654 insertions(+) create mode 100644 hacks/glx/dymaxionmap.c (limited to 'hacks/glx/dymaxionmap.c') diff --git a/hacks/glx/dymaxionmap.c b/hacks/glx/dymaxionmap.c new file mode 100644 index 0000000..711db2e --- /dev/null +++ b/hacks/glx/dymaxionmap.c @@ -0,0 +1,1654 @@ +/* dymaxionmap --- Buckminster Fuller's unwrapped icosahedral globe. + * Copyright (c) 2016-2018 Jamie Zawinski. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + */ + +#define LABEL_FONT "-*-helvetica-bold-r-normal-*-*-240-*-*-*-*-*-*" + +#ifdef STANDALONE +#define DEFAULTS "*delay: 20000 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + "*labelFont: " LABEL_FONT "\n" +# define release_planet 0 +# include "xlockmore.h" /* from the xscreensaver distribution */ +#else /* !STANDALONE */ +# include "xlock.h" /* from the xlockmore distribution */ +#endif /* !STANDALONE */ + +#ifdef USE_GL /* whole file */ + +#include "sphere.h" +#include "normals.h" +#include "texfont.h" +#include "dymaxionmap-coords.h" + +#ifdef HAVE_XMU +# ifndef VMS +# include +#else /* VMS */ +# include +# endif /* VMS */ +#endif + +#define DEF_ROTATE "True" +#define DEF_ROLL "True" +#define DEF_WANDER "True" +#define DEF_TEXTURE "True" +#define DEF_STARS "True" +#define DEF_GRID "True" +#define DEF_SPEED "1.0" +#define DEF_IMAGE "BUILTIN_FLAT" +#define DEF_IMAGE2 "NONE" +#define DEF_FRAMES "720" + +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#undef BELLRAND +#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) +#undef RANDSIGN +#define RANDSIGN() ((random() & 1) ? 1 : -1) + +static int do_roll; +static int do_wander; +static int do_texture; +static int do_stars; +static int do_grid; +static int frames; +static GLfloat speed; +static char *which_image; +static char *which_image2; + +static XrmOptionDescRec opts[] = { + {"-speed", ".speed", XrmoptionSepArg, 0 }, + {"-roll", ".roll", XrmoptionNoArg, "true" }, + {"+roll", ".roll", XrmoptionNoArg, "false" }, + {"-wander", ".wander", XrmoptionNoArg, "true" }, + {"+wander", ".wander", XrmoptionNoArg, "false" }, + {"-texture", ".texture", XrmoptionNoArg, "true" }, + {"+texture", ".texture", XrmoptionNoArg, "false" }, + {"-stars", ".stars", XrmoptionNoArg, "true" }, + {"+stars", ".stars", XrmoptionNoArg, "false" }, + {"-grid", ".grid", XrmoptionNoArg, "true" }, + {"+grid", ".grid", XrmoptionNoArg, "false" }, + {"-flat", ".image", XrmoptionNoArg, "BUILTIN_FLAT" }, + {"-satellite",".image", XrmoptionNoArg, "BUILTIN_SAT" }, + {"-image", ".image", XrmoptionSepArg, 0 }, + {"-image2", ".image2", XrmoptionSepArg, 0 }, + {"-frames", ".frames", XrmoptionSepArg, 0 }, +}; + +static argtype vars[] = { + {&speed, "speed", "Speed", DEF_SPEED, t_Float}, + {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool}, + {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool}, + {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool}, + {&do_stars, "stars", "Stars", DEF_STARS, t_Bool}, + {&do_grid, "grid", "Grid", DEF_GRID, t_Bool}, + {&which_image, "image", "Image", DEF_IMAGE, t_String}, + {&which_image2,"image2", "Image2", DEF_IMAGE2, t_String}, + {&frames, "frames", "Frames", DEF_FRAMES, t_Int}, +}; + +ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL}; + +#ifdef USE_MODULES +ModStruct planet_description = +{"planet", "init_planet", "draw_planet", NULL, + "draw_planet", "init_planet", "free_planet", &planet_opts, + 1000, 1, 2, 1, 4, 1.0, "", + "Buckminster Fuller's unwrapped icosahedral globe", 0, NULL}; +#endif + +# ifdef __GNUC__ + __extension__ /* don't warn about "string length is greater than the length + ISO C89 compilers are required to support" when including + the following XPM file... */ +# endif + +#include "images/gen/earth_flat_png.h" +#include "images/gen/earth_png.h" +#include "images/gen/earth_night_png.h" +#include "images/gen/ground_png.h" + +#include "ximage-loader.h" +#include "rotator.h" +#include "gltrackball.h" + + +typedef struct { + GLXContext *glx_context; + GLuint starlist; + int starcount; + rotator *rot, *rot2; + trackball_state *trackball; + Bool button_down_p; + enum { STARTUP, FLAT, FOLD, + ICO, STEL_IN, AXIS, SPIN, STEL, STEL_OUT, + ICO2, UNFOLD } state; + GLfloat ratio; + GLuint tex1, tex2; + texture_font_data *font_data; + int loading_sw, loading_sh; + + XImage *day, *night, *dusk, *cvt; + XImage **images; /* One image for each frame of time-of-day. */ + int nimages; + + double current_frame; + Bool cache_p; + long delay; + +} planetstruct; + + +static planetstruct *planets = NULL; + + +static double +double_time (void) +{ + struct timeval now; +# ifdef GETTIMEOFDAY_TWO_ARGS + struct timezone tzp; + gettimeofday(&now, &tzp); +# else + gettimeofday(&now); +# endif + + return (now.tv_sec + ((double) now.tv_usec * 0.000001)); +} + + +/* Draw faint latitude and longitude lines into the RGBA XImage. + */ +static void +add_grid_lines (XImage *image) +{ + int i; + int off = 24; + for (i = 0; i < 24; i++) + { + int x = (i + 0.5) * image->width / (double) 24; + int y; + for (y = 0; y < image->height; y++) + { + unsigned long rgba = XGetPixel (image, x, y); + int r = (rgba >> 24) & 0xFF; + int g = (rgba >> 16) & 0xFF; + int b = (rgba >> 8) & 0xFF; + int a = (rgba >> 0) & 0xFF; + int off2 = (((r + g + b) / 3) < 0x7F ? off : -off); + r = MAX (0, MIN (0xFF, r + off2)); + g = MAX (0, MIN (0xFF, g + off2)); + b = MAX (0, MIN (0xFF, b + off2)); + XPutPixel (image, x, y, (r << 24) | (g << 16) | (b << 8) | a); + } + } + + for (i = 1; i < 11; i++) + { + int y = i * image->height / (double) 12; + int x; + for (x = 0; x < image->width; x++) + { + unsigned long rgba = XGetPixel (image, x, y); + int r = (rgba >> 24) & 0xFF; + int g = (rgba >> 16) & 0xFF; + int b = (rgba >> 8) & 0xFF; + int a = (rgba >> 0) & 0xFF; + int off2 = (((r + g + b) / 3) < 0x7F ? off : -off); + r = MAX (0, MIN (0xFF, r + off2)); + g = MAX (0, MIN (0xFF, g + off2)); + b = MAX (0, MIN (0xFF, b + off2)); + XPutPixel (image, x, y, (r << 24) | (g << 16) | (b << 8) | a); + } + } +} + + +static void +adjust_brightness (XImage *image, double amount) +{ + uint32_t *in = (uint32_t *) image->data; + int i; + int end = image->height * image->bytes_per_line / 4; + for (i = 0; i < end; i++) + { + uint32_t p = *in; + /* #### Why is this ABGR instead of RGBA? */ + uint32_t a = (p >> 24) & 0xFF; + uint32_t g = (p >> 16) & 0xFF; + uint32_t b = (p >> 8) & 0xFF; + uint32_t r = (p >> 0) & 0xFF; + r = MAX(0, MIN(0xFF, (long) (r * amount))); + g = MAX(0, MIN(0xFF, (long) (g * amount))); + b = MAX(0, MIN(0xFF, (long) (b * amount))); + p = (a << 24) | (g << 16) | (b << 8) | r; + *in++ = p; + } +} + + +static GLfloat +vector_angle (XYZ a, XYZ b) +{ + double La = sqrt (a.x*a.x + a.y*a.y + a.z*a.z); + double Lb = sqrt (b.x*b.x + b.y*b.y + b.z*b.z); + double cc, angle; + + if (La == 0 || Lb == 0) return 0; + if (a.x == b.x && a.y == b.y && a.z == b.z) return 0; + + /* dot product of two vectors is defined as: + La * Lb * cos(angle between vectors) + and is also defined as: + ax*bx + ay*by + az*bz + so: + La * Lb * cos(angle) = ax*bx + ay*by + az*bz + cos(angle) = (ax*bx + ay*by + az*bz) / (La * Lb) + angle = acos ((ax*bx + ay*by + az*bz) / (La * Lb)); + */ + cc = (a.x*b.x + a.y*b.y + a.z*b.z) / (La * Lb); + if (cc > 1) cc = 1; /* avoid fp rounding error (1.000001 => sqrt error) */ + angle = acos (cc); + + return (angle); +} + + +/* Creates a grayscale image encoding the day/night terminator for a random + axial tilt. + */ +static XImage * +create_daylight_mask (Display *dpy, Visual *v, int w, int h) +{ + XImage *image = XCreateImage (dpy, v, 8, ZPixmap, 0, 0, w, h, 8, 0); + int x, y; + XYZ sun; + double axial_tilt = frand(23.4) / (180/M_PI) * RANDSIGN(); + double dusk = M_PI * 0.035; + sun.x = 0; + sun.y = cos (axial_tilt); + sun.z = sin (axial_tilt); + + image->data = (char *) malloc (image->height * image->bytes_per_line); + + for (y = 0; y < image->height; y++) + { + double lat = -M_PI_2 + (M_PI * (y / (double) image->height)); + double cosL = cos(lat); + double sinL = sin(lat); + for (x = 0; x < image->width; x++) + { + double lon = -M_PI_2 + (M_PI * 2 * (x / (double) image->width)); + XYZ v; + double a; + unsigned long p; + v.x = cos(lon) * cosL; + v.y = sin(lon) * cosL; + v.z = sinL; + a = vector_angle (sun, v); + a -= M_PI_2; + a = (a < -dusk ? 1 : a >= dusk ? 0 : (dusk - a) / (dusk * 2)); + p = 0xFF & (unsigned long) (a * 0xFF); + XPutPixel (image, x, y, p); + } + } + return image; +} + + +static void +load_images (ModeInfo *mi) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + int i; + + if (which_image && !strcmp (which_image, "BUILTIN_FLAT")) + { + which_image = strdup("BUILTIN_FLAT"); + which_image2 = strdup("BUILTIN_FLAT"); + } + else if (which_image && !strcmp (which_image, "BUILTIN_SAT")) + { + which_image = strdup("BUILTIN_DAY"); + which_image2 = strdup("BUILTIN_NIGHT"); + } + + if (!which_image) which_image = strdup(""); + if (!which_image2) which_image2 = strdup(""); + + for (i = 0; i < 2; i++) + { + char *s = (i == 0 ? which_image : which_image2); + XImage *image; + if (!strcmp (s, "BUILTIN_DAY")) + image = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi), + earth_png, sizeof(earth_png)); + else if (!strcmp (s, "BUILTIN_NIGHT")) + image = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi), + earth_night_png,sizeof(earth_night_png)); + else if (!strcmp (s, "BUILTIN") || + !strcmp (s, "BUILTIN_FLAT") || + (i == 0 && !strcmp (s, ""))) + image = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi), + earth_flat_png, sizeof(earth_flat_png)); + else if (!strcmp (s, "NONE")) + image = 0; + else if (*s) + image = file_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi), s); + else + image = 0; + + /* if (image) fprintf (stderr, "%s: %d: loaded %s\n", progname, i, s); */ + + if (i == 0) + gp->day = image; + else + gp->night = image; + } + + if (gp->night && !gp->day) + gp->day = gp->night, gp->night = 0; + + gp->nimages = frames; + gp->current_frame = random() % gp->nimages; + + if (gp->nimages < 1) + gp->nimages = 1; + + if (gp->nimages < 2 && gp->night) + { + XDestroyImage (gp->night); + gp->night = 0; + gp->nimages = 1; + } + + if (! gp->night) + gp->nimages = 1; + + if (do_grid) + { + if (gp->day) add_grid_lines (gp->day); + if (gp->night) add_grid_lines (gp->night); + } + + if (gp->day && gp->night && !gp->dusk) + { + if (gp->day->width != gp->night->width || + gp->day->height != gp->night->height) + { + fprintf (stderr, "%s: day and night images must be the same size" + " (%dx%d vs %dx%d)\n", progname, + gp->day->width, gp->day->height, + gp->night->width, gp->night->height); + exit (1); + } + gp->dusk = create_daylight_mask (MI_DISPLAY (mi), MI_VISUAL (mi), + gp->day->width, gp->day->height); + } + + /* Make the day image brighter, because that's easier than doing it + with GL lights. */ + adjust_brightness (gp->day, 1.4); + + if (!strcmp (which_image, which_image2)) + /* If day and night are the same image, make night way darker. */ + adjust_brightness (gp->night, 0.2); + else if (gp->night) + /* Otherwise make it just a little darker. */ + adjust_brightness (gp->night, 0.7); + + + gp->images = (XImage **) calloc (gp->nimages, sizeof(*gp->images)); + + /* Create 'cvt', a map that projects each pixel from Equirectangular to + Dymaxion. It is 2x the width/height of the source images. We iterate + by half pixel to make sure we hit every pixel in 'out'. It would be + cleaner to iterate over 'out' instead of over 'in' but + dymaxionmap-coords.c only goes forward. This is... not super fast. + */ + { + double W = 5.5; + double H = 3 * sqrt(3)/2; + int x2, y2; + int w = gp->day->width; + int h = gp->day->height; + uint32_t *out; + + gp->cvt = XCreateImage (MI_DISPLAY(mi), MI_VISUAL(mi), 32, ZPixmap, 0, 0, + gp->day->width*2, gp->day->height*2, 32, 0); + gp->cvt->data = (char *) + malloc (gp->cvt->height * gp->cvt->bytes_per_line); + out = (uint32_t *) gp->cvt->data; + + for (y2 = 0; y2 < h*2; y2++) + { + double y = (double) y2/2; + double lat = -90 + (180 * (y / (double) h)); + for (x2 = 0; x2 < w*2; x2++) + { + double x = (double) x2/2; + double lon = -180 + (360 * x / w); + double ox, oy; + dymaxion_convert (lon, lat, &ox, &oy); + ox = w - (w * ox / W); + oy = (h * oy / H); + + *out++ = (((((uint32_t) ox) & 0xFFFF) << 16) | + ((((uint32_t) oy) & 0xFFFF))); + } + } + } + + /* A 128 GB iPhone 6s dies at around 540 frames, ~1 GB of XImages. + A 16 GB iPad Air 2 dies at around 320 frames, ~640 MB. + Caching on mobile doesn't matter much: we can just run at 100% CPU. + + On some systems it would be more efficient to cache the images inside + a texture on the GPU instead of moving it from RAM to GPU every few + frames; but on other systems, we'd just run out of GPU memory instead. */ + { + unsigned long cache_size = (gp->day->width * gp->day->height * 4 * + gp->nimages); +# ifdef HAVE_MOBILE + unsigned long max = 320 * 1024 * 1024L; /* 320 MB */ +# else + unsigned long max = 2 * 1024 * 1024 * 1024L; /* 2 GB */ +# endif + gp->cache_p = (cache_size < max); + } +} + + +static void +cache_current_frame (ModeInfo *mi) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + XImage *blended, *dymaxion; + int i, x, y, w, h, end, xoff; + uint32_t *day, *night, *cvt, *out; + uint8_t *dusk; + + if (gp->images[(int) gp->current_frame]) + return; + + xoff = (gp->dusk + ? gp->dusk->width * ((double) gp->current_frame / gp->nimages) + : 0); + + w = gp->day->width; + h = gp->day->height; + + if (!gp->night) + blended = gp->day; + else + { + /* Blend the foreground and background images through the dusk map. + */ + blended = XCreateImage (MI_DISPLAY(mi), MI_VISUAL(mi), + gp->day->depth, ZPixmap, 0, 0, w, h, 32, 0); + if (!blended) abort(); + blended->data = (char *) malloc (h * blended->bytes_per_line); + if (!blended->data) abort(); + + end = blended->height * blended->bytes_per_line / 4; + day = (uint32_t *) gp->day->data; + night = (uint32_t *) gp->night->data; + dusk = (uint8_t *) gp->dusk->data; + out = (uint32_t *) blended->data; + + for (i = 0; i < end; i++) + { + uint32_t d = *day++; + uint32_t n = *night++; + uint32_t x = i % w; + uint32_t y = i / w; + double r = dusk[y * w + ((x + xoff) % w)] / 256.0; + double r2 = 1-r; +# define ADD(M) (((unsigned long) \ + ((((d >> M) & 0xFF) * r) + \ + (((n >> M) & 0xFF) * r2))) \ + << M) + /* #### Why is this ABGR instead of RGBA? */ + *out++ = (0xFF << 24) | ADD(16) | ADD(8) | ADD(0); +# undef ADD + } + } + + /* Convert blended Equirectangular to Dymaxion through the 'cvt' map. + */ + dymaxion = XCreateImage (MI_DISPLAY(mi), MI_VISUAL(mi), + gp->day->depth, ZPixmap, 0, 0, w, h, 32, 0); + dymaxion->data = (char *) calloc (h, dymaxion->bytes_per_line); + + day = (uint32_t *) blended->data; + out = (uint32_t *) dymaxion->data; + cvt = (uint32_t *) gp->cvt->data; + + for (y = 0; y < h*2; y++) + for (x = 0; x < w*2; x++) + { + unsigned long m = *cvt++; + unsigned long dx = (m >> 16) & 0xFFFF; + unsigned long dy = m & 0xFFFF; + unsigned long p = day[(y>>1) * w + (x>>1)]; + unsigned long p2 = out[dy * w + dx]; + if (p2 & 0xFF000000) + /* RGBA nonzero alpha: initialized. Average with existing, + otherwise the grid lines look terrible. */ + p = (((((p>>24) & 0xFF) + ((p2>>24) & 0xFF)) >> 1) << 24 | + ((((p>>16) & 0xFF) + ((p2>>16) & 0xFF)) >> 1) << 16 | + ((((p>> 8) & 0xFF) + ((p2>> 8) & 0xFF)) >> 1) << 8 | + ((((p>> 0) & 0xFF) + ((p2>> 0) & 0xFF)) >> 1) << 0); + out[dy * w + dx] = p; + } + + /* Fill in the triangles that are not a part of The World with the + color of the ocean to avoid texture-tearing on the folded edges. + */ + out = (uint32_t *) dymaxion->data; + end = dymaxion->height * dymaxion->bytes_per_line / 4; + { + double lat = -48.44, lon = -123.39; /* R'Lyeh */ + int x = (lon + 180) * blended->width / 360.0; + int y = (lat + 90) * blended->height / 180.0; + unsigned long ocean = XGetPixel (gp->day, x, y); + for (i = 0; i < end; i++) + { + uint32_t p = *out; + if (! (p & 0xFF000000)) /* AGBR */ + *out = ocean; + out++; + } + } + + if (blended != gp->day) + XDestroyImage (blended); + + gp->images[(int) gp->current_frame] = dymaxion; + + if (!gp->cache_p) /* Keep only one image around; recompute every time. */ + { + i = ((int) gp->current_frame) - 1; + if (i < 0) i = gp->nimages - 1; + if (gp->images[i]) + { + XDestroyImage (gp->images[i]); + gp->images[i] = 0; + } + } +} + + +static void +setup_texture (ModeInfo * mi) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + XImage *ground; + + glGenTextures (1, &gp->tex1); + glBindTexture (GL_TEXTURE_2D, gp->tex1); + + /* Must be after glBindTexture */ + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + load_images (mi); + + glGenTextures (1, &gp->tex2); + glBindTexture (GL_TEXTURE_2D, gp->tex2); + + /* Must be after glBindTexture */ + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + /* The underground image can go on flat, without the dymaxion transform. */ + ground = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi), + ground_png, sizeof(ground_png)); + clear_gl_error(); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + /* glPixelStorei(GL_UNPACK_ROW_LENGTH, ground->width); */ + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, + ground->width, ground->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, ground->data); + check_gl_error ("ground texture"); + XDestroyImage (ground); +} + + +static void +init_stars (ModeInfo *mi) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + int i, j; + int width = MI_WIDTH(mi); + int height = MI_HEIGHT(mi); + int size = (width > height ? width : height); + int nstars = size * size / 80; + int max_size = 3; + GLfloat inc = 0.5; + int steps = max_size / inc; + GLfloat scale = 1; + + if (MI_WIDTH(mi) > 2560) { /* Retina displays */ + scale *= 2; + nstars /= 2; + } + + gp->starlist = glGenLists(1); + glNewList(gp->starlist, GL_COMPILE); + for (j = 1; j <= steps; j++) + { + glPointSize(inc * j * scale); + glBegin (GL_POINTS); + for (i = 0; i < nstars / steps; i++) + { + GLfloat d = 0.1; + GLfloat r = 0.15 + frand(0.3); + GLfloat g = r + frand(d) - d; + GLfloat b = r + frand(d) - d; + + GLfloat x = frand(1)-0.5; + GLfloat y = frand(1)-0.5; + GLfloat z = ((random() & 1) + ? frand(1)-0.5 + : (BELLRAND(1)-0.5)/12); /* milky way */ + d = sqrt (x*x + y*y + z*z); + x /= d; + y /= d; + z /= d; + glColor3f (r, g, b); + glVertex3f (x, y, z); + gp->starcount++; + } + glEnd (); + } + glEndList (); + + check_gl_error("stars initialization"); +} + + +ENTRYPOINT void +reshape_planet (ModeInfo *mi, int width, int height) +{ + GLfloat h = (GLfloat) height / (GLfloat) width; + + glViewport(0, 0, (GLint) width, (GLint) height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-1.0, 1.0, -h, h, 5.0, 200.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.0, 0.0, -40); + +# ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */ + { + int o = (int) current_device_rotation(); + if (o != 0 && o != 180 && o != -180) + glScalef (h, h, h); + } +# endif + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + + +static void +do_normal2 (ModeInfo *mi, Bool frontp, XYZ a, XYZ b, XYZ c) +{ + XYZ n = (frontp + ? calc_normal (a, b, c) + : calc_normal (b, a, c)); + glNormal3f (n.x, n.y, n.z); + +# if 0 + if (frontp && MI_IS_WIREFRAME(mi)) + { + glBegin (GL_LINES); + glVertex3f ((a.x + b.x + c.x) / 3, + (a.y + b.y + c.y) / 3, + (a.z + b.z + c.z) / 3); + glVertex3f ((a.x + b.x + c.x) / 3 + n.x, + (a.y + b.y + c.y) / 3 + n.y, + (a.z + b.z + c.z) / 3 + n.z); + glEnd(); + } +# endif +} + + +static void +triangle0 (ModeInfo *mi, Bool frontp, GLfloat stel_ratio, int facemask, + XYZ *corners_ret) +{ + /* Render a triangle as six sub-triangles. + Facemask bits 0-5 indicate which sub-triangle to draw. + + A + / \ + / | \ + / | \ + / 0 | 1 \ + E /_ | _\ F + / \_ | _/ \ + / 5 \D/ 2 \ + / / | \ \ + / / 4 | 3 \ \ + / / | \ \ + B ----------------------- C + G + */ + + Bool wire = MI_IS_WIREFRAME(mi); + GLfloat h = sqrt(3) / 2; + GLfloat h2 = sqrt(h*h - (h/2)*(h/2)) - 0.5; + XYZ A, B, C, D, E, F, G; + XYZ tA, tB, tC, tD, tE, tF, tG; + XYZ a, b, c; + XYZ ta, tb, tc; + A.x = 0; A.y = h; A.z = 0; + B.x = -0.5, B.y = 0; B.z = 0; + C.x = 0.5, C.y = 0; C.z = 0; + D.x = 0; D.y = h/3; D.z = 0; + E.x = -h2; E.y = h/2; E.z = 0; + F.x = h2; F.y = h/2; F.z = 0; + G.x = 0; G.y = 0; G.z = 0; + + /* When tweaking object XY to stellate, don't change texture coordinates. */ + tA = A; tB = B; tC = C; tD = D; tE = E; tF = F; tG = G; + + /* Eyeballed this to find the depth of stellation that seems to most + approximate a sphere. + */ + D.z = 0.193 * stel_ratio; + + /* We want to raise E, F and G as well but we can't just shift Z: + we need to keep them on the same vector from the center of the sphere, + which means also changing F and G's X and Y. + */ + E.z = F.z = G.z = 0.132 * stel_ratio; + { + double magic_x = 0.044; + double magic_y = 0.028; + /* G.x stays 0 */ + G.y -= sqrt (magic_x*magic_x + magic_y*magic_y) * stel_ratio; + E.x -= magic_x * stel_ratio; + E.y += magic_y * stel_ratio; + F.x += magic_x * stel_ratio; + F.y += magic_y * stel_ratio; + } + + + if (facemask & 1<<0) + { + a = E; b = D; c = A; + ta = tE; tb = tD; tc = tA; + do_normal2 (mi, frontp, a, b, c); + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z); + glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z); + glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z); + glEnd(); + mi->polygon_count++; + } + if (facemask & 1<<1) + { + a = D; b = F; c = A; + ta = tD; tb = tF; tc = tA; + do_normal2 (mi, frontp, a, b, c); + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z); + glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z); + glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z); + glEnd(); + mi->polygon_count++; + } + if (facemask & 1<<2) + { + a = D; b = C; c = F; + ta = tD; tb = tC; tc = tF; + do_normal2 (mi, frontp, a, b, c); + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z); + glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z); + glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z); + glEnd(); + mi->polygon_count++; + } + if (facemask & 1<<3) + { + a = G; b = C; c = D; + ta = tG; tb = tC; tc = tD; + do_normal2 (mi, frontp, a, b, c); + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z); + glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z); + glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z); + glEnd(); + mi->polygon_count++; + } + if (facemask & 1<<4) + { + a = B; b = G; c = D; + ta = tB; tb = tG; tc = tD; + do_normal2 (mi, frontp, a, b, c); + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z); + glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z); + glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z); + glEnd(); + mi->polygon_count++; + } + if (facemask & 1<<5) + { + a = B; b = D; c = E; + ta = tB; tb = tD; tc = tE; + do_normal2 (mi, frontp, a, b, c); + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z); + glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z); + glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z); + glEnd(); + mi->polygon_count++; + } + if (facemask & 1<<6) + { + a = E; b = D; c = A; + ta = tE; tb = tD; tc = tA; + do_normal2 (mi, frontp, a, b, c); + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + glTexCoord2f (ta.x, ta.y); glVertex3f (a.x, a.y, a.z); + glTexCoord2f (tb.x, tb.y); glVertex3f (b.x, b.y, b.z); + glTexCoord2f (tc.x, tc.y); glVertex3f (c.x, c.y, c.z); + glEnd(); + mi->polygon_count++; + } + + if (corners_ret) + { + corners_ret[0] = A; + corners_ret[1] = B; + corners_ret[2] = C; + corners_ret[3] = D; + corners_ret[4] = E; + corners_ret[5] = F; + corners_ret[6] = G; + } +} + + +/* The segments, numbered arbitrarily from the top left: + ________ _ ________ + \ /\ /\ \ |\ / + \ 0 / \ / \3> | \ 5 / + \ / 1 \ / 2 \| ..|4 \ /-6-.. + ___________\/______\/______\/______\/______\ + | /\ /\ /\ /\ /\ + |7 / \ 9 / \ 11 / \ 13 / \ 15 / \ + | / 8 \ / 10 \ / 12 \ / 14 \ / 16 \ + |/______\/______\/______\/______\/______\ + \ /\ / /\ /\ + \ 17 / \ 18 / / \ 20 / \ + \ / \ / / 19 \ / 21 \ + \/ \/ /______\/______\ + + Each triangle can be connected to at most two other triangles. + We start from the middle, #12, and work our way to the edges. + Its centroid is 0,0. + + (Note that dymaxionmap-coords.c uses a different numbering system.) + */ +static void +triangle (ModeInfo *mi, int which, Bool frontp, + GLfloat fold_ratio, GLfloat stel_ratio) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + const GLfloat fg[] = { 1, 1, 1, 1 }; + const GLfloat bg[] = { 0.3, 0.3, 0.3, 1 }; + int a = -1, b = -1; + GLfloat max = acos (sqrt(5)/3); + GLfloat rot = -max * fold_ratio / (M_PI/180); + Bool wire = MI_IS_WIREFRAME(mi); + XYZ corners[7]; + + glColor3fv (fg); + if (!wire) + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fg); + + switch (which) { + case 3: /* One third of the face. */ + triangle0 (mi, frontp, stel_ratio, 1<<3 | 1<<4, corners); + break; + case 4: /* Two thirds of the face: convex. */ + triangle0 (mi, frontp, stel_ratio, 1<<1 | 1<<2 | 1<<3 | 1<<4, corners); + break; + case 6: /* One half of the face. */ + triangle0 (mi, frontp, stel_ratio, 1<<1 | 1<<2 | 1<<3, corners); + break; + case 7: /* One half of the face. */ + triangle0 (mi, frontp, stel_ratio, 1<<2 | 1<<3 | 1<<4, corners); + break; + default: /* Full face. */ + triangle0 (mi, frontp, stel_ratio, 0x3F, corners); + break; + } + + if (wire) + { + char tag[20]; + glColor3fv (bg); + sprintf (tag, "%d", which); + glPushMatrix(); + glTranslatef (-0.1, 0.2, 0); + glScalef (0.005, 0.005, 0.005); + print_texture_string (gp->font_data, tag); + glPopMatrix(); + mi->polygon_count++; + } + + + /* The connection hierarchy of the faces starting at the middle, #12. */ + switch (which) { + case 0: break; + case 1: a = 0; b = -1; break; + case 2: a = -1; b = 3; break; + case 3: break; + case 4: a = -1; b = 5; break; + case 5: a = -1; b = 6; break; + case 7: break; + case 6: break; + case 8: a = 17; b = 7; break; + case 9: a = 8; b = -1; break; + case 10: a = 18; b = 9; break; + case 11: a = 10; b = 1; break; + case 12: a = 11; b = 13; break; + case 13: a = 2; b = 14; break; + case 14: a = 15; b = 20; break; + case 15: a = 4; b = 16; break; + case 16: break; + case 17: break; + case 18: break; + case 19: break; + case 20: a = 21; b = 19; break; + case 21: break; + default: abort(); break; + } + + if (a != -1) + { + glPushMatrix(); + glTranslatef (-0.5, 0, 0); /* Move model matrix to upper left */ + glRotatef (60, 0, 0, 1); + glTranslatef ( 0.5, 0, 0); + + glMatrixMode(GL_TEXTURE); + /* glPushMatrix(); */ + glTranslatef (-0.5, 0, 0); /* Move texture matrix the same way */ + glRotatef (60, 0, 0, 1); + glTranslatef ( 0.5, 0, 0); + + glMatrixMode(GL_MODELVIEW); + + glRotatef (rot, 1, 0, 0); + triangle (mi, a, frontp, fold_ratio, stel_ratio); + + /* This should just be a PopMatrix on the TEXTURE stack, but + fucking iOS has GL_MAX_TEXTURE_STACK_DEPTH == 4! WTF! + So we have to undo our rotations and translations manually. + */ + glMatrixMode(GL_TEXTURE); + /* glPopMatrix(); */ + glTranslatef (-0.5, 0, 0); + glRotatef (-60, 0, 0, 1); + glTranslatef (0.5, 0, 0); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + + if (b != -1) + { + glPushMatrix(); + glTranslatef (0.5, 0, 0); /* Move model matrix to upper right */ + glRotatef (-60, 0, 0, 1); + glTranslatef (-0.5, 0, 0); + + glMatrixMode(GL_TEXTURE); + /* glPushMatrix(); */ + glTranslatef (0.5, 0, 0); /* Move texture matrix the same way */ + glRotatef (-60, 0, 0, 1); + glTranslatef (-0.5, 0, 0); + + glMatrixMode(GL_MODELVIEW); + + glRotatef (rot, 1, 0, 0); + triangle (mi, b, frontp, fold_ratio, stel_ratio); + + /* See above. Grr. */ + glMatrixMode(GL_TEXTURE); + /* glPopMatrix(); */ + glTranslatef (0.5, 0, 0); + glRotatef (60, 0, 0, 1); + glTranslatef (-0.5, 0, 0); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + + + /* Draw a border around the edge of the world. + */ + if (!wire && frontp && stel_ratio == 0 && fold_ratio < 0.95) + { + int edges = 0; + GLfloat c[] = { 0, 0.2, 0.5, 1 }; + c[3] = 1-fold_ratio; + + switch (which) + { + case 0: edges = 1<<0 | 1<<2; break; + case 1: edges = 1<<2; break; + case 2: edges = 1<<0; break; + case 3: edges = 1<<3 | 1<<4; break; + case 4: edges = 1<<3 | 1<<5; break; + case 5: edges = 1<<0 | 1<<6; break; + case 6: edges = 1<<2 | 1<<7; break; + case 16: edges = 1<<0 | 1<<2; break; + case 21: edges = 1<<0 | 1<<2; break; + case 19: edges = 1<<0 | 1<<2; break; + case 12: edges = 1<<1; break; + case 18: edges = 1<<0 | 1<<2; break; + case 17: edges = 1<<0 | 1<<2; break; + case 7: edges = 1<<8 | 1<<9; break; + case 9: edges = 1<<2; break; + default: break; + } + + glDisable (GL_TEXTURE_2D); + glDisable (GL_LIGHTING); + glLineWidth (2); + glColor4fv (c); + glBegin (GL_LINES); + if (edges & 1<<0) + { + glVertex3f (corners[0].x, corners[0].y, corners[0].z); + glVertex3f (corners[1].x, corners[1].y, corners[1].z); + } + if (edges & 1<<1) + { + glVertex3f (corners[1].x, corners[1].y, corners[1].z); + glVertex3f (corners[2].x, corners[2].y, corners[2].z); + } + if (edges & 1<<2) + { + glVertex3f (corners[2].x, corners[2].y, corners[2].z); + glVertex3f (corners[0].x, corners[0].y, corners[0].z); + } + if (edges & 1<<3) + { + glVertex3f (corners[1].x, corners[1].y, corners[1].z); + glVertex3f (corners[3].x, corners[3].y, corners[3].z); + } + if (edges & 1<<4) + { + glVertex3f (corners[3].x, corners[3].y, corners[3].z); + glVertex3f (corners[2].x, corners[2].y, corners[2].z); + } + if (edges & 1<<5) + { + glVertex3f (corners[3].x, corners[3].y, corners[3].z); + glVertex3f (corners[0].x, corners[0].y, corners[0].z); + } + if (edges & 1<<6) + { + glVertex3f (corners[0].x, corners[0].y, corners[0].z); + glVertex3f (corners[5].x, corners[5].y, corners[5].z); + } + if (edges & 1<<7) + { + glVertex3f (corners[0].x, corners[0].y, corners[0].z); + glVertex3f (corners[6].x, corners[6].y, corners[6].z); + } + if (edges & 1<<8) + { + glVertex3f (corners[1].x, corners[1].y, corners[1].z); + glVertex3f (corners[5].x, corners[5].y, corners[5].z); + } + if (edges & 1<<9) + { + glVertex3f (corners[5].x, corners[5].y, corners[5].z); + glVertex3f (corners[2].x, corners[2].y, corners[2].z); + } + glEnd(); + glEnable (GL_TEXTURE_2D); + glEnable (GL_LIGHTING); + } +} + + +static void +draw_triangles (ModeInfo *mi, GLfloat fold_ratio, GLfloat stel_ratio) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + Bool wire = MI_IS_WIREFRAME(mi); + GLfloat h = sqrt(3) / 2; + GLfloat c = h / 3; + + glTranslatef (0, -h/3, 0); /* Center on face 12 */ + + /* When closed, center on midpoint of icosahedron. Eyeballed this. */ + glTranslatef (0, 0, fold_ratio * 0.754); + + glFrontFace (GL_CCW); + + /* Adjust the texture matrix so that it has the same coordinate space + as the model. */ + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + { + GLfloat texw = 5.5; + GLfloat texh = 3 * h; + GLfloat midx = 2.5; + GLfloat midy = 3 * c; + glScalef (1/texw, -1/texh, 1); + glTranslatef (midx, midy, 0); + } + glMatrixMode(GL_MODELVIEW); + + + + /* Front faces */ + + if (wire) + glDisable (GL_TEXTURE_2D); + else if (do_texture) + { + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, gp->tex1); + } + else + glDisable (GL_TEXTURE_2D); + + triangle (mi, 12, True, fold_ratio, stel_ratio); + + /* Back faces */ + + if (wire) + glDisable (GL_TEXTURE_2D); + else if (do_texture) + { + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, gp->tex2); + } + else + glDisable (GL_TEXTURE_2D); + + glFrontFace (GL_CW); + + triangle (mi, 12, False, fold_ratio, 0); + + glMatrixMode(GL_TEXTURE); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); +} + + +static void +align_axis (ModeInfo *mi, int undo) +{ + /* Rotate so that an axis is lined up with the north and south poles + on the map, which are not in the center of their faces, or any + other easily computable spot. */ + + GLfloat r1 = 20.5; + GLfloat r2 = 28.5; + + if (undo) + { + glRotatef (-r2, 0, 1, 0); + glRotatef ( r2, 1, 0, 0); + glRotatef (-r1, 1, 0, 0); + } + else + { + glRotatef (r1, 1, 0, 0); + glRotatef (-r2, 1, 0, 0); + glRotatef ( r2, 0, 1, 0); + } +} + + +static void +draw_axis (ModeInfo *mi) +{ + GLfloat s; + glDisable (GL_TEXTURE_2D); + glDisable (GL_LIGHTING); + glPushMatrix(); + + align_axis (mi, 0); + glTranslatef (0.34, 0.39, -0.61); + + s = 0.96; + glScalef (s, s, s); /* tighten up the enclosing sphere */ + + glLineWidth (1); + glColor3f (0.5, 0.5, 0); + + glRotatef (90, 1, 0, 0); /* unit_sphere is off by 90 */ + glRotatef (9.5, 0, 1, 0); /* line up the time zones */ + glFrontFace (GL_CCW); + unit_sphere (12, 24, True); + glBegin(GL_LINES); + glVertex3f(0, -2, 0); + glVertex3f(0, 2, 0); + glEnd(); + + glPopMatrix(); +} + + + + +ENTRYPOINT Bool +planet_handle_event (ModeInfo *mi, XEvent *event) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + + if (gltrackball_event_handler (event, gp->trackball, + MI_WIDTH (mi), MI_HEIGHT (mi), + &gp->button_down_p)) + return True; + else if (event->xany.type == KeyPress) + { + KeySym keysym; + char c = 0; + XLookupString (&event->xkey, &c, 1, &keysym, 0); + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + { + int i; + double cf = gp->current_frame; + + /* Switch between the satellite and flat map, preserving position. */ + if (gp->day) XDestroyImage (gp->day); + if (gp->night) XDestroyImage (gp->night); + if (gp->cvt) XDestroyImage (gp->cvt); + gp->day = 0; + gp->night = 0; + gp->cvt = 0; + + for (i = 0; i < gp->nimages; i++) + if (gp->images[i]) XDestroyImage (gp->images[i]); + free (gp->images); + gp->images = 0; + + which_image = strdup (!strcmp (which_image, "BUILTIN_DAY") + ? "BUILTIN_FLAT" : "BUILTIN_DAY"); + which_image2 = strdup (!strcmp (which_image2, "BUILTIN_NIGHT") + ? "BUILTIN_FLAT" : "BUILTIN_NIGHT"); + load_images (mi); + gp->current_frame = cf; +# if 0 + switch (gp->state) { + case FLAT: case ICO: case STEL: case AXIS: case ICO2: + gp->ratio = 1; + break; + default: + break; + } +# endif + return True; + } + } + + return False; +} + + +ENTRYPOINT void +init_planet (ModeInfo * mi) +{ + planetstruct *gp; + int screen = MI_SCREEN(mi); + Bool wire = MI_IS_WIREFRAME(mi); + + MI_INIT (mi, planets); + gp = &planets[screen]; + + if ((gp->glx_context = init_GL(mi)) != NULL) { + reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + } + + gp->state = STARTUP; + gp->ratio = 0; + gp->font_data = load_texture_font (mi->dpy, "labelFont"); + gp->delay = MI_DELAY(mi); + + { + double spin_speed = 0.1; + double wander_speed = 0.002; + gp->rot = make_rotator (do_roll ? spin_speed : 0, + do_roll ? spin_speed : 0, + 0, 1, + do_wander ? wander_speed : 0, + False); + gp->rot2 = make_rotator (0, 0, 0, 0, wander_speed, False); + gp->trackball = gltrackball_init (True); + } + + if (wire) + do_texture = False; + + if (do_texture) + setup_texture (mi); + + if (do_stars) + init_stars (mi); + + glEnable (GL_DEPTH_TEST); + glEnable (GL_NORMALIZE); + glEnable (GL_CULL_FACE); + + if (!wire) + { + GLfloat pos[4] = {1, 1, 1, 0}; + GLfloat amb[4] = {0, 0, 0, 1}; + GLfloat dif[4] = {1, 1, 1, 1}; + GLfloat spc[4] = {0, 1, 1, 1}; + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT0, GL_POSITION, pos); + glLightfv(GL_LIGHT0, GL_AMBIENT, amb); + glLightfv(GL_LIGHT0, GL_DIFFUSE, dif); + glLightfv(GL_LIGHT0, GL_SPECULAR, spc); + } +} + + +static GLfloat +ease_fn (GLfloat r) +{ + return cos ((r/2 + 1) * M_PI) + 1; /* Smooth curve up, end at slope 1. */ +} + + +static GLfloat +ease_ratio (GLfloat r) +{ + GLfloat ease = 0.35; + if (r <= 0) return 0; + else if (r >= 1) return 1; + else if (r <= ease) return ease * ease_fn (r / ease); + else if (r > 1-ease) return 1 - ease * ease_fn ((1 - r) / ease); + else return r; +} + + +ENTRYPOINT void +draw_planet (ModeInfo * mi) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + long delay = gp->delay; + double x, y, z; + + if (!gp->glx_context) + return; + + glDrawBuffer(GL_BACK); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glXMakeCurrent (dpy, window, *(gp->glx_context)); + + mi->polygon_count = 0; + + if (! gp->button_down_p) + switch (gp->state) { + case STARTUP: gp->ratio += speed * 0.01; break; + case FLAT: gp->ratio += speed * 0.005 * + /* Stay flat longer if animating day and night. */ + (gp->nimages <= 1 ? 1 : 0.3); + break; + case FOLD: gp->ratio += speed * 0.01; break; + case ICO: gp->ratio += speed * 0.01; break; + case STEL_IN: gp->ratio += speed * 0.05; break; + case STEL: gp->ratio += speed * 0.01; break; + case STEL_OUT: gp->ratio += speed * 0.07; break; + case ICO2: gp->ratio += speed * 0.07; break; + case AXIS: gp->ratio += speed * 0.02; break; + case SPIN: gp->ratio += speed * 0.005; break; + case UNFOLD: gp->ratio += speed * 0.01; break; + default: abort(); + } + + if (gp->ratio > 1.0) + { + gp->ratio = 0; + switch (gp->state) { + case STARTUP: gp->state = FLAT; break; + case FLAT: gp->state = FOLD; break; + case FOLD: gp->state = ICO; break; + case ICO: gp->state = STEL_IN; break; + case STEL_IN: gp->state = STEL; break; + case STEL: + { + int i = (random() << 9) % 7; + gp->state = (i < 3 ? STEL_OUT : + i < 6 ? SPIN : AXIS); + } + break; + case AXIS: gp->state = STEL_OUT; break; + case SPIN: gp->state = STEL_OUT; break; + case STEL_OUT: gp->state = ICO2; break; + case ICO2: gp->state = UNFOLD; break; + case UNFOLD: gp->state = FLAT; break; + default: abort(); + } + } + + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POINT_SMOOTH); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + glPushMatrix(); + + gltrackball_rotate (gp->trackball); + glRotatef (current_device_rotation(), 0, 0, 1); + +# ifdef HAVE_MOBILE /* Fill more of the screen. */ + { + int size = MI_WIDTH(mi) < MI_HEIGHT(mi) + ? MI_WIDTH(mi) : MI_HEIGHT(mi); + GLfloat s = (size > 768 ? 1.4 : /* iPad */ + 2); /* iPhone */ + glScalef (s, s, s); + if (MI_WIDTH(mi) < MI_HEIGHT(mi)) + glRotatef (90, 0, 0, 1); + } +# endif + + if (gp->state != STARTUP) + { + get_position (gp->rot, &x, &y, &z, !gp->button_down_p); + x = (x - 0.5) * 3; + y = (y - 0.5) * 3; + z = 0; + glTranslatef(x, y, z); + } + + if (do_roll && gp->state != STARTUP) + { + double max = 65; + get_position (gp->rot2, &x, &y, 0, !gp->button_down_p); + glRotatef (max/2 - x*max, 1, 0, 0); + glRotatef (max/2 - y*max, 0, 1, 0); + } + + if (do_stars) + { + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glPushMatrix(); + glScalef (60, 60, 60); + glRotatef (90, 1, 0, 0); + glRotatef (35, 1, 0, 0); + glCallList (gp->starlist); + mi->polygon_count += gp->starcount; + glPopMatrix(); + glClear(GL_DEPTH_BUFFER_BIT); + } + + if (! wire) + { + glEnable (GL_LIGHTING); + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + if (do_texture) + glEnable(GL_TEXTURE_2D); + + if (do_texture /* && !gp->button_down_p */) + { + int i; + int prev = gp->current_frame; + + /* By default, advance terminator by about an hour every 5 seconds. */ + gp->current_frame += 0.1 * speed * (gp->nimages / 360.0); + while (gp->current_frame >= gp->nimages) + gp->current_frame -= gp->nimages; + i = gp->current_frame; + + /* Load the current image into the texture. + */ + if (i != prev || !gp->images[i]) + { + double start = double_time(); + cache_current_frame (mi); + + glBindTexture (GL_TEXTURE_2D, gp->tex1); + + /* Must be after glBindTexture */ + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, + gp->images[i]->width, + gp->images[i]->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, + gp->images[i]->data); + check_gl_error ("texture"); + + /* If caching the image took a bunch of time, deduct that from + our per-frame delay to keep the timing a little smoother. */ + delay -= 1000000 * (double_time() - start); + if (delay < 0) delay = 0; + } + } + + glTranslatef (-0.5, -0.4, 0); + glScalef (2.6, 2.6, 2.6); + + { + GLfloat fold_ratio = 0; + GLfloat stel_ratio = 0; + switch (gp->state) { + case FOLD: fold_ratio = gp->ratio; break; + case UNFOLD: fold_ratio = 1 - gp->ratio; break; + case ICO: case ICO2: fold_ratio = 1; break; + case STEL: case AXIS: case SPIN: fold_ratio = 1; stel_ratio = 1; break; + case STEL_IN: fold_ratio = 1; stel_ratio = gp->ratio; break; + case STEL_OUT: fold_ratio = 1; stel_ratio = 1 - gp->ratio; break; + case STARTUP: /* Tilt in from flat */ + glRotatef (-90 * ease_ratio (1 - gp->ratio), 1, 0, 0); + break; + + default: break; + } + +# ifdef HAVE_MOBILE /* Enlarge the icosahedron a bit to make it more visible */ + { + GLfloat s = 1 + 1.3 * ease_ratio (fold_ratio); + glScalef (s, s, s); + } +# endif + + if (gp->state == SPIN) + { + align_axis (mi, 0); + glRotatef (ease_ratio (gp->ratio) * 360 * 3, 0, 0, 1); + align_axis (mi, 1); + } + + draw_triangles (mi, ease_ratio (fold_ratio), ease_ratio (stel_ratio)); + + if (gp->state == AXIS) + draw_axis(mi); + } + + glPopMatrix(); + + if (mi->fps_p) do_fps (mi); + glFinish(); + glXSwapBuffers(dpy, window); + + MI_DELAY(mi) = delay; +} + + +ENTRYPOINT void +free_planet (ModeInfo * mi) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + int i; + + if (gp->day) XDestroyImage (gp->day); + if (gp->night) XDestroyImage (gp->night); + if (gp->dusk) XDestroyImage (gp->dusk); + if (gp->cvt) XDestroyImage (gp->cvt); + + for (i = 0; i < gp->nimages; i++) + if (gp->images[i]) XDestroyImage (gp->images[i]); + free (gp->images); + + if (gp->glx_context) { + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(gp->glx_context)); + + if (glIsList(gp->starlist)) + glDeleteLists(gp->starlist, 1); + } +} + + +XSCREENSAVER_MODULE_2 ("DymaxionMap", dymaxionmap, planet) + +#endif -- cgit v1.2.3-55-g7522