diff options
Diffstat (limited to 'hacks/glx/etruscanvenus.c')
-rw-r--r-- | hacks/glx/etruscanvenus.c | 1825 |
1 files changed, 1825 insertions, 0 deletions
diff --git a/hacks/glx/etruscanvenus.c b/hacks/glx/etruscanvenus.c new file mode 100644 index 0000000..71af80a --- /dev/null +++ b/hacks/glx/etruscanvenus.c @@ -0,0 +1,1825 @@ +/* etruscanvenus --- Shows a 3d immersion of a Klein bottle that + rotates in 3d or on which you can walk and that can deform smoothly + between the Etruscan Venus surface, the Roman surface, the Boy + surface surface, and the Ida surface. */ + +#if 0 +static const char sccsid[] = "@(#)etruscanvenus.c 1.1 05/01/20 xlockmore"; +#endif + +/* Copyright (c) 2019-2020 Carsten Steger <carsten@mirsanmir.org>. */ + +/* + * 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. + * + * REVISION HISTORY: + * C. Steger - 05/01/20: Initial version + */ + +/* + * This program shows a 3d immersion of a Klein bottle that smoothly + * deforms between the Etruscan Venus surface, the Roman surface, the + * Boy surface, and the Ida surface. You can walk on the Klein bottle + * or turn it in 3d. Topologically, all surfaces are Klein bottles, + * even the Roman and Boy surfaces, which are doubly covered and + * therefore appear to be an immersed real projective plane. The + * smooth deformation between these surfaces was constructed by George + * K. Francis. + * + * The Klein bottle is a non-orientable surface. To make this + * apparent, the two-sided color mode can be used. Alternatively, + * orientation markers (curling arrows) can be drawn as a texture map + * on the surface of the Klein bottle. While walking on the Klein + * bottle, you will notice that the orientation of the curling arrows + * changes (which it must because the Klein bottle is non-orientable). + * Since all the surfaces except the Ida surface have points where the + * surface normal is not well defined for some points, walking is only + * performed on the Ida surface. + * + * As mentioned above, the Roman and Boy surfaces are doubly covered + * and therefore appear to be an immersed real projective plane. + * Since some of the parameter names are based on this interpretation + * of the surface, the geometry of the real projective plane will be + * briefly disussed. The real projective plane is a model for the + * projective geometry in 2d space. One point can be singled out as + * the origin. A line can be singled out as the line at infinity, + * i.e., a line that lies at an infinite distance to the origin. The + * line at infinity is topologically a circle. Points on the line at + * infinity are also used to model directions in projective geometry. + * Direction and distance bands refer to this interpretation of the + * surface. If direction bands are used, the bands extend from the + * origin of the projective plane in different directions to the line + * at infinity and back to the origin. If distance bands are used, + * the bands lie at constant distances to the origin. The same + * interpretation is used for distance and direction colors. Although + * there is no conceptually equivalent geometric interpretation for + * the two Klein bottle surfaces (the Etruscan Venus and Ida + * surfaces), the smooth deformation between the surfaces results in a + * natural extension of these concepts to the Klein bottle surfaces. + * + * The immersed surfaces can be projected to the screen either + * perspectively or orthographically. When using the walking mode, + * perspective projection to the screen will be used. + * + * There are three display modes for the Klein bottle: mesh + * (wireframe), solid, or transparent. Furthermore, the appearance of + * the surface can be as a solid object or as a set of see-through + * bands. The bands can be distance bands or direction bands, as + * explained above. + * + * The colors with with the Klein bottle is drawn can be set to + * one-sided, two-sided, distance, or direction. In one-sided mode, + * the surface is drawn with the same color on both sides of the + * underlying triangles. In two-sided mode, the surface is drawn with + * red on one side of the underlying triangles and green on the other + * side. Since the surface actually only has one side, the color + * jumps from red to green along a line on the surface. This mode + * enables you to see that the surface is non-orientable. In distance + * mode, the surface is displayed with fully saturated colors that + * depend on the distance of the points on the projective plane to the + * origin, as described above. If the surface is displayed as + * distance bands, each band will be displayed with a different color. + * In direction mode, the surface is displayed with fully saturated + * colors that depend on the angle of the points on the projective + * plane with respect to the origin (see above for an explanation). + * If the surface is displayed as direction bands, each band will be + * displayed with a different color. The colors used to color the + * surface can either be static or can be changed dynamically. + * + * The rotation speed for each of the three coordinate axes around + * which the Klein bottle rotates can be chosen. + * + * Furthermore, in the walking mode the walking direction in the 2d + * base square of the surface and the walking speed can be chosen. + * The walking direction is measured as an angle in degrees in the 2d + * square that forms the coordinate system of the surface. A value of + * 0 or 180 means that the walk is along a circle at a randomly chosen + * distance from the origin (parallel to a distance band). A value of + * 90 or 270 means that the walk is directly along a direction band. + * Any other value results in a curved path along the surface. As + * noted above, walking is performed only on the Ida surface. + * + * By default, the immersion of the Klein bottle smoothly deforms + * between the Etruscan Venus surface, the Roman surface, the Boy + * surface, and the Ida surface. It is possible to choose the speed + * of the deformation. Furthermore, it is possible to switch the + * deformation off. It is also possible to determine the initial + * deformation of the immersion. This is mostly useful if the + * deformation is switched off, in which case it will determine the + * appearance of the surface. A value of 0 corresponds to the + * Etruscan Venus surface, a value of 1000 to the Roman surface, a + * value of 2000 to the Boy surface, and a value of 3000 to the Ida + * surface. + * + * This program is inspired by George K. Francis's book "A Topological + * Picturebook", Springer, 1987, by George K. Francis's paper "The + * Etruscan Venus" in P. Concus, R. Finn, and D. A. Hoffman: + * "Geometric Analysis and Computer Graphics", Springer, 1991, and by + * a video entitled "The Etruscan Venus" by Donna J. Cox, George + * K. Francis, and Raymond L. Idaszak, presented at SIGGRAPH 1989. + */ + +#include "curlicue.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define DISP_WIREFRAME 0 +#define DISP_SURFACE 1 +#define DISP_TRANSPARENT 2 +#define NUM_DISPLAY_MODES 3 + +#define APPEARANCE_SOLID 0 +#define APPEARANCE_DISTANCE_BANDS 1 +#define APPEARANCE_DIRECTION_BANDS 2 +#define NUM_APPEARANCES 3 + +#define COLORS_ONESIDED 0 +#define COLORS_TWOSIDED 1 +#define COLORS_DISTANCE 2 +#define COLORS_DIRECTION 3 +#define NUM_COLORS 4 + +#define VIEW_WALK 0 +#define VIEW_TURN 1 +#define NUM_VIEW_MODES 2 + +#define DISP_PERSPECTIVE 0 +#define DISP_ORTHOGRAPHIC 1 +#define NUM_DISP_MODES 2 + +#define DEF_DISPLAY_MODE "random" +#define DEF_APPEARANCE "random" +#define DEF_COLORS "random" +#define DEF_VIEW_MODE "random" +#define DEF_MARKS "False" +#define DEF_CHANGE_COLORS "True" +#define DEF_DEFORM "True" +#define DEF_PROJECTION "random" +#define DEF_SPEEDX "1.1" +#define DEF_SPEEDY "1.3" +#define DEF_SPEEDZ "1.5" +#define DEF_WALK_DIRECTION "83.0" +#define DEF_WALK_SPEED "20.0" +#define DEF_DEFORM_SPEED "10.0" +#define DEF_INIT_DEFORM "0.0" + +#ifdef STANDALONE +# define DEFAULTS "*delay: 10000 \n" \ + "*showFPS: False \n" \ + +# define release_etruscanvenus 0 +# include "xlockmore.h" /* from the xscreensaver distribution */ +#else /* !STANDALONE */ +# include "xlock.h" /* from the xlockmore distribution */ +#endif /* !STANDALONE */ + +#ifdef USE_GL + +#ifndef HAVE_JWXYZ +# include <X11/keysym.h> +#endif + +#include "gltrackball.h" + +#include <float.h> + + +#ifdef USE_MODULES +ModStruct etruscanvenus_description = +{"etruscanvenus", "init_etruscanvenus", "draw_etruscanvenus", + NULL, "draw_etruscanvenus", "change_etruscanvenus", + "free_etruscanvenus", &etruscanvenus_opts, 25000, 1, 1, 1, 1.0, 4, "", + "Rotate a 3d immersion of a Klein bottle in 3d or walk on it", + 0, NULL}; + +#endif + + +static char *mode; +static char *appear; +static char *color_mode; +static char *view_mode; +static Bool marks; +static Bool deform; +static Bool change_colors; +static char *proj; +static float speed_x; +static float speed_y; +static float speed_z; +static float walk_direction; +static float walk_speed; +static float deform_speed; +static float init_deform; + + +static XrmOptionDescRec opts[] = +{ + {"-mode", ".displayMode", XrmoptionSepArg, 0 }, + {"-wireframe", ".displayMode", XrmoptionNoArg, "wireframe" }, + {"-surface", ".displayMode", XrmoptionNoArg, "surface" }, + {"-transparent", ".displayMode", XrmoptionNoArg, "transparent" }, + {"-appearance", ".appearance", XrmoptionSepArg, 0 }, + {"-solid", ".appearance", XrmoptionNoArg, "solid" }, + {"-distance-bands", ".appearance", XrmoptionNoArg, "distance-bands" }, + {"-direction-bands", ".appearance", XrmoptionNoArg, "direction-bands" }, + {"-colors", ".colors", XrmoptionSepArg, 0 }, + {"-onesided-colors", ".colors", XrmoptionNoArg, "one-sided" }, + {"-twosided-colors", ".colors", XrmoptionNoArg, "two-sided" }, + {"-distance-colors", ".colors", XrmoptionNoArg, "distance" }, + {"-direction-colors", ".colors", XrmoptionNoArg, "direction" }, + {"-change-colors", ".changeColors", XrmoptionNoArg, "on"}, + {"+change-colors", ".changeColors", XrmoptionNoArg, "off"}, + {"-view-mode", ".viewMode", XrmoptionSepArg, 0 }, + {"-walk", ".viewMode", XrmoptionNoArg, "walk" }, + {"-turn", ".viewMode", XrmoptionNoArg, "turn" }, + {"-deform", ".deform", XrmoptionNoArg, "on"}, + {"+deform", ".deform", XrmoptionNoArg, "off"}, + {"-orientation-marks", ".marks", XrmoptionNoArg, "on"}, + {"+orientation-marks", ".marks", XrmoptionNoArg, "off"}, + {"-projection", ".projection", XrmoptionSepArg, 0 }, + {"-perspective", ".projection", XrmoptionNoArg, "perspective" }, + {"-orthographic", ".projection", XrmoptionNoArg, "orthographic" }, + {"-speed-x", ".speedx", XrmoptionSepArg, 0 }, + {"-speed-y", ".speedy", XrmoptionSepArg, 0 }, + {"-speed-z", ".speedz", XrmoptionSepArg, 0 }, + {"-walk-direction", ".walkDirection", XrmoptionSepArg, 0 }, + {"-walk-speed", ".walkSpeed", XrmoptionSepArg, 0 }, + {"-deformation-speed", ".deformSpeed", XrmoptionSepArg, 0 }, + {"-initial-deformation", ".initDeform", XrmoptionSepArg, 0 }, + {"-etruscan-venus", ".initDeform", XrmoptionNoArg, "0.0" }, + {"-roman", ".initDeform", XrmoptionNoArg, "1000.0" }, + {"-boy", ".initDeform", XrmoptionNoArg, "2000.0" }, + {"-ida", ".initDeform", XrmoptionNoArg, "3000.0" }, +}; + +static argtype vars[] = +{ + { &mode, "displayMode", "DisplayMode", DEF_DISPLAY_MODE, t_String }, + { &appear, "appearance", "Appearance", DEF_APPEARANCE, t_String }, + { &color_mode, "colors", "Colors", DEF_COLORS, t_String }, + { &change_colors, "changeColors", "ChangeColors", DEF_CHANGE_COLORS, t_Bool }, + { &view_mode, "viewMode", "ViewMode", DEF_VIEW_MODE, t_String }, + { &deform, "deform", "Deform", DEF_DEFORM, t_Bool }, + { &marks, "marks", "Marks", DEF_MARKS, t_Bool }, + { &proj, "projection", "Projection", DEF_PROJECTION, t_String }, + { &speed_x, "speedx", "Speedx", DEF_SPEEDX, t_Float}, + { &speed_y, "speedy", "Speedy", DEF_SPEEDY, t_Float}, + { &speed_z, "speedz", "Speedz", DEF_SPEEDZ, t_Float}, + { &walk_direction, "walkDirection", "WalkDirection", DEF_WALK_DIRECTION, t_Float}, + { &walk_speed, "walkSpeed", "WalkSpeed", DEF_WALK_SPEED, t_Float}, + { &deform_speed, "deformSpeed", "DeformSpeed", DEF_DEFORM_SPEED, t_Float}, + { &init_deform, "initDeform", "InitDeform", DEF_INIT_DEFORM, t_Float }, +}; + +ENTRYPOINT ModeSpecOpt etruscanvenus_opts = +{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, NULL}; + + +/* Offset by which we walk above the surface */ +#define DELTAY 0.01 + +/* Color change speeds */ +#define DRHO 0.7 +#define DSIGMA 1.1 +#define DTAU 1.7 + +/* Number of subdivisions of the surface */ +#define NUMU 192 +#define NUMV 128 + +/* Number of subdivisions per band */ +#define NUMBDIR 8 +#define NUMBDIST 4 + +/* Constants that are used to calculate the approximate center of the + surface in the z direction. */ +#define Z1 (0.8141179221194051) +#define Z2 (0.1359276851926206) +#define Z3 (1.1581097545867050) +#define Z4 (0.7186549129158579) +#define Z5 (2.5393401559381240) + +/* Constants that are used to calculate the approximate radius of the + surface. */ +#define R1 (1.308007044714129) +#define R2 (4.005205981405042) +#define R3 (-2.893994600199527) +#define R4 (-1.266709537162707) + + +typedef struct { + GLint WindH, WindW; + GLXContext *glx_context; + /* Options */ + int display_mode; + int appearance; + int colors; + Bool change_colors; + int view; + int projection; + Bool marks; + /* 3D rotation angles */ + float alpha, beta, delta; + /* Color rotation angles */ + float rho, sigma, tau; + /* Movement parameters */ + float umove, vmove, dumove, dvmove; + int side; + /* Deformation parameters */ + float dd; + int defdir; + /* The viewing offset in 3d */ + float offset3d[3]; + /* The 3d coordinates of the surface and their derivatives */ + float *ev; + float *evn; + /* The precomputed colors of the surface */ + float *col; + /* The precomputed texture coordinates of the surface */ + float *tex; + /* The "curlicue" texture */ + GLuint tex_name; + /* Aspect ratio of the current window */ + float aspect; + /* Trackball states */ + trackball_state *trackball; + Bool button_pressed; + /* A random factor to modify the rotation speeds */ + float speed_scale; +} etruscanvenusstruct; + +static etruscanvenusstruct *etruscanvenus = (etruscanvenusstruct *) NULL; + + +/* Add a rotation around the x-axis to the matrix m. */ +static void rotatex(float m[3][3], float phi) +{ + float c, s, u, v; + int i; + + phi *= M_PI/180.0; + c = cos(phi); + s = sin(phi); + for (i=0; i<3; i++) + { + u = m[i][1]; + v = m[i][2]; + m[i][1] = c*u+s*v; + m[i][2] = -s*u+c*v; + } +} + + +/* Add a rotation around the y-axis to the matrix m. */ +static void rotatey(float m[3][3], float phi) +{ + float c, s, u, v; + int i; + + phi *= M_PI/180.0; + c = cos(phi); + s = sin(phi); + for (i=0; i<3; i++) + { + u = m[i][0]; + v = m[i][2]; + m[i][0] = c*u-s*v; + m[i][2] = s*u+c*v; + } +} + + +/* Add a rotation around the z-axis to the matrix m. */ +static void rotatez(float m[3][3], float phi) +{ + float c, s, u, v; + int i; + + phi *= M_PI/180.0; + c = cos(phi); + s = sin(phi); + for (i=0; i<3; i++) + { + u = m[i][0]; + v = m[i][1]; + m[i][0] = c*u+s*v; + m[i][1] = -s*u+c*v; + } +} + + +/* Compute the rotation matrix m from the rotation angles. */ +static void rotateall(float al, float be, float de, float m[3][3]) +{ + int i, j; + + for (i=0; i<3; i++) + for (j=0; j<3; j++) + m[i][j] = (i==j); + rotatex(m,al); + rotatey(m,be); + rotatez(m,de); +} + + +/* Multiply two rotation matrices: o=m*n. */ +static void mult_rotmat(float m[3][3], float n[3][3], float o[3][3]) +{ + int i, j, k; + + for (i=0; i<3; i++) + { + for (j=0; j<3; j++) + { + o[i][j] = 0.0; + for (k=0; k<3; k++) + o[i][j] += m[i][k]*n[k][j]; + } + } +} + + +/* Compute a 3D rotation matrix from a unit quaternion. */ +static void quat_to_rotmat(float p[4], float m[3][3]) +{ + double al, be, de; + double r00, r01, r02, r12, r22; + + r00 = 1.0-2.0*(p[1]*p[1]+p[2]*p[2]); + r01 = 2.0*(p[0]*p[1]+p[2]*p[3]); + r02 = 2.0*(p[2]*p[0]-p[1]*p[3]); + r12 = 2.0*(p[1]*p[2]+p[0]*p[3]); + r22 = 1.0-2.0*(p[1]*p[1]+p[0]*p[0]); + + al = atan2(-r12,r22)*180.0/M_PI; + be = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI; + de = atan2(-r01,r00)*180.0/M_PI; + + rotateall(al,be,de,m); +} + + +/* Compute a fully saturated and bright color based on an angle and, + optionally, a color rotation matrix. */ +static void color(etruscanvenusstruct *ev, double angle, float mat[3][3], + float col[4]) +{ + int s; + double t, ca, sa; + float m; + + if (!ev->change_colors) + { + if (ev->colors == COLORS_ONESIDED || ev->colors == COLORS_TWOSIDED) + return; + + if (angle >= 0.0) + angle = fmod(angle,2.0*M_PI); + else + angle = fmod(angle,-2.0*M_PI); + s = floor(angle/(M_PI/3)); + t = angle/(M_PI/3)-s; + if (s >= 6) + s = 0; + switch (s) + { + case 0: + col[0] = 1.0; + col[1] = t; + col[2] = 0.0; + break; + case 1: + col[0] = 1.0-t; + col[1] = 1.0; + col[2] = 0.0; + break; + case 2: + col[0] = 0.0; + col[1] = 1.0; + col[2] = t; + break; + case 3: + col[0] = 0.0; + col[1] = 1.0-t; + col[2] = 1.0; + break; + case 4: + col[0] = t; + col[1] = 0.0; + col[2] = 1.0; + break; + case 5: + col[0] = 1.0; + col[1] = 0.0; + col[2] = 1.0-t; + break; + } + } + else /* ev->change_colors */ + { + if (ev->colors == COLORS_ONESIDED || ev->colors == COLORS_TWOSIDED) + { + col[0] = mat[0][2]; + col[1] = mat[1][2]; + col[2] = mat[2][2]; + } + else + { + ca = cos(angle); + sa = sin(angle); + col[0] = ca*mat[0][0]+sa*mat[0][1]; + col[1] = ca*mat[1][0]+sa*mat[1][1]; + col[2] = ca*mat[2][0]+sa*mat[2][1]; + } + m = 0.5f/fmaxf(fmaxf(fabsf(col[0]),fabsf(col[1])),fabsf(col[2])); + col[0] = m*col[0]+0.5f; + col[1] = m*col[1]+0.5f; + col[2] = m*col[2]+0.5f; + } + if (ev->display_mode == DISP_TRANSPARENT) + col[3] = 0.7; + else + col[3] = 1.0; +} + + +/* Set up the surface colors and texture. */ +static void setup_etruscan_venus_color_texture(ModeInfo *mi, double umin, + double umax, double vmin, + double vmax, int numu, int numv) +{ + int i, j, k; + double u, v, ur, vr, vc; + etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)]; + + ur = umax-umin; + vr = vmax-vmin; + for (i=0; i<=numv; i++) + { + for (j=0; j<=numu; j++) + { + k = i*(numu+1)+j; + u = ur*j/numu+umin; + if (ev->appearance == APPEARANCE_DISTANCE_BANDS) + v = -vr*i/numv+vmin; + else + v = vr*i/numv+vmin; + if (!ev->change_colors) + { + if (ev->colors == COLORS_DISTANCE) + { + if (ev->appearance == APPEARANCE_DISTANCE_BANDS) + vc = -4.0*v; + else + vc = 4.0*v; + if (vc >= 4.0*M_PI) + vc -= 4.0*M_PI; + if (vc >= 2.0*M_PI) + vc = 4.0*M_PI-vc; + color(ev,vc,NULL,&ev->col[4*k]); + } + else /* ev->colors == COLORS_DIRECTION */ + { + color(ev,u,NULL,&ev->col[4*k]); + } + } + ev->tex[2*k+0] = 48*u/(2.0*M_PI); + if (ev->appearance == APPEARANCE_DISTANCE_BANDS) + ev->tex[2*k+1] = 64*v/(2.0*M_PI)-0.5; + else + ev->tex[2*k+1] = 64*v/(2.0*M_PI); + } + } +} + + +/* Draw a 3d immersion of the surface. */ +static int etruscan_venus(ModeInfo *mi, double umin, double umax, + double vmin, double vmax, int numu, int numv) +{ + int polys = 0; + static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 }; + static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 }; + static const GLfloat mat_diff_oneside[] = { 0.9, 0.4, 0.3, 1.0 }; + static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 }; + static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 }; + static const GLfloat mat_diff_trans_oneside[] = { 0.9, 0.4, 0.3, 0.7 }; + float mat_diff_dyn[4], mat_diff_dyn_compl[4]; + float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[3][3], matc[3][3]; + int i, j, k, l, m, o; + double u, v, ur, vr, oz, vc; + double xx[3], xxu[3], xxv[3]; + double r, s, t; + double dd, bb, ll, db, dl, radius; + double cv, sv, c2v, s2v, cu, su, c2u, s2u, c3u, s3u; + double bosqrt2, b2osqrt2, b3osqrt2, nom, den, nomv, denu, denv, den2; + double f, fx, fy, fz, x, y, z; + double fu, fv, fxu, fxv, fyu, fyv, fzv, xu, xv, yu, yv, zu, zv; + float qu[4], r1[3][3], r2[3][3]; + etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)]; + + dd = ev->dd; + if (dd < 1.0) + { + bb = 0.0; + ll = dd; + } + else if (dd < 2.0) + { + bb = dd-1.0; + ll = 1.0; + } + else if (dd < 3.0) + { + bb = 1.0; + ll = 3.0-dd; + } + else /* dd < 4.0 */ + { + bb = 4.0-dd; + ll = 0.0; + } + db = ((6.0*bb-15.0)*bb+10.0)*bb*bb*bb; + dl = ((6.0*ll-15.0)*ll+10.0)*ll*ll*ll; + /* Calculate the approximate center of the surface in the z direction. */ + oz = (Z1*(sin(0.5*M_PI*pow(dl,Z3))+Z2*sin(1.5*M_PI*pow(dl,Z3)))* + exp(Z4*pow(db,Z5))); + /* Calculate the approximate radius of the surface. */ + r = R1+(db-0.5)*(dl-0.5)+R2*exp(R3*(1.0-db))*exp(R4*dl); + radius = 0.8/r; + + if (ev->change_colors) + rotateall(ev->rho,ev->sigma,ev->tau,matc); + + if (ev->view == VIEW_WALK) + { + u = ev->umove; + v = ev->vmove; + u = 0.5*u; + bosqrt2 = db/M_SQRT2; + b2osqrt2 = 2.0*bosqrt2; + b3osqrt2 = 3.0*bosqrt2; + cu = cos(u); + su = sin(u); + c2u = cos(2.0*u); + s2u = sin(2.0*u); + c3u = cos(3.0*u); + s3u = sin(3.0*u); + cv = cos(v); + sv = sin(v); + c2v = cos(2.0*v); + s2v = sin(2.0*v); + nom = (1.0-dl+dl*cv); + den = (1.0-bosqrt2*s3u*s2v); + f = nom/den; + fx = c2u*cv+cu*sv; + fy = s2u*cv-su*sv; + fz = M_SQRT2*cv; + x = f*fx; + y = f*fy; + z = f*fz; + nomv = -dl*sv; + denu = -b3osqrt2*c3u*s2v; + denv = -b2osqrt2*s3u*c2v; + den2 = 1.0/(den*den); + fu = -nom*denu*den2; + fv = (den*nomv-nom*denv)*den2; + fxu = -su*sv-2.0*s2u*cv; + fxv = cu*cv-c2u*sv; + fyu = 2.0*c2u*cv-cu*sv; + fyv = -s2u*sv-su*cv; + fzv = -M_SQRT2*sv; + xu = fu*fx+f*fxu; + xv = fv*fx+f*fxv; + yu = fu*fy+f*fyu; + yv = fv*fy+f*fyv; + zu = fu*fz; + zv = fv*fz+f*fzv; + xx[0] = x; + xx[1] = y; + xx[2] = z-oz; + n[0] = yu*zv-zu*yv; + n[1] = zu*xv-xu*zv; + n[2] = xu*yv-yu*xv; + t = n[0]*n[0]+n[1]*n[1]+n[2]*n[2]; + /* Avoid degenerate tangential plane basis vectors as much as + possible. */ + if (t < FLT_EPSILON) + { + u += 0.01; + v += 0.01; + cu = cos(u); + su = sin(u); + c2u = cos(2.0*u); + s2u = sin(2.0*u); + c3u = cos(3.0*u); + s3u = sin(3.0*u); + cv = cos(v); + sv = sin(v); + c2v = cos(2.0*v); + s2v = sin(2.0*v); + nom = (1.0-dl+dl*cv); + den = (1.0-bosqrt2*s3u*s2v); + f = nom/den; + fx = c2u*cv+cu*sv; + fy = s2u*cv-su*sv; + fz = M_SQRT2*cv; + nomv = -dl*sv; + denu = -b3osqrt2*c3u*s2v; + denv = -b2osqrt2*s3u*c2v; + den2 = 1.0/(den*den); + fu = -nom*denu*den2; + fv = (den*nomv-nom*denv)*den2; + fxu = -su*sv-2.0*s2u*cv; + fxv = cu*cv-c2u*sv; + fyu = 2.0*c2u*cv-cu*sv; + fyv = -s2u*sv-su*cv; + fzv = -M_SQRT2*sv; + xu = fu*fx+f*fxu; + xv = fv*fx+f*fxv; + yu = fu*fy+f*fyu; + yv = fv*fy+f*fyv; + zu = fu*fz; + zv = fv*fz+f*fzv; + } + xxu[0] = xu; + xxu[1] = yu; + xxu[2] = zu; + xxv[0] = xv; + xxv[1] = yv; + xxv[2] = zv; + for (l=0; l<3; l++) + { + p[l] = xx[l]*radius; + pu[l] = xxu[l]*radius; + pv[l] = xxv[l]*radius; + } + n[0] = pu[1]*pv[2]-pu[2]*pv[1]; + n[1] = pu[2]*pv[0]-pu[0]*pv[2]; + n[2] = pu[0]*pv[1]-pu[1]*pv[0]; + t = 1.0/(ev->side*4.0*sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2])); + n[0] *= t; + n[1] *= t; + n[2] *= t; + pm[0] = 0.5*pu[0]*ev->dumove+pv[0]*ev->dvmove; + pm[1] = 0.5*pu[1]*ev->dumove+pv[1]*ev->dvmove; + pm[2] = 0.5*pu[2]*ev->dumove+pv[2]*ev->dvmove; + t = 1.0/(4.0*sqrt(pm[0]*pm[0]+pm[1]*pm[1]+pm[2]*pm[2])); + pm[0] *= t; + pm[1] *= t; + pm[2] *= t; + b[0] = n[1]*pm[2]-n[2]*pm[1]; + b[1] = n[2]*pm[0]-n[0]*pm[2]; + b[2] = n[0]*pm[1]-n[1]*pm[0]; + t = 1.0/(4.0*sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2])); + b[0] *= t; + b[1] *= t; + b[2] *= t; + + /* Compute alpha, beta, gamma from the three basis vectors. + | -b[0] -b[1] -b[2] | + m = | n[0] n[1] n[2] | + | -pm[0] -pm[1] -pm[2] | + */ + ev->alpha = atan2(-n[2],-pm[2])*180/M_PI; + ev->beta = atan2(-b[2],sqrt(b[0]*b[0]+b[1]*b[1]))*180/M_PI; + ev->delta = atan2(b[1],-b[0])*180/M_PI; + + /* Compute the rotation that rotates the surface in 3D. */ + rotateall(ev->alpha,ev->beta,ev->delta,mat); + + u = ev->umove; + v = ev->vmove; + u = 0.5*u; + bosqrt2 = db/M_SQRT2; + b2osqrt2 = 2.0*bosqrt2; + b3osqrt2 = 3.0*bosqrt2; + cu = cos(u); + su = sin(u); + c2u = cos(2.0*u); + s2u = sin(2.0*u); + s3u = sin(3.0*u); + cv = cos(v); + sv = sin(v); + s2v = sin(2.0*v); + nom = (1.0-dl+dl*cv); + den = (1.0-bosqrt2*s3u*s2v); + f = nom/den; + fx = c2u*cv+cu*sv; + fy = s2u*cv-su*sv; + fz = M_SQRT2*cv; + x = f*fx; + y = f*fy; + z = f*fz; + xx[0] = x; + xx[1] = y; + xx[2] = z-oz; + for (l=0; l<3; l++) + { + r = 0.0; + for (m=0; m<3; m++) + r += mat[l][m]*xx[m]; + p[l] = r*radius; + } + + ev->offset3d[0] = -p[0]; + ev->offset3d[1] = -p[1]-DELTAY; + ev->offset3d[2] = -p[2]; + } + else + { + /* Compute the rotation that rotates the surface in 3D, including the + trackball rotations. */ + rotateall(ev->alpha,ev->beta,ev->delta,r1); + + gltrackball_get_quaternion(ev->trackball,qu); + quat_to_rotmat(qu,r2); + + mult_rotmat(r2,r1,mat); + } + + if (!ev->change_colors) + { + if (ev->colors == COLORS_ONESIDED) + { + glColor3fv(mat_diff_oneside); + if (ev->display_mode == DISP_TRANSPARENT) + { + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE, + mat_diff_trans_oneside); + } + else + { + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE, + mat_diff_oneside); + } + } + else if (ev->colors == COLORS_TWOSIDED) + { + glColor3fv(mat_diff_red); + if (ev->display_mode == DISP_TRANSPARENT) + { + glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red); + glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green); + } + else + { + glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red); + glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green); + } + } + } + else /* ev->change_colors */ + { + color(ev,0.0,matc,mat_diff_dyn); + if (ev->colors == COLORS_ONESIDED) + { + glColor3fv(mat_diff_dyn); + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn); + } + else if (ev->colors == COLORS_TWOSIDED) + { + mat_diff_dyn_compl[0] = 1.0f-mat_diff_dyn[0]; + mat_diff_dyn_compl[1] = 1.0f-mat_diff_dyn[1]; + mat_diff_dyn_compl[2] = 1.0f-mat_diff_dyn[2]; + mat_diff_dyn_compl[3] = mat_diff_dyn[3]; + glColor3fv(mat_diff_dyn); + glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn); + glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn_compl); + } + } + glBindTexture(GL_TEXTURE_2D,ev->tex_name); + + ur = umax-umin; + vr = vmax-vmin; + + /* Set up the surface coordinates and normals. */ + if (ev->appearance == APPEARANCE_DISTANCE_BANDS) + { + for (i=0; i<=numv; i++) + { + if ((i & (NUMBDIST-1)) >= NUMBDIST/4+1 && + (i & (NUMBDIST-1)) < 3*NUMBDIST/4) + continue; + for (j=0; j<=numu; j++) + { + o = i*(numu+1)+j; + u = ur*j/numu+umin; + v = -vr*i/numv+vmin; + if (ev->change_colors) + { + /* Compute the colors dynamically. */ + if (ev->colors == COLORS_DISTANCE) + { + vc = -4.0*v; + if (vc >= 4.0*M_PI) + vc -= 4.0*M_PI; + if (vc >= 2.0*M_PI) + vc = 4.0*M_PI-vc; + color(ev,vc,matc,&ev->col[4*o]); + } + else if (ev->colors == COLORS_DIRECTION) + { + color(ev,u,matc,&ev->col[4*o]); + } + } + u = 0.5*u; + bosqrt2 = db/M_SQRT2; + b2osqrt2 = 2.0*bosqrt2; + b3osqrt2 = 3.0*bosqrt2; + cu = cos(u); + su = sin(u); + c2u = cos(2.0*u); + s2u = sin(2.0*u); + c3u = cos(3.0*u); + s3u = sin(3.0*u); + cv = cos(v); + sv = sin(v); + c2v = cos(2.0*v); + s2v = sin(2.0*v); + nom = (1.0-dl+dl*cv); + den = (1.0-bosqrt2*s3u*s2v); + f = nom/den; + fx = c2u*cv+cu*sv; + fy = s2u*cv-su*sv; + fz = M_SQRT2*cv; + x = f*fx; + y = f*fy; + z = f*fz; + nomv = -dl*sv; + denu = -b3osqrt2*c3u*s2v; + denv = -b2osqrt2*s3u*c2v; + den2 = 1.0/(den*den); + fu = -nom*denu*den2; + fv = (den*nomv-nom*denv)*den2; + fxu = -su*sv-2.0*s2u*cv; + fxv = cu*cv-c2u*sv; + fyu = 2.0*c2u*cv-cu*sv; + fyv = -s2u*sv-su*cv; + fzv = -M_SQRT2*sv; + xu = fu*fx+f*fxu; + xv = fv*fx+f*fxv; + yu = fu*fy+f*fyu; + yv = fv*fy+f*fyv; + zu = fu*fz; + zv = fv*fz+f*fzv; + xx[0] = x; + xx[1] = y; + xx[2] = z-oz; + n[0] = yu*zv-zu*yv; + n[1] = zu*xv-xu*zv; + n[2] = xu*yv-yu*xv; + t = n[0]*n[0]+n[1]*n[1]+n[2]*n[2]; + /* Avoid degenerate tangential plane basis vectors as much as + possible. */ + if (t < FLT_EPSILON) + { + u += 0.01; + v += 0.01; + cu = cos(u); + su = sin(u); + c2u = cos(2.0*u); + s2u = sin(2.0*u); + c3u = cos(3.0*u); + s3u = sin(3.0*u); + cv = cos(v); + sv = sin(v); + c2v = cos(2.0*v); + s2v = sin(2.0*v); + nom = (1.0-dl+dl*cv); + den = (1.0-bosqrt2*s3u*s2v); + f = nom/den; + fx = c2u*cv+cu*sv; + fy = s2u*cv-su*sv; + fz = M_SQRT2*cv; + nomv = -dl*sv; + denu = -b3osqrt2*c3u*s2v; + denv = -b2osqrt2*s3u*c2v; + den2 = 1.0/(den*den); + fu = -nom*denu*den2; + fv = (den*nomv-nom*denv)*den2; + fxu = -su*sv-2.0*s2u*cv; + fxv = cu*cv-c2u*sv; + fyu = 2.0*c2u*cv-cu*sv; + fyv = -s2u*sv-su*cv; + fzv = -M_SQRT2*sv; + xu = fu*fx+f*fxu; + xv = fv*fx+f*fxv; + yu = fu*fy+f*fyu; + yv = fv*fy+f*fyv; + zu = fu*fz; + zv = fv*fz+f*fzv; + } + xxu[0] = xu; + xxu[1] = yu; + xxu[2] = zu; + xxv[0] = xv; + xxv[1] = yv; + xxv[2] = zv; + for (l=0; l<3; l++) + { + r = 0.0; + s = 0.0; + t = 0.0; + for (m=0; m<3; m++) + { + r += mat[l][m]*xx[m]; + s += mat[l][m]*xxu[m]; + t += mat[l][m]*xxv[m]; + } + p[l] = r*radius+ev->offset3d[l]; + pu[l] = s*radius; + pv[l] = t*radius; + } + n[0] = pu[1]*pv[2]-pu[2]*pv[1]; + n[1] = pu[2]*pv[0]-pu[0]*pv[2]; + n[2] = pu[0]*pv[1]-pu[1]*pv[0]; + t = 1.0/sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]); + n[0] *= t; + n[1] *= t; + n[2] *= t; + ev->ev[3*o+0] = p[0]; + ev->ev[3*o+1] = p[1]; + ev->ev[3*o+2] = p[2]; + ev->evn[3*o+0] = n[0]; + ev->evn[3*o+1] = n[1]; + ev->evn[3*o+2] = n[2]; + } + } + } + else /* ev->appearance != APPEARANCE_DISTANCE_BANDS */ + { + for (j=0; j<=numu; j++) + { + if (ev->appearance == APPEARANCE_DIRECTION_BANDS && + ((j & (NUMBDIR-1)) >= NUMBDIR/2+1)) + continue; + for (i=0; i<=numv; i++) + { + o = i*(numu+1)+j; + u = ur*j/numu+umin; + v = vr*i/numv+vmin; + if (ev->change_colors) + { + /* Compute the colors dynamically. */ + if (ev->colors == COLORS_DISTANCE) + { + vc = 4.0*v; + if (vc >= 4.0*M_PI) + vc -= 4.0*M_PI; + if (vc >= 2.0*M_PI) + vc = 4.0*M_PI-vc; + color(ev,vc,matc,&ev->col[4*o]); + } + else if (ev->colors == COLORS_DIRECTION) + { + color(ev,u,matc,&ev->col[4*o]); + } + } + u = 0.5*u; + bosqrt2 = db/M_SQRT2; + b2osqrt2 = 2.0*bosqrt2; + b3osqrt2 = 3.0*bosqrt2; + cu = cos(u); + su = sin(u); + c2u = cos(2.0*u); + s2u = sin(2.0*u); + c3u = cos(3.0*u); + s3u = sin(3.0*u); + cv = cos(v); + sv = sin(v); + c2v = cos(2.0*v); + s2v = sin(2.0*v); + nom = (1.0-dl+dl*cv); + den = (1.0-bosqrt2*s3u*s2v); + f = nom/den; + fx = c2u*cv+cu*sv; + fy = s2u*cv-su*sv; + fz = M_SQRT2*cv; + x = f*fx; + y = f*fy; + z = f*fz; + nomv = -dl*sv; + denu = -b3osqrt2*c3u*s2v; + denv = -b2osqrt2*s3u*c2v; + den2 = 1.0/(den*den); + fu = -nom*denu*den2; + fv = (den*nomv-nom*denv)*den2; + fxu = -su*sv-2.0*s2u*cv; + fxv = cu*cv-c2u*sv; + fyu = 2.0*c2u*cv-cu*sv; + fyv = -s2u*sv-su*cv; + fzv = -M_SQRT2*sv; + xu = fu*fx+f*fxu; + xv = fv*fx+f*fxv; + yu = fu*fy+f*fyu; + yv = fv*fy+f*fyv; + zu = fu*fz; + zv = fv*fz+f*fzv; + xx[0] = x; + xx[1] = y; + xx[2] = z-oz; + n[0] = yu*zv-zu*yv; + n[1] = zu*xv-xu*zv; + n[2] = xu*yv-yu*xv; + t = n[0]*n[0]+n[1]*n[1]+n[2]*n[2]; + /* Avoid degenerate tangential plane basis vectors as much as + possible. */ + if (t < FLT_EPSILON) + { + u += 0.01; + v += 0.01; + cu = cos(u); + su = sin(u); + c2u = cos(2.0*u); + s2u = sin(2.0*u); + c3u = cos(3.0*u); + s3u = sin(3.0*u); + cv = cos(v); + sv = sin(v); + c2v = cos(2.0*v); + s2v = sin(2.0*v); + nom = (1.0-dl+dl*cv); + den = (1.0-bosqrt2*s3u*s2v); + f = nom/den; + fx = c2u*cv+cu*sv; + fy = s2u*cv-su*sv; + fz = M_SQRT2*cv; + nomv = -dl*sv; + denu = -b3osqrt2*c3u*s2v; + denv = -b2osqrt2*s3u*c2v; + den2 = 1.0/(den*den); + fu = -nom*denu*den2; + fv = (den*nomv-nom*denv)*den2; + fxu = -su*sv-2.0*s2u*cv; + fxv = cu*cv-c2u*sv; + fyu = 2.0*c2u*cv-cu*sv; + fyv = -s2u*sv-su*cv; + fzv = -M_SQRT2*sv; + xu = fu*fx+f*fxu; + xv = fv*fx+f*fxv; + yu = fu*fy+f*fyu; + yv = fv*fy+f*fyv; + zu = fu*fz; + zv = fv*fz+f*fzv; + } + xxu[0] = xu; + xxu[1] = yu; + xxu[2] = zu; + xxv[0] = xv; + xxv[1] = yv; + xxv[2] = zv; + for (l=0; l<3; l++) + { + r = 0.0; + s = 0.0; + t = 0.0; + for (m=0; m<3; m++) + { + r += mat[l][m]*xx[m]; + s += mat[l][m]*xxu[m]; + t += mat[l][m]*xxv[m]; + } + p[l] = r*radius+ev->offset3d[l]; + pu[l] = s*radius; + pv[l] = t*radius; + } + n[0] = pu[1]*pv[2]-pu[2]*pv[1]; + n[1] = pu[2]*pv[0]-pu[0]*pv[2]; + n[2] = pu[0]*pv[1]-pu[1]*pv[0]; + t = 1.0/sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]); + n[0] *= t; + n[1] *= t; + n[2] *= t; + ev->ev[3*o+0] = p[0]; + ev->ev[3*o+1] = p[1]; + ev->ev[3*o+2] = p[2]; + ev->evn[3*o+0] = n[0]; + ev->evn[3*o+1] = n[1]; + ev->evn[3*o+2] = n[2]; + } + } + } + + if (ev->appearance == APPEARANCE_DISTANCE_BANDS) + { + for (i=0; i<numv; i++) + { + if ((i & (NUMBDIST-1)) >= NUMBDIST/4 && + (i & (NUMBDIST-1)) < 3*NUMBDIST/4) + continue; + if (ev->display_mode == DISP_WIREFRAME) + glBegin(GL_QUAD_STRIP); + else + glBegin(GL_TRIANGLE_STRIP); + for (j=0; j<=numu; j++) + { + for (k=0; k<=1; k++) + { + l = (i+k); + m = j; + o = l*(numu+1)+m; + glTexCoord2fv(&ev->tex[2*o]); + if (ev->colors != COLORS_ONESIDED && ev->colors != COLORS_TWOSIDED) + { + glColor3fv(&ev->col[4*o]); + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE, + &ev->col[4*o]); + } + glNormal3fv(&ev->evn[3*o]); + glVertex3fv(&ev->ev[3*o]); + polys++; + } + } + glEnd(); + } + } + else /* ev->appearance != APPEARANCE_DISTANCE_BANDS */ + { + for (j=0; j<numu; j++) + { + if (ev->appearance == APPEARANCE_DIRECTION_BANDS && + ((j & (NUMBDIR-1)) >= NUMBDIR/2)) + continue; + if (ev->display_mode == DISP_WIREFRAME) + glBegin(GL_QUAD_STRIP); + else + glBegin(GL_TRIANGLE_STRIP); + for (i=0; i<=numv; i++) + { + for (k=0; k<=1; k++) + { + l = i; + m = (j+k); + o = l*(numu+1)+m; + glTexCoord2fv(&ev->tex[2*o]); + if (ev->colors != COLORS_ONESIDED && ev->colors != COLORS_TWOSIDED) + { + glColor3fv(&ev->col[4*o]); + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE, + &ev->col[4*o]); + } + glNormal3fv(&ev->evn[3*o]); + glVertex3fv(&ev->ev[3*o]); + polys++; + } + } + glEnd(); + } + } + + polys /= 2; + return polys; +} + + +/* Generate a texture image that shows the orientation reversal. */ +static void gen_texture(ModeInfo *mi) +{ + etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)]; + + glGenTextures(1,&ev->tex_name); + glBindTexture(GL_TEXTURE_2D,ev->tex_name); + glPixelStorei(GL_UNPACK_ALIGNMENT,1); + 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); + glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,TEX_DIMENSION,TEX_DIMENSION,0, + GL_LUMINANCE,GL_UNSIGNED_BYTE,texture); +} + + +static void init(ModeInfo *mi) +{ + static const GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 }; + static const GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; + static const GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 }; + static const GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; + static const GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; + etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)]; + + if (deform_speed == 0.0) + deform_speed = 10.0; + + if (init_deform < 0.0) + init_deform = 0.0; + if (init_deform >= 4000.0) + init_deform = 0.0; + + if (walk_speed == 0.0) + walk_speed = 20.0; + + if (ev->view == VIEW_TURN) + { + ev->alpha = frand(360.0); + ev->beta = frand(360.0); + ev->delta = frand(360.0); + } + else + { + ev->alpha = 0.0; + ev->beta = 0.0; + ev->delta = 0.0; + } + ev->umove = frand(2.0*M_PI); + ev->vmove = frand(2.0*M_PI); + ev->dumove = 0.0; + ev->dvmove = 0.0; + ev->side = 1; + + ev->dd = init_deform*0.001; + ev->defdir = 1; + + ev->rho = frand(360.0); + ev->sigma = frand(360.0); + ev->tau = frand(360.0); + + ev->offset3d[0] = 0.0; + ev->offset3d[1] = 0.0; + ev->offset3d[2] = -2.0; + + gen_texture(mi); + setup_etruscan_venus_color_texture(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,NUMU,NUMV); + + if (ev->marks) + glEnable(GL_TEXTURE_2D); + else + glDisable(GL_TEXTURE_2D); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + if (ev->projection == DISP_PERSPECTIVE || ev->view == VIEW_WALK) + { + if (ev->view == VIEW_WALK) + gluPerspective(60.0,1.0,0.01,10.0); + else + gluPerspective(60.0,1.0,0.1,10.0); + } + else + { + glOrtho(-1.0,1.0,-1.0,1.0,0.1,10.0); + } + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + +# ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */ + if (ev->display_mode == DISP_WIREFRAME) + ev->display_mode = DISP_SURFACE; +# endif + + if (ev->display_mode == DISP_SURFACE) + { + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glShadeModel(GL_SMOOTH); + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient); + glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse); + glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular); + glLightfv(GL_LIGHT0,GL_POSITION,light_position); + glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular); + glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0); + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); + } + else if (ev->display_mode == DISP_TRANSPARENT) + { + glDisable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient); + glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse); + glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular); + glLightfv(GL_LIGHT0,GL_POSITION,light_position); + glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular); + glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0); + glDepthMask(GL_FALSE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + } + else /* ev->display_mode == DISP_WIREFRAME */ + { + glDisable(GL_DEPTH_TEST); + glShadeModel(GL_FLAT); + glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); + glDisable(GL_LIGHTING); + glDisable(GL_LIGHT0); + glDisable(GL_BLEND); + } +} + + +/* Redisplay the Klein bottle. */ +static void display_etruscanvenus(ModeInfo *mi) +{ + etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)]; + + if (!ev->button_pressed) + { + if (deform) + { + ev->dd += ev->defdir*deform_speed*0.001; + if (ev->dd < 0.0) + ev->dd += 4.0; + if (ev->dd >= 4.0) + ev->dd -= 4.0; + /* Randomly change the deformation direction at one of the four + surface types in 10% of the cases. */ + if (fabs(round(ev->dd)-ev->dd) <= deform_speed*0.0005) + { + if (LRAND() % 10 == 0) + ev->defdir = -ev->defdir; + } + } + if (ev->view == VIEW_TURN) + { + ev->alpha += speed_x*ev->speed_scale; + if (ev->alpha >= 360.0) + ev->alpha -= 360.0; + ev->beta += speed_y*ev->speed_scale; + if (ev->beta >= 360.0) + ev->beta -= 360.0; + ev->delta += speed_z*ev->speed_scale; + if (ev->delta >= 360.0) + ev->delta -= 360.0; + } + if (ev->view == VIEW_WALK) + { + ev->dumove = cos(walk_direction*M_PI/180.0)*walk_speed*M_PI/4096.0; + ev->dvmove = sin(walk_direction*M_PI/180.0)*walk_speed*M_PI/4096.0; + ev->umove += ev->dumove; + if (ev->umove >= 2.0*M_PI) + { + ev->umove -= 2.0*M_PI; + ev->vmove = 2.0*M_PI-ev->vmove; + ev->side = -ev->side; + } + if (ev->umove < 0.0) + { + ev->umove += 2.0*M_PI; + ev->vmove = 2.0*M_PI-ev->vmove; + ev->side = -ev->side; + } + ev->vmove += ev->dvmove; + if (ev->vmove >= 2.0*M_PI) + ev->vmove -= 2.0*M_PI; + if (ev->vmove < 0.0) + ev->vmove += 2.0*M_PI; + } + if (ev->change_colors) + { + ev->rho += DRHO; + if (ev->rho >= 360.0) + ev->rho -= 360.0; + ev->sigma += DSIGMA; + if (ev->sigma >= 360.0) + ev->sigma -= 360.0; + ev->tau += DTAU; + if (ev->tau >= 360.0) + ev->tau -= 360.0; + } + } + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + if (ev->projection == DISP_PERSPECTIVE || ev->view == VIEW_WALK) + { + if (ev->view == VIEW_WALK) + gluPerspective(60.0,ev->aspect,0.01,10.0); + else + gluPerspective(60.0,ev->aspect,0.1,10.0); + } + else + { + if (ev->aspect >= 1.0) + glOrtho(-ev->aspect,ev->aspect,-1.0,1.0,0.1,10.0); + else + glOrtho(-1.0,1.0,-1.0/ev->aspect,1.0/ev->aspect,0.1,10.0); + } + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + mi->polygon_count = etruscan_venus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,NUMU,NUMV); +} + + +ENTRYPOINT void reshape_etruscanvenus(ModeInfo *mi, int width, int height) +{ + etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)]; + int y = 0; + + if (width > height * 5) { /* tiny window: show middle */ + height = width; + y = -height/2; + } + + ev->WindW = (GLint)width; + ev->WindH = (GLint)height; + glViewport(0,y,width,height); + ev->aspect = (GLfloat)width/(GLfloat)height; +} + + +ENTRYPOINT Bool etruscanvenus_handle_event(ModeInfo *mi, XEvent *event) +{ + etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)]; + + if (event->xany.type == ButtonPress && event->xbutton.button == Button1) + { + ev->button_pressed = True; + gltrackball_start(ev->trackball, event->xbutton.x, event->xbutton.y, + MI_WIDTH(mi), MI_HEIGHT(mi)); + return True; + } + else if (event->xany.type == ButtonRelease && + event->xbutton.button == Button1) + { + ev->button_pressed = False; + return True; + } + else if (event->xany.type == MotionNotify && ev->button_pressed) + { + gltrackball_track(ev->trackball, event->xmotion.x, event->xmotion.y, + MI_WIDTH(mi), MI_HEIGHT(mi)); + return True; + } + + return False; +} + + +/* + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + * Xlock hooks. + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + */ + +/* + *----------------------------------------------------------------------------- + * Initialize etruscanvenus. Called each time the window changes. + *----------------------------------------------------------------------------- + */ + +ENTRYPOINT void init_etruscanvenus(ModeInfo *mi) +{ + etruscanvenusstruct *ev; + + MI_INIT (mi, etruscanvenus); + ev = &etruscanvenus[MI_SCREEN(mi)]; + + ev->ev = calloc(3*(NUMU+1)*(NUMV+1),sizeof(float)); + ev->evn = calloc(3*(NUMU+1)*(NUMV+1),sizeof(float)); + ev->col = calloc(4*(NUMU+1)*(NUMV+1),sizeof(float)); + ev->tex = calloc(2*(NUMU+1)*(NUMV+1),sizeof(float)); + + ev->trackball = gltrackball_init(True); + ev->button_pressed = False; + + /* Set the display mode. */ + if (!strcasecmp(mode,"random")) + { + ev->display_mode = random() % NUM_DISPLAY_MODES; + } + else if (!strcasecmp(mode,"wireframe")) + { + ev->display_mode = DISP_WIREFRAME; + } + else if (!strcasecmp(mode,"surface")) + { + ev->display_mode = DISP_SURFACE; + } + else if (!strcasecmp(mode,"transparent")) + { + ev->display_mode = DISP_TRANSPARENT; + } + else + { + ev->display_mode = random() % NUM_DISPLAY_MODES; + } + + ev->marks = marks; + + /* Orientation marks don't make sense in wireframe mode. */ + if (ev->display_mode == DISP_WIREFRAME) + ev->marks = False; + + /* Set the appearance. */ + if (!strcasecmp(appear,"random")) + { + ev->appearance = random() % NUM_APPEARANCES; + } + else if (!strcasecmp(appear,"solid")) + { + ev->appearance = APPEARANCE_SOLID; + } + else if (!strcasecmp(appear,"distance-bands")) + { + ev->appearance = APPEARANCE_DISTANCE_BANDS; + } + else if (!strcasecmp(appear,"direction-bands")) + { + ev->appearance = APPEARANCE_DIRECTION_BANDS; + } + else + { + ev->appearance = random() % NUM_APPEARANCES; + } + + /* Set the color mode. */ + if (!strcasecmp(color_mode,"random")) + { + ev->colors = random() % NUM_COLORS; + } + else if (!strcasecmp(color_mode,"one-sided")) + { + ev->colors = COLORS_ONESIDED; + } + else if (!strcasecmp(color_mode,"two-sided")) + { + ev->colors = COLORS_TWOSIDED; + } + else if (!strcasecmp(color_mode,"distance")) + { + ev->colors = COLORS_DISTANCE; + } + else if (!strcasecmp(color_mode,"direction")) + { + ev->colors = COLORS_DIRECTION; + } + else + { + ev->colors = random() % NUM_COLORS; + } + + ev->change_colors = change_colors; + + /* Set the view mode. */ + if (!strcasecmp(view_mode,"random")) + { + /* Select the walking mode only in 10% of the cases. */ + if (LRAND() % 10 == 0) + ev->view = VIEW_WALK; + else + ev->view = VIEW_TURN; + } + else if (!strcasecmp(view_mode,"walk")) + { + ev->view = VIEW_WALK; + } + else if (!strcasecmp(view_mode,"turn")) + { + ev->view = VIEW_TURN; + } + else + { + /* Select the walking mode only in 10% of the cases. */ + if (LRAND() % 10 == 0) + ev->view = VIEW_WALK; + else + ev->view = VIEW_TURN; + } + + if (ev->view == VIEW_WALK) + { + /* Walking only works on the Ida surface. Therefore, set the initial + deformation to the Ida surface and switch off the deformation. */ + init_deform = 3000.0; + deform = False; + } + + /* Set the 3d projection mode. */ + if (!strcasecmp(proj,"random")) + { + /* Orthographic projection only makes sense in turn mode. */ + if (ev->view == VIEW_TURN) + ev->projection = random() % NUM_DISP_MODES; + else + ev->projection = DISP_PERSPECTIVE; + } + else if (!strcasecmp(proj,"perspective")) + { + ev->projection = DISP_PERSPECTIVE; + } + else if (!strcasecmp(proj,"orthographic")) + { + ev->projection = DISP_ORTHOGRAPHIC; + } + else + { + /* Orthographic projection only makes sense in turn mode. */ + if (ev->view == VIEW_TURN) + ev->projection = random() % NUM_DISP_MODES; + else + ev->projection = DISP_PERSPECTIVE; + } + + /* Make multiple screens rotate at slightly different rates. */ + ev->speed_scale = 0.9+frand(0.3); + + if ((ev->glx_context = init_GL(mi)) != NULL) + { + reshape_etruscanvenus(mi,MI_WIDTH(mi),MI_HEIGHT(mi)); + glDrawBuffer(GL_BACK); + init(mi); + } + else + { + MI_CLEARWINDOW(mi); + } +} + +/* + *----------------------------------------------------------------------------- + * Called by the mainline code periodically to update the display. + *----------------------------------------------------------------------------- + */ +ENTRYPOINT void draw_etruscanvenus(ModeInfo *mi) +{ + Display *display = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + etruscanvenusstruct *ev; + + if (etruscanvenus == NULL) + return; + ev = &etruscanvenus[MI_SCREEN(mi)]; + + MI_IS_DRAWN(mi) = True; + if (!ev->glx_context) + return; + + glXMakeCurrent(display, window, *ev->glx_context); + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + display_etruscanvenus(mi); + + if (MI_IS_FPS(mi)) + do_fps (mi); + + glFlush(); + + glXSwapBuffers(display,window); +} + + +/* + *----------------------------------------------------------------------------- + * The display is being taken away from us. Free up malloc'ed + * memory and X resources that we've alloc'ed. + *----------------------------------------------------------------------------- + */ + +ENTRYPOINT void free_etruscanvenus(ModeInfo *mi) +{ + etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)]; + + if (!ev->glx_context) return; + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *ev->glx_context); + + if (ev->ev) free(ev->ev); + if (ev->evn) free(ev->evn); + if (ev->col) free(ev->col); + if (ev->tex) free(ev->tex); + gltrackball_free(ev->trackball); + if (ev->tex_name) glDeleteTextures(1, &ev->tex_name); +} + +#ifndef STANDALONE +ENTRYPOINT void change_etruscanvenus(ModeInfo *mi) +{ + etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)]; + + if (!ev->glx_context) + return; + + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *ev->glx_context); + init(mi); +} +#endif /* !STANDALONE */ + +XSCREENSAVER_MODULE ("EtruscanVenus", etruscanvenus) + +#endif /* USE_GL */ |