X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Fdymaxionmap.c;fp=hacks%2Fglx%2Fdymaxionmap.c;h=f82a98dd6231351bfa4e5f88ed599ac6d54ed18b;hp=0000000000000000000000000000000000000000;hb=aa75c7476aeaa84cf3abc192b376a8b03c325213;hpb=88cfe534a698a0562e81345957a50714af1453bc diff --git a/hacks/glx/dymaxionmap.c b/hacks/glx/dymaxionmap.c new file mode 100644 index 00000000..f82a98dd --- /dev/null +++ b/hacks/glx/dymaxionmap.c @@ -0,0 +1,1028 @@ +/* dymaxionmap --- Buckminster Fuller's unwrapped icosahedral globe. + * Copyright (c) 2016 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 refresh_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" + +#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_SPEED "1.0" +#define DEF_IMAGE "BUILTIN" + +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#undef BELLRAND +#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) + +static int do_roll; +static int do_wander; +static int do_texture; +static int do_stars; +static GLfloat speed; +static char *which_image; + +static XrmOptionDescRec opts[] = { + {"-speed", ".dymaxionmap.speed", XrmoptionSepArg, 0 }, + {"-roll", ".dymaxionmap.roll", XrmoptionNoArg, "true" }, + {"+roll", ".dymaxionmap.roll", XrmoptionNoArg, "false" }, + {"-wander", ".dymaxionmap.wander", XrmoptionNoArg, "true" }, + {"+wander", ".dymaxionmap.wander", XrmoptionNoArg, "false" }, + {"-texture", ".dymaxionmap.texture", XrmoptionNoArg, "true" }, + {"+texture", ".dymaxionmap.texture", XrmoptionNoArg, "false" }, + {"-stars", ".dymaxionmap.stars", XrmoptionNoArg, "true" }, + {"+stars", ".dymaxionmap.stars", XrmoptionNoArg, "false" }, + {"-image", ".dymaxionmap.image", 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}, + {&which_image, "image", "Image", DEF_IMAGE, t_String}, +}; + +ENTRYPOINT ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL}; + +#ifdef USE_MODULES +ModStruct planet_description = +{"planet", "init_planet", "draw_planet", "release_planet", + "draw_planet", "init_planet", NULL, &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/dymaxionmap.xpm" +#include "../images/ground.xpm" + +#include "xpm-ximage.h" +#include "rotator.h" +#include "gltrackball.h" + + +typedef struct { + GLXContext *glx_context; + GLuint starlist; + int starcount; + rotator *rot; + 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; +} planetstruct; + + +static planetstruct *planets = NULL; + + +/* Set up and enable texturing on our object */ +static void +setup_xpm_texture (ModeInfo *mi, char **xpm_data) +{ + XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi), + MI_COLORMAP (mi), xpm_data); + char buf[1024]; + clear_gl_error(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + image->width, image->height, 0, + GL_RGBA, + /* GL_UNSIGNED_BYTE, */ + GL_UNSIGNED_INT_8_8_8_8_REV, + image->data); + sprintf (buf, "builtin texture (%dx%d)", image->width, image->height); + check_gl_error(buf); + + /* setup parameters for texturing */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + 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); +} + + +static void +setup_file_texture (ModeInfo *mi, char *filename) +{ + Display *dpy = mi->dpy; + Visual *visual = mi->xgwa.visual; + char buf[1024]; + + Colormap cmap = mi->xgwa.colormap; + XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename); + clear_gl_error(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + image->width, image->height, 0, + GL_RGBA, + /* GL_UNSIGNED_BYTE, */ + GL_UNSIGNED_INT_8_8_8_8_REV, + image->data); + sprintf (buf, "texture: %.100s (%dx%d)", + filename, image->width, image->height); + check_gl_error(buf); + + /* setup parameters for texturing */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width); + + 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); +} + + +static void +setup_texture(ModeInfo * mi) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + + if (!which_image || + !*which_image || + !strcmp(which_image, "BUILTIN")) + { + glGenTextures (1, &gp->tex1); + glBindTexture (GL_TEXTURE_2D, gp->tex1); + setup_xpm_texture (mi, dymaxionmap_xpm); + } + else + { + glGenTextures (1, &gp->tex1); + glBindTexture (GL_TEXTURE_2D, gp->tex1); + setup_file_texture (mi, which_image); + } + + glGenTextures (1, &gp->tex2); + glBindTexture (GL_TEXTURE_2D, gp->tex2); + setup_xpm_texture (mi, ground); + + check_gl_error("texture initialization"); + + /* Need to flip the texture top for bottom for some reason. */ + glMatrixMode (GL_TEXTURE); + glScalef (1, -1, 1); + glMatrixMode (GL_MODELVIEW); +} + + +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; + + gp->starlist = glGenLists(1); + glNewList(gp->starlist, GL_COMPILE); + for (j = 1; j <= steps; j++) + { + glPointSize(inc * j); + 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, 100.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 (Bool frontp, XYZ a, XYZ b, XYZ c) +{ + if (frontp) + do_normal (a.x, a.y, a.z, + b.x, b.y, b.z, + c.x, c.y, c.z); + else + do_normal (b.x, b.y, b.z, + a.x, a.y, a.z, + c.x, c.y, c.z); +} + + +static void +triangle0 (ModeInfo *mi, Bool frontp, GLfloat stel_ratio, int bitmask) +{ + /* Render a triangle as six sub-triangles: + + 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 (bitmask & 1<<0) + { + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + a = E; b = D; c = A; + ta = tE; tb = tD; tc = tA; + do_normal2 (frontp, a, b, c); + 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 (bitmask & 1<<1) + { + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + a = D; b = F; c = A; + ta = tD; tb = tF; tc = tA; + do_normal2 (frontp, a, b, c); + 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 (bitmask & 1<<2) + { + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + a = D; b = C; c = F; + ta = tD; tb = tC; tc = tF; + do_normal2 (frontp, a, b, c); + 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 (bitmask & 1<<3) + { + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + a = G; b = C; c = D; + ta = tG; tb = tC; tc = tD; + do_normal2 (frontp, a, b, c); + 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 (bitmask & 1<<4) + { + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + a = B; b = G; c = D; + ta = tB; tb = tG; tc = tD; + do_normal2 (frontp, a, b, c); + 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 (bitmask & 1<<5) + { + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + a = B; b = D; c = E; + ta = tB; tb = tD; tc = tE; + do_normal2 (frontp, a, b, c); + 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 (bitmask & 1<<6) + { + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES); + a = E; b = D; c = A; + ta = tE; tb = tD; tc = tA; + do_normal2 (frontp, a, b, c); + 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++; + } +} + + +/* 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. + */ +static void +triangle (ModeInfo *mi, int which, Bool frontp, + GLfloat fold_ratio, GLfloat stel_ratio) +{ + planetstruct *gp = &planets[MI_SCREEN(mi)]; + const GLfloat fg[3] = { 1, 1, 1 }; + const GLfloat bg[3] = { 0.3, 0.3, 0.3 }; + 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); + + if (wire) + glColor3fv (fg); + + switch (which) { + case 3: /* One third of the face. */ + triangle0 (mi, frontp, stel_ratio, 1<<3 | 1<<4); + break; + case 4: /* Two thirds of the face: convex. */ + triangle0 (mi, frontp, stel_ratio, 1<<1 | 1<<2 | 1<<3 | 1<<4); + break; + case 6: /* One half of the face. */ + triangle0 (mi, frontp, stel_ratio, 1<<1 | 1<<2 | 1<<3); + break; + case 7: /* One half of the face. */ + triangle0 (mi, frontp, stel_ratio, 1<<2 | 1<<3 | 1<<4); + break; + default: /* Full face. */ + triangle0 (mi, frontp, stel_ratio, 0x3F); + 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(); + } +} + + +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 */ + + 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') + { + switch (gp->state) { + case FLAT: case ICO: case STEL: case AXIS: case ICO2: + gp->ratio = 1; + break; + default: + break; + } + return True; + } + } + + return False; +} + + +ENTRYPOINT void +init_planet (ModeInfo * mi) +{ + planetstruct *gp; + int screen = MI_SCREEN(mi); + Bool wire = MI_IS_WIREFRAME(mi); + + if (planets == NULL) { + if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi), + sizeof (planetstruct))) == NULL) + return; + } + 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"); + + { + 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->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] = {0.4, 0.2, 0.4, 0.0}; + GLfloat amb[4] = {0.4, 0.4, 0.4, 1.0}; + GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0}; + GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0}; + + 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); + 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; 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); + + 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) + { + get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p); + glRotatef (x * 360, 1.0, 0.0, 0.0); + glRotatef (y * 360, 0.0, 1.0, 0.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); + + if (do_texture) + glEnable(GL_TEXTURE_2D); + + 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); +} + + +ENTRYPOINT void +release_planet (ModeInfo * mi) +{ + if (planets != NULL) { + int screen; + + for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) { + planetstruct *gp = &planets[screen]; + + if (gp->glx_context) { + /* Display lists MUST be freed while their glXContext is current. */ + /* but this gets a BadMatch error. -jwz */ + /*glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(gp->glx_context));*/ + + if (glIsList(gp->starlist)) + glDeleteLists(gp->starlist, 1); + } + } + (void) free((void *) planets); + planets = NULL; + } + FreeAllGL(mi); +} + + +XSCREENSAVER_MODULE_2 ("DymaxionMap", dymaxionmap, planet) + +#endif