diff options
Diffstat (limited to 'hacks/glx/sphereeversion.c')
-rw-r--r-- | hacks/glx/sphereeversion.c | 3339 |
1 files changed, 3339 insertions, 0 deletions
diff --git a/hacks/glx/sphereeversion.c b/hacks/glx/sphereeversion.c new file mode 100644 index 0000000..cd58275 --- /dev/null +++ b/hacks/glx/sphereeversion.c @@ -0,0 +1,3339 @@ +/* sphereeversion --- Shows a sphere eversion, i.e., a smooth deformation + (homotopy) that turns a sphere inside out. During the eversion, the + deformed sphere is allowed to intersect itself transversally. However, + no creases or pinch points are allowed to occur. */ + +#if 0 +static const char sccsid[] = "@(#)sphereeversion.c 1.1 20/03/22 xlockmore"; +#endif + +/* Copyright (c) 2020-2021 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 - 20/03/22: Initial version + * C. Steger - 20/04/10: Added blending between visualization modes + * C. Steger - 20/06/01: Removed blending because accumulation buffers have + * * been deprecated since OpenGL 3.0 + * C. Steger - 20/07/26: Make the polygon offset work under OpenGL ES + * C. Steger - 20/08/03: Add an easing function for one part of the animation + * C. Steger - 20/10/11: Add easing functions for more parts of the animation + * C. Steger - 21/01/03: Added per-fragment shading + * C. Steger - 21/01/05: Added blending between visualization modes using + * multiple render passes + */ + +/* + * This program shows a sphere eversion, i.e., a smooth deformation + * (homotopy) that turns a sphere inside out. During the eversion, + * the deformed sphere is allowed to intersect itself transversally. + * However, no creases or pinch points are allowed to occur. + * + * The deformed sphere can be projected to the screen either + * perspectively or orthographically. + * + * There are three display modes for the sphere: solid, transparent, + * or random. If random mode is selected, the mode is changed each + * time an eversion has been completed. + * + * The appearance of the sphere can be as a solid object, as a set of + * see-through bands, or random. The bands can be parallel bands or + * meridian bands, i.e., bands that run along the parallels (lines of + * latitude) or bands that run along the meridians (lines of + * longitude) of the sphere. If random mode is selected, the + * appearance is changed each time an eversion has been completed. + * + * It is also possible to display a graticule (i.e., a coordinate grid + * consisting of parallel and meridian lines) on top of the surface. + * The graticule mode can be set to on, off, or random. If random + * mode is selected, the graticule mode is changed each time an + * eversion has been completed. + * + * It is possible to define a surface order of the sphere eversion as + * random or as a value between 2 and 5. This determines the the + * complexity of the deformation. For higher surface orders, some + * z-fighting might occur around the central stage of the eversion, + * which might lead to some irregular flickering of the displayed + * surface if it is displayed as a solid object. For odd surface + * orders, z-fighting will occur very close to the central stage of + * the eversion since the deformed sphere is a doubly covered Boy + * surface (for surface order 3) or a doubly covered generalized Boy + * surface (for surface order 5) in this case. If you find this + * distracting, you should set the surface order to 2. If a random + * surface order is selected, the surface order is changed each time + * an eversion has been completed. + * + * The colors with with the sphere is drawn can be set to two-sided, + * parallel, meridian, or random. In two-sided mode, the sphere is + * drawn with red on one side and green on the other side. In + * parallel mode, the sphere is displayed with colors that run from + * red to cyan on one side of the surface and from green to violet on + * the other side. The colors are aligned with the parallels of the + * sphere in this mode. In meridian mode, the the sphere is displayed + * with colors that run from red to white to cyan to black and back to + * red on one side of the surface and from green to white to violet to + * black and back to green on the other side. The colors are aligned + * with the meridians of the sphere in this mode. If random mode is + * selected, the color scheme is changed each time an eversion has + * been completed. + * + * By default, the sphere is rotated to a new viewing position each + * time an eversion has been completed. In addition, it is possible + * to rotate the sphere while it is deforming. The rotation speed for + * each of the three coordinate axes around which the sphere rotates + * can be chosen arbitrarily. For best effects, however, it is + * suggested to rotate only around the z axis while the sphere is + * deforming. + * + * This program is inspired by the following paper: Adam Bednorz, + * Witold Bednorz: "Analytic sphere eversion using ruled surfaces", + * Differential Geometry and its Applications 64:59-79, 2019. + */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define DISP_SURFACE 0 +#define DISP_TRANSPARENT 1 +#define NUM_DISPLAY_MODES 2 + +#define APPEARANCE_SOLID 0 +#define APPEARANCE_PARALLEL_BANDS 1 +#define APPEARANCE_MERIDIAN_BANDS 2 +#define NUM_APPEARANCES 3 + +#define COLORS_TWOSIDED 0 +#define COLORS_PARALLEL 1 +#define COLORS_MERIDIAN 2 +#define NUM_COLORS 3 + +#define DISP_PERSPECTIVE 0 +#define DISP_ORTHOGRAPHIC 1 +#define NUM_DISP_MODES 2 + +#define DEF_DISPLAY_MODE "random" +#define DEF_APPEARANCE "random" +#define DEF_GRATICULE "random" +#define DEF_COLORS "random" +#define DEF_PROJECTION "random" +#define DEF_SPEEDX "0.0" +#define DEF_SPEEDY "0.0" +#define DEF_SPEEDZ "0.0" +#define DEF_DEFORM_SPEED "10.0" +#define DEF_SURFACE_ORDER "random" + + +/* For some strange reason, the color buffer must be initialized + and used on macOS. Otherwise one- and two-sided lighting will + not work. */ +#if (defined(HAVE_COCOA) || defined(__APPLE__)) && !defined(HAVE_IPHONE) +#define VERTEXATTRIBARRAY_WORKAROUND +#endif + +#ifdef STANDALONE +# define DEFAULTS "*delay: 10000 \n" \ + "*showFPS: False \n" \ + "*prefersGLSL: True \n" \ + +# define release_sphereeversion 0 +# include "xlockmore.h" /* from the xscreensaver distribution */ +#else /* !STANDALONE */ +# include "xlock.h" /* from the xlockmore distribution */ +#endif /* !STANDALONE */ + +#ifdef USE_GL + +#include "glsl-utils.h" +#include "gltrackball.h" +#include "pow2.h" + +#include <float.h> + + +#ifdef USE_MODULES +ModStruct sphereeversion_description = +{"sphereeversion", "init_sphereeversion", "draw_sphereeversion", + NULL, "draw_sphereeversion", "change_sphereeversion", + "free_sphereeversion", &sphereeversion_opts, 25000, 1, 1, 1, 1.0, 4, "", + "Show a sphere eversion", 0, NULL}; + +#endif + + +static char *mode; +static char *appear; +static char *color_mode; +static char *graticule; +static char *proj; +static float speed_x; +static float speed_y; +static float speed_z; +static float deform_speed; +static char *surface_order; + + +static XrmOptionDescRec opts[] = +{ + {"-mode", ".displayMode", XrmoptionSepArg, 0 }, + {"-surface", ".displayMode", XrmoptionNoArg, "surface" }, + {"-transparent", ".displayMode", XrmoptionNoArg, "transparent" }, + {"-appearance", ".appearance", XrmoptionSepArg, 0 }, + {"-solid", ".appearance", XrmoptionNoArg, "solid" }, + {"-parallel-bands", ".appearance", XrmoptionNoArg, "parallel-bands" }, + {"-meridian-bands", ".appearance", XrmoptionNoArg, "meridian-bands" }, + {"-graticule", ".graticule", XrmoptionSepArg, 0 }, + {"-colors", ".colors", XrmoptionSepArg, 0 }, + {"-twosided-colors", ".colors", XrmoptionNoArg, "two-sided" }, + {"-parallel-colors", ".colors", XrmoptionNoArg, "parallel" }, + {"-meridian-colors", ".colors", XrmoptionNoArg, "meridian" }, + {"-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 }, + {"-deformation-speed", ".deformSpeed", XrmoptionSepArg, 0 }, + {"-surface-order", ".surfaceOrder", XrmoptionSepArg, 0 }, +}; + +static argtype vars[] = +{ + { &mode, "displayMode", "DisplayMode", DEF_DISPLAY_MODE, t_String }, + { &appear, "appearance", "Appearance", DEF_APPEARANCE, t_String }, + { &graticule, "graticule", "Graticule", DEF_GRATICULE, t_String }, + { &color_mode, "colors", "Colors", DEF_COLORS, t_String }, + { &surface_order, "surfaceOrder", "SurfaceOrder", DEF_SURFACE_ORDER, t_String }, + { &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}, + { &deform_speed, "deformSpeed", "DeformSpeed", DEF_DEFORM_SPEED, t_Float}, +}; + +ENTRYPOINT ModeSpecOpt sphereeversion_opts = +{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, NULL}; + + +/* Shape parameters for the Bednorz sphere eversion. */ +#define BEDNORZ_OMEGA 2.0 +#define BEDNORZ_Q (2.0/3.0) +#define BEDNORZ_ETA_MIN (3.0/4.0) +#define BEDNORZ_BETA_MAX 0.1 +#define BEDNORZ_ALPHA 1.0 +#define BEDNORZ_EPS2 0.001 +#define BEDNORZ_EPS3 0.002 +#define BEDNORZ_EPS4 0.001 +#define BEDNORZ_EPS5 0.002 + +#define BEDNORZ_TAU1 (1.0/BEDNORZ_Q) +#define BEDNORZ_TAU2 2.5 +#define BEDNORZ_TAU3 4.5 +#define BEDNORZ_TAU4 6.0 + +#define BEDNORZ_TAU_MIN (-BEDNORZ_TAU4) +#define BEDNORZ_TAU_MAX (BEDNORZ_TAU4) + +/* Number of subdivisions of the surface */ +#define NUMTH 256 +#define NUMPH 256 + +/* Number of subdivisions between grid lines */ +#define NUMGRID 32 + +/* Number of subdivisions per band */ +#define NUMBDIR 16 +#define NUMBDIST 16 + +/* Animation states */ +#define ANIM_DEFORM 0 +#define ANIM_TURN 1 + +/* Angle of a single turn step */ +#define TURN_STEP 1.0 + +typedef struct { + int n; + double kappa; + double omega; + double t; + double p; + double q; + double xi; + double eta; + double alpha; + double beta; + double gamma; + double lambda; + double eps; +} bednorz_shape_par; + + +typedef struct { + GLint WindH, WindW; + GLXContext *glx_context; + /* Options */ + int display_mode[2]; + Bool random_display_mode; + int appearance[2]; + Bool random_appearance; + Bool graticule[2]; + Bool random_graticule; + int colors[2]; + Bool random_colors; + int projection; + /* 3D rotation angles */ + float alpha, beta, delta; + /* Animation state */ + int anim_state; + /* Deformation parameters */ + float tau; + int defdir; + /* Turning parameters */ + int turn_step; + int num_turn; + float qs[4], qe[4]; + /* Two global shape parameters of the Bednorz sphere eversion */ + float eta_min, beta_max; + /* The order of the Bednorz sphere eversion */ + int g; + Bool random_g; + /* The viewing offset in 3d */ + float offset3d[3]; + /* The 3d coordinates of the surface and the corresponding normal vectors */ + float *se; + float *sen; + /* The precomputed colors of the surface */ + float *colf[2]; + float *colb[2]; + /* 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; +#ifdef HAVE_GLSL + GLuint *solid_indices, *parallel_indices; + GLuint *meridian_indices, *line_indices; + Bool use_shaders, buffers_initialized; + GLuint poly_shader_program; + GLint poly_pos_index, poly_normal_index; + GLint poly_colorf_index, poly_colorb_index; + GLint poly_mv_index, poly_proj_index; + GLint poly_glbl_ambient_index, poly_lt_ambient_index; + GLint poly_lt_diffuse_index, poly_lt_specular_index; + GLint poly_lt_direction_index, poly_lt_halfvect_index; + GLint poly_front_ambient_index, poly_back_ambient_index; + GLint poly_front_diffuse_index, poly_back_diffuse_index; + GLint poly_specular_index, poly_shininess_index; + GLuint line_shader_program; + GLint line_pos_index, line_color_index; + GLint line_mv_index, line_proj_index; + GLint max_tex_size; + GLuint color_textures[2]; + GLuint blend_shader_program; + GLint blend_vertex_p_index, blend_vertex_t_index; + GLint blend_t_index, blend_sampler0_index, blend_sampler1_index; + GLuint vertex_pos_buffer, vertex_normal_buffer; + GLuint vertex_colorf_buffer[2], vertex_colorb_buffer[2]; + GLuint solid_indices_buffer, parallel_indices_buffer; + GLuint meridian_indices_buffer, line_indices_buffer; + GLint num_solid_strips, num_solid_triangles; + GLint num_parallel_strips, num_parallel_triangles; + GLint num_meridian_strips, num_meridian_triangles; + GLint num_lines; +#endif /* HAVE_GLSL */ +} sphereeversionstruct; + +static sphereeversionstruct *sphereeversion = (sphereeversionstruct *) NULL; + + +#ifdef HAVE_GLSL + +/* The GLSL versions that correspond to different versions of OpenGL. */ +static const GLchar *shader_version_2_1 = + "#version 120\n"; +static const GLchar *shader_version_3_0 = + "#version 130\n"; +static const GLchar *shader_version_3_0_es = + "#version 300 es\n" + "precision highp float;\n" + "precision highp int;\n"; + +/* The vertex shader code is composed of code fragments that depend on + the OpenGL version and code fragments that are version-independent. + They are concatenated by glShaderSource in the function init_glsl(). */ +static const GLchar *poly_vertex_shader_attribs_2_1 = + "attribute vec3 VertexPosition;\n" + "attribute vec3 VertexNormal;\n" + "attribute vec4 VertexColorF;\n" + "attribute vec4 VertexColorB;\n" + "\n" + "varying vec3 Normal;\n" + "varying vec4 ColorF;\n" + "varying vec4 ColorB;\n" + "\n"; +static const GLchar *poly_vertex_shader_attribs_3_0 = + "in vec3 VertexPosition;\n" + "in vec3 VertexNormal;\n" + "in vec4 VertexColorF;\n" + "in vec4 VertexColorB;\n" + "\n" + "out vec3 Normal;\n" + "out vec4 ColorF;\n" + "out vec4 ColorB;\n" + "\n"; +static const GLchar *poly_vertex_shader_main = + "uniform mat4 MatModelView;\n" + "uniform mat4 MatProj;\n" + "\n" + "void main (void)\n" + "{\n" + " ColorF = VertexColorF;\n" + " ColorB = VertexColorB;\n" + " Normal = normalize(MatModelView*vec4(VertexNormal,0.0f)).xyz;\n" + " vec4 Position = MatModelView*vec4(VertexPosition,1.0f);\n" + " gl_Position = MatProj*Position;\n" + "}\n"; + +/* The fragment shader code is composed of code fragments that depend on + the OpenGL version and code fragments that are version-independent. + They are concatenated by glsl_CompileAndLinkShaders in the function + init_glsl(). */ +static const GLchar *poly_fragment_shader_attribs_2_1 = + "varying vec3 Normal;\n" + "varying vec4 ColorF;\n" + "varying vec4 ColorB;\n" + "\n"; +static const GLchar *poly_fragment_shader_attribs_3_0 = + "in vec3 Normal;\n" + "in vec4 ColorF;\n" + "in vec4 ColorB;\n" + "\n" + "out vec4 FragColor;\n" + "\n"; +static const GLchar *poly_fragment_shader_main = + "uniform vec4 LtGlblAmbient;\n" + "uniform vec4 LtAmbient, LtDiffuse, LtSpecular;\n" + "uniform vec3 LtDirection, LtHalfVector;\n" + "uniform vec4 MatFrontAmbient, MatBackAmbient;\n" + "uniform vec4 MatFrontDiffuse, MatBackDiffuse;\n" + "uniform vec4 MatSpecular;\n" + "uniform float MatShininess;\n" + "\n" + "void main (void)\n" + "{\n" + " vec3 normalDirection;\n" + " vec4 ambientColor, diffuseColor, sceneColor;\n" + " vec4 ambientLighting, diffuseReflection, specularReflection, color;\n" + " float ndotl, ndoth, pf;\n" + " \n" + " if (gl_FrontFacing)\n" + " {\n" + " normalDirection = normalize(Normal);\n" + " sceneColor = ColorF*MatFrontAmbient*LtGlblAmbient;\n" + " ambientColor = ColorF*MatFrontAmbient;\n" + " diffuseColor = ColorF*MatFrontDiffuse;\n" + " }\n" + " else\n" + " {\n" + " normalDirection = -normalize(Normal);\n" + " sceneColor = ColorB*MatBackAmbient*LtGlblAmbient;\n" + " ambientColor = ColorB*MatBackAmbient;\n" + " diffuseColor = ColorB*MatBackDiffuse;\n" + " }\n" + " \n" + " ndotl = max(0.0f,dot(normalDirection,LtDirection));\n" + " ndoth = max(0.0f,dot(normalDirection,LtHalfVector));\n" + " if (ndotl == 0.0f)\n" + " pf = 0.0f;\n" + " else\n" + " pf = pow(ndoth,MatShininess);\n" + " ambientLighting = ambientColor*LtAmbient;\n" + " diffuseReflection = LtDiffuse*diffuseColor*ndotl;\n" + " specularReflection = LtSpecular*MatSpecular*pf;\n" + " color = sceneColor+ambientLighting+diffuseReflection+\n" + " specularReflection;\n"; +static const GLchar *poly_fragment_shader_out_2_1 = + " gl_FragColor = clamp(color,0.0f,1.0f);\n" + "}\n"; +static const GLchar *poly_fragment_shader_out_3_0 = + " FragColor = clamp(color,0.0f,1.0f);\n" + "}\n"; + +/* The vertex shader code is composed of code fragments that depend on + the OpenGL version and code fragments that are version-independent. + They are concatenated by glsl_CompileAndLinkShaders in the function + init_glsl(). */ +static const GLchar *line_vertex_shader_attribs_2_1 = + "attribute vec3 VertexPosition;\n" + "\n"; +static const GLchar *line_vertex_shader_attribs_3_0 = + "in vec3 VertexPosition;\n" + "\n"; +static const GLchar *line_vertex_shader_main = + "uniform mat4 MatModelView;\n" + "uniform mat4 MatProj;\n" + "\n" + "void main (void)\n" + "{\n" + " vec4 Position = MatModelView*vec4(VertexPosition,1.0f);\n" + " gl_Position = MatProj*Position;\n" + "}\n"; + +/* The fragment shader code is composed of code fragments that depend on + the OpenGL version and code fragments that are version-independent. + They are concatenated by glsl_CompileAndLinkShaders in the function + init_glsl(). */ +static const GLchar *line_fragment_shader_attribs_2_1 = + ""; +static const GLchar *line_fragment_shader_attribs_3_0 = + "out vec4 FragColor;\n" + "\n"; +static const GLchar *line_fragment_shader_main = + "uniform vec4 LineColor;\n" + "\n" + "void main (void)\n" + "{\n"; +static const GLchar *line_fragment_shader_out_2_1 = + " gl_FragColor = LineColor;\n" + "}\n"; +static const GLchar *line_fragment_shader_out_3_0 = + " FragColor = LineColor;\n" + "}\n"; + +/* The vertex shader code is composed of code fragments that depend on + the OpenGL version and code fragments that are version-independent. + They are concatenated by glsl_CompileAndLinkShaders in the function + init_glsl(). */ +static const GLchar *blend_vertex_shader_attribs_2_1 = + "attribute vec2 VertexP;\n" + "attribute vec2 VertexT;\n" + "\n" + "varying vec2 TexCoord;\n" + "\n"; +static const GLchar *blend_vertex_shader_attribs_3_0 = + "in vec2 VertexP;\n" + "in vec2 VertexT;\n" + "\n" + "out vec2 TexCoord;\n" + "\n"; +static const GLchar *blend_vertex_shader_main = + "void main (void)\n" + "{\n" + " gl_Position = vec4(VertexP,0.0f,1.0f);\n" + " TexCoord = VertexT;\n" + "}\n"; + +/* The fragment shader code is composed of code fragments that depend on + the OpenGL version and code fragments that are version-independent. + They are concatenated by glsl_CompileAndLinkShaders in the function + init_glsl(). */ +static const GLchar *blend_fragment_shader_attribs_2_1 = + "varying vec2 TexCoord;\n" + "\n"; +static const GLchar *blend_fragment_shader_attribs_3_0 = + "in vec2 TexCoord;\n" + "\n" + "out vec4 FragColor;\n" + "\n"; +static const GLchar *blend_fragment_shader_main = + "uniform sampler2D TextureSampler0;" + "uniform sampler2D TextureSampler1;" + "uniform float T;" + "\n" + "void main (void)\n" + "{\n"; +static const GLchar *blend_fragment_shader_out_2_1 = + " vec3 Color0 = texture2D(TextureSampler0,TexCoord.st).rgb;\n" + " vec3 Color1 = texture2D(TextureSampler1,TexCoord.st).rgb;\n" + " gl_FragColor = vec4(T*Color0+(1.0f-T)*Color1,1.0f);\n" + "}\n"; +static const GLchar *blend_fragment_shader_out_3_0 = + " vec3 Color0 = texture(TextureSampler0,TexCoord.st).rgb;\n" + " vec3 Color1 = texture(TextureSampler1,TexCoord.st).rgb;\n" + " FragColor = vec4(T*Color0+(1.0f-T)*Color1,1.0f);\n" + "}\n"; + +#endif /* HAVE_GLSL */ + + +/* 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 3D rotation angles from a unit quaternion. */ +static void quat_to_angles(float q[4], float *alpha, float *beta, float *delta) +{ + double r00, r01, r02, r12, r22; + + r00 = q[0]*q[0]+q[1]*q[1]-q[2]*q[2]-q[3]*q[3]; + r01 = 2.0*(q[1]*q[2]-q[0]*q[3]); + r02 = 2.0*(q[1]*q[3]+q[0]*q[2]); + r12 = 2.0*(q[2]*q[3]-q[0]*q[1]); + r22 = q[0]*q[0]-q[1]*q[1]-q[2]*q[2]+q[3]*q[3]; + + *alpha = atan2(-r12,r22)*180.0/M_PI; + *beta = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI; + *delta = atan2(-r01,r00)*180.0/M_PI; +} + + +/* Compute a 3D rotation matrix from an xscreensaver unit quaternion. Note + that xscreensaver has a different convention for unit quaternions than + the one that is used in this hack. */ +static void quat_to_rotmat(float p[4], float m[3][3]) +{ + float 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 quaternion from angles in degrees. */ +static void angles_to_quat(float alpha, float beta, float delta, float p[4]) +{ + alpha *= M_PI/180.0; + beta *= M_PI/180.0; + delta *= M_PI/180.0; + p[0] = (cos(0.5*alpha)*cos(0.5*beta)*cos(0.5*delta)- + sin(0.5*alpha)*sin(0.5*beta)*sin(0.5*delta)); + p[1] = (sin(0.5*alpha)*cos(0.5*beta)*cos(0.5*delta)+ + cos(0.5*alpha)*sin(0.5*beta)*sin(0.5*delta)); + p[2] = (cos(0.5*alpha)*sin(0.5*beta)*cos(0.5*delta)- + sin(0.5*alpha)*cos(0.5*beta)*sin(0.5*delta)); + p[3] = (cos(0.5*alpha)*cos(0.5*beta)*sin(0.5*delta)+ + sin(0.5*alpha)*sin(0.5*beta)*cos(0.5*delta)); +} + + +/* Perform a spherical linear interpolation between two quaternions. */ +static void quat_slerp(float t, float qs[4], float qe[4], float q[4]) +{ + double cos_t, sin_t, alpha, beta, theta, phi, l; + + alpha = t; + cos_t = qs[0]*qe[0]+qs[1]*qe[1]+qs[2]*qe[2]+qs[3]*qe[3]; + if (1.0-cos_t < FLT_EPSILON) + { + beta = 1.0-alpha; + } + else + { + theta = acos(cos_t); + phi = theta; + sin_t = sin(theta); + beta = sin(theta-alpha*phi)/sin_t; + alpha = sin(alpha*phi)/sin_t; + } + q[0] = beta*qs[0]+alpha*qe[0]; + q[1] = beta*qs[1]+alpha*qe[1]; + q[2] = beta*qs[2]+alpha*qe[2]; + q[3] = beta*qs[3]+alpha*qe[3]; + l = 1.0/sqrt(q[0]*q[0]+q[1]*q[1]+q[2]*q[2]+q[3]*q[3]); + q[0] *= l; + q[1] *= l; + q[2] *= l; + q[3] *= l; +} + + +/* Compute a fully saturated and bright color based on an angle and a color + rotation matrix. */ +static void color(sphereeversionstruct *se, float angle, float mat[3][3], + float colf[4], float colb[4]) +{ + float ca, sa; + float m; + + ca = cosf(angle); + sa = sinf(angle); + colf[0] = ca*mat[0][0]+sa*mat[0][2]; + colf[1] = ca*mat[1][0]+sa*mat[1][2]; + colf[2] = ca*mat[2][0]+sa*mat[2][2]; + m = 0.5f/fmaxf(fmaxf(fabsf(colf[0]),fabsf(colf[1])),fabsf(colf[2])); + colf[0] = m*colf[0]+0.5f; + colf[1] = m*colf[1]+0.5f; + colf[2] = m*colf[2]+0.5f; + if (se->display_mode[0] == DISP_TRANSPARENT) + colf[3] = 0.7f; + else + colf[3] = 1.0f; + + colb[0] = ca*mat[0][1]+sa*mat[0][2]; + colb[1] = ca*mat[1][1]+sa*mat[1][2]; + colb[2] = ca*mat[2][1]+sa*mat[2][2]; + m = 0.5f/fmaxf(fmaxf(fabsf(colb[0]),fabsf(colb[1])),fabsf(colb[2])); + colb[0] = m*colb[0]+0.5f; + colb[1] = m*colb[1]+0.5f; + colb[2] = m*colb[2]+0.5f; + if (se->display_mode[0] == DISP_TRANSPARENT) + colb[3] = 0.7f; + else + colb[3] = 1.0f; +} + + +/* Compute the cross product between the vectors a and b. */ +static inline void cross(float a[3], float b[3], float c[3]) +{ + c[0] = a[1]*b[2]-a[2]*b[1]; + c[1] = a[2]*b[0]-a[0]*b[2]; + c[2] = a[0]*b[1]-a[1]*b[0]; +} + + +/* Compute x^n for integers 0 <= n <= 11 efficiently. */ +static inline double ipow(double x, int n) +{ + double x2, x4, x8; + + switch (n) + { + case 0: + return 1.0; + case 1: + return x; + case 2: + x2 = x*x; + return x2; + case 3: + x2 = x*x; + return x2*x; + case 4: + x2 = x*x; + x4 = x2*x2; + return x4; + case 5: + x2 = x*x; + x4 = x2*x2; + return x4*x; + case 6: + x2 = x*x; + x4 = x2*x2; + return x4*x2; + case 7: + x2 = x*x; + x4 = x2*x2; + return x4*x2*x; + case 8: + x2 = x*x; + x4 = x2*x2; + x8 = x4*x4; + return x8; + case 9: + x2 = x*x; + x4 = x2*x2; + x8 = x4*x4; + return x8*x; + case 10: + x2 = x*x; + x4 = x2*x2; + x8 = x4*x4; + return x8*x2; + case 11: + x2 = x*x; + x4 = x2*x2; + x8 = x4*x4; + return x8*x2*x; + default: + return pow(x,n); + } +} + + +/* Compute the Bednorz shape parameter kappa based on the eversion order n. */ +static inline double bednorz_get_kappa(int n) +{ + return (n-1.0)/(2.0*n); +} + + +/* Compute the Bednorz shape parameter t based on the deformation + parameter tau. */ +static inline double bednorz_get_t(double tau) +{ + return (tau >= BEDNORZ_TAU1 ? + BEDNORZ_TAU1 : + (tau <= -BEDNORZ_TAU1 ? + -BEDNORZ_TAU1 : + tau)); +} + + +/* Compute the Bednorz shape parameter q based on the deformation + parameter tau. */ +static inline double bednorz_get_q(double tau) +{ + double abs_tau; + + abs_tau = fabs(tau); + return (abs_tau < BEDNORZ_TAU1 ? + 0.0 : + (abs_tau < BEDNORZ_TAU2 ? + BEDNORZ_Q*(abs_tau-BEDNORZ_TAU1)/(BEDNORZ_TAU2-BEDNORZ_TAU1) : + BEDNORZ_Q)); +} + + +/* Compute the Bednorz shape parameter p based on the deformation + parameter tau. */ +static inline double bednorz_get_p(double tau) +{ + return 1.0-fabs(bednorz_get_q(tau)*bednorz_get_t(tau)); +} + + +/* Compute the Bednorz shape parameter xi based on the deformation + parameter tau. */ +static inline double bednorz_get_xi(double tau) +{ + double abs_tau; + + abs_tau = fabs(tau); + return (abs_tau < BEDNORZ_TAU2 ? + 1.0 : + (abs_tau < BEDNORZ_TAU3 ? + (BEDNORZ_TAU3-abs_tau)/(BEDNORZ_TAU3-BEDNORZ_TAU2) : + 0.0)); +} + + +/* Compute the Bednorz shape parameter eta based on the deformation + parameter tau and the shape parameter eta_min. */ +static inline double bednorz_get_eta(double tau, double eta_min) +{ + double abs_tau; + + abs_tau = fabs(tau); + return (abs_tau < BEDNORZ_TAU2 ? + eta_min : + (abs_tau < BEDNORZ_TAU3 ? + (eta_min+(1.0-eta_min)* + (abs_tau-BEDNORZ_TAU2)/(BEDNORZ_TAU3-BEDNORZ_TAU2)) : + 1.0)); +} + + +/* Compute the Bednorz shape parameter alpha based on the deformation + parameter tau. */ +static inline double bednorz_get_alpha(double tau) +{ + double xi; + + xi = bednorz_get_xi(tau); + return BEDNORZ_ALPHA*ipow(xi,2); +} + + +/* Compute the Bednorz shape parameter beta based on the deformation + parameter tau and the shape parameter beta_max. */ +static inline double bednorz_get_beta(double tau, double beta_max) +{ + double xi; + + xi = bednorz_get_xi(tau); + return ipow(1.0-xi,2)+beta_max*ipow(xi,3); +} + + +/* Compute the Bednorz shape parameter gamma based on the shape + parameters alpha and beta. */ +static inline double bednorz_get_gamma(double alpha, double beta) +{ + return 2.0*sqrt(alpha*beta); +} + + +/* Compute the Bednorz shape parameter lambda based on the deformation + parameter tau. */ +static inline double bednorz_get_lambda(double tau) +{ + double abs_tau; + + abs_tau = fabs(tau); + return (abs_tau < BEDNORZ_TAU3 ? + 1.0 : + (abs_tau < BEDNORZ_TAU4 ? + (BEDNORZ_TAU4-abs_tau)/(BEDNORZ_TAU4-BEDNORZ_TAU3) : + 0.0)); +} + + +/* Compute the Bednorz shape parameter eps based on the deformation + parameter tau and the eversion order n. This is an extension to the + original approach that prevents z fighting to some extent in certain + stages of the eversion. */ +static inline double bednorz_get_eps(double tau, int n) +{ + double sgn_tau, abs_tau; + + sgn_tau = (tau < 0.0 ? -1.0 : (tau > 0.0 ? 1.0 : 0.0)); + abs_tau = fabs(tau); + switch (n) + { + case 2: + return (abs_tau < BEDNORZ_TAU1 ? + 0.0 : + (abs_tau < BEDNORZ_TAU2 ? + (BEDNORZ_EPS2*sgn_tau* + (abs_tau-BEDNORZ_TAU1)/(BEDNORZ_TAU2-BEDNORZ_TAU1)) : + (abs_tau < BEDNORZ_TAU3 ? + BEDNORZ_EPS2*sgn_tau : + (abs_tau < BEDNORZ_TAU4 ? + (BEDNORZ_EPS2*sgn_tau* + (BEDNORZ_TAU4-abs_tau)/(BEDNORZ_TAU4-BEDNORZ_TAU3)) : + 0.0)))); + case 3: + return (abs_tau < BEDNORZ_TAU1 ? + BEDNORZ_EPS3*sgn_tau*abs_tau/BEDNORZ_TAU1 : + (abs_tau < BEDNORZ_TAU3 ? + BEDNORZ_EPS3*sgn_tau : + (abs_tau < BEDNORZ_TAU4 ? + (BEDNORZ_EPS3*sgn_tau* + (BEDNORZ_TAU4-abs_tau)/(BEDNORZ_TAU4-BEDNORZ_TAU3)) : + 0.0))); + case 4: + return (abs_tau < BEDNORZ_TAU1 ? + BEDNORZ_EPS4*sgn_tau*abs_tau/BEDNORZ_TAU1 : + (abs_tau < BEDNORZ_TAU3 ? + BEDNORZ_EPS4*sgn_tau : + (abs_tau < BEDNORZ_TAU4 ? + (BEDNORZ_EPS4*sgn_tau* + (BEDNORZ_TAU4-abs_tau)/(BEDNORZ_TAU4-BEDNORZ_TAU3)) : + 0.0))); + case 5: + return (abs_tau < BEDNORZ_TAU1 ? + BEDNORZ_EPS5*sgn_tau*abs_tau/BEDNORZ_TAU1 : + (abs_tau < BEDNORZ_TAU3 ? + BEDNORZ_EPS5*sgn_tau : + (abs_tau < BEDNORZ_TAU4 ? + (BEDNORZ_EPS5*sgn_tau* + (BEDNORZ_TAU4-abs_tau)/(BEDNORZ_TAU4-BEDNORZ_TAU3)) : + 0.0))); + default: + return 0.0; + } +} + + +/* Compute the equations for a point x and its partial derivatives dxdph and + dxdth in the Bednorz sphere eversion based on the sphere parameters phi + (longitude) and theta (latitude) and the shape parameters bsp. This + corresponds to equations (4), (12), and (15) in the paper. */ +static inline void bednorz_get_p0(double phi, double theta, + bednorz_shape_par *bsp, double x[3], + double dxdph[3], double dxdth[3]) +{ + int n; + double kappa, omega, t, p, q, eta, lambda; + double ct, st, cp, sp, cnm1p, snm1p, cnp, snp; + double ctn, ictn, ictnp1, ct2, st2, ct2n, ict2n, ict2np1, ton; + double oml, omlplctn, pe1pk, tat2k, nst2pct2, ost, snpmqt; + double nm1p, ostictn, oictnp1, nst2pct2oictnp1, tcp, tsp; + double lost, tomlplctn, lostcp, lostsp, tomlplctncp, tomlplctnsp; + double tomlplctncpmlostsp, tomlplctnspplostcp, ntctnst, oct2; + double omlpe1pktat2k, nst2; + + n = bsp->n; + kappa = bsp->kappa; + omega = bsp->omega; + t = bsp->t; + p = bsp->p; + q = bsp->q; + eta = bsp->eta; + lambda = bsp->lambda; + + ct = cos(theta); + st = sin(theta); + cp = cos(phi); + sp = sin(phi); + cnp = cos(n*phi); + snp = sin(n*phi); + ctn = ipow(ct,n); + ictn = 1.0/ctn; + ictnp1 = ictn/ct; + ct2 = ct*ct; + st2 = st*st; + ton = t/n; + snpmqt = snp-q*t; + ost = omega*st; + + if (lambda >= 1.0) + { + cnm1p = cos((n-1)*phi); + snm1p = sin((n-1)*phi); + nst2pct2 = n*st2+ct2; + nm1p = (n-1)*p; + tcp = t*cp; + tsp = t*sp; + ostictn = ost*ictn; + oictnp1 = omega*ictnp1; + nst2pct2oictnp1 = nst2pct2*oictnp1; + x[0] = p*snm1p-sp*ostictn+tcp; + x[1] = p*cnm1p+cp*ostictn+tsp; + x[2] = snpmqt*ostictn-ton*cnp; + dxdph[0] = nm1p*cnm1p-cp*ostictn-tsp; + dxdph[1] = -nm1p*snm1p-sp*ostictn+tcp; + dxdph[2] = n*cnp*ostictn+t*snp; + dxdth[0] = -sp*nst2pct2oictnp1; + dxdth[1] = cp*nst2pct2oictnp1; + dxdth[2] = snpmqt*nst2pct2oictnp1; + /* The same formulas written out in full glory: + x[0] = (t*cos(phi)+ + p*sin((n-1)*phi)- + (omega*sin(theta)/ipow(cos(theta),n))*sin(phi)); + x[1] = (t*sin(phi)+ + p*cos((n-1)*phi)+ + (omega*sin(theta)/ipow(cos(theta),n))*cos(phi)); + x[2] = ((omega*sin(theta)/ipow(cos(theta),n))*sin(n*phi)- + (t/n)*cos(n*phi)- + omega*q*t*sin(theta)/ipow(cos(theta),n)); + dxdph[0] = (-t*sin(phi)+ + (n-1)*p*cos((n-1)*phi)- + (omega*sin(theta)/ipow(cos(theta),n))*cos(phi)); + dxdph[1] = (t*cos(phi)- + (n-1)*p*sin((n-1)*phi)- + (omega*sin(theta)/ipow(cos(theta),n))*sin(phi)); + dxdph[2] = ((n*omega*sin(theta)/ipow(cos(theta),n))*cos(n*phi)+ + t*sin(n*phi)); + dxdth[0] = -((omega*sin(phi)*(n*ipow(sin(theta),2)+ipow(cos(theta),2)))/ + ipow(cos(theta),n+1)); + dxdth[1] = ((omega*cos(phi)*(n*ipow(sin(theta),2)+ipow(cos(theta),2)))/ + ipow(cos(theta),n+1)); + dxdth[2] = ((omega*sin(n*phi)*(n*ipow(sin(theta),2)+ipow(cos(theta),2))/ + ipow(cos(theta),n+1))- + (omega*q*t*(n*ipow(sin(theta),2)+ipow(cos(theta),2))/ + ipow(cos(theta),n+1))); + */ + } + else + { + ct2n = ipow(ct,2*n); + ict2n = 1.0/ct2n; + ict2np1 = ict2n/ct; + oml = 1.0-lambda; + omlplctn = oml+lambda*ctn; + pe1pk = pow(eta,1.0+kappa); + tat2k = t*pow(fabs(t),2.0*kappa); + lost = lambda*ost; + lostcp = lost*cp; + lostsp = lost*sp; + tomlplctn = t*omlplctn; + tomlplctncp = tomlplctn*cp; + tomlplctnsp = tomlplctn*sp; + tomlplctncpmlostsp = tomlplctncp-lostsp; + tomlplctnspplostcp = tomlplctnsp+lostcp; + ntctnst = n*t*ctn*st; + oct2 = omega*ct2; + omlpe1pktat2k = oml*pe1pk*tat2k; + nst2 = n*st2; + x[0] = tomlplctncpmlostsp*ictn; + x[1] = tomlplctnspplostcp*ictn; + x[2] = lambda*(ost*snpmqt*ictn-ton*cnp)-omlpe1pktat2k*st*ict2n; + dxdph[0] = -tomlplctnspplostcp*ictn; + dxdph[1] = tomlplctncpmlostsp*ictn; + dxdph[2] = lambda*(omega*n*st*cnp*ictn+t*snp); + dxdth[0] = (n*tomlplctncpmlostsp*st-lambda*(ntctnst*cp+oct2*sp))*ictnp1; + dxdth[1] = (n*tomlplctnspplostcp*st-lambda*(ntctnst*sp-oct2*cp))*ictnp1; + dxdth[2] = (lambda*omega*snpmqt*(nst2+ct2)*ictnp1- + omlpe1pktat2k*(2.0*nst2+ct2)*ict2np1); + /* The same formulas written out in full glory: + x[0] = ((t*(1.0-lambda+lambda*ipow(cos(theta),n))*cos(phi)- + lambda*omega*sin(theta)*sin(phi))/ + ipow(cos(theta),n)); + x[1] = ((t*(1.0-lambda+lambda*ipow(cos(theta),n))*sin(phi)+ + lambda*omega*sin(theta)*cos(phi))/ + ipow(cos(theta),n)); + x[2] = (lambda*(omega*sin(theta)*(sin(n*phi)-q*t)/ipow(cos(theta),n)- + (t/n)*cos(n*phi))- + (1.0-lambda)*pow(eta,1.0+kappa)* + t*pow(fabs(t),2.0*kappa)*sin(theta)/ipow(cos(theta),2*n)); + dxdph[0] = ((-t*(1.0-lambda+lambda*ipow(cos(theta),n))*sin(phi)- + lambda*omega*sin(theta)*cos(phi))/ + ipow(cos(theta),n)); + dxdph[1] = ((t*(1.0-lambda+lambda*ipow(cos(theta),n))*cos(phi)- + lambda*omega*sin(theta)*sin(phi))/ + ipow(cos(theta),n)); + dxdph[2] = (lambda*(omega*n*sin(theta)*(cos(n*phi))/ipow(cos(theta),n)+ + t*sin(n*phi))); + dxdth[0] = ((n*(t*(1.0-lambda+lambda*ipow(cos(theta),n))*cos(phi)- + lambda*omega*sin(theta)*sin(phi))*sin(theta)/ + ipow(cos(theta),n+1))- + (lambda*(n*t*ipow(cos(theta),n)*sin(theta)*cos(phi)+ + omega*ipow(cos(theta),2)*sin(phi))/ + ipow(cos(theta),n+1))); + dxdth[1] = ((n*(t*(1.0-lambda+lambda*ipow(cos(theta),n))*sin(phi)+ + lambda*omega*sin(theta)*cos(phi))*sin(theta)/ + ipow(cos(theta),n+1))- + (lambda*(n*t*ipow(cos(theta),n)*sin(theta)*sin(phi)- + omega*ipow(cos(theta),2)*cos(phi))/ + ipow(cos(theta),n+1))); + dxdth[2] = ((lambda*omega*(sin(n*phi)-q*t)* + (n*ipow(sin(theta),2)+ipow(cos(theta),2))/ + ipow(cos(theta),n+1))- + ((1.0-lambda)*pow(eta,1.0+kappa)*t*pow(fabs(t),2.0*kappa)* + (2.0*n*ipow(sin(theta),2)+ipow(cos(theta),2))/ + ipow(cos(theta),2*n+1))); + */ + } +} + + +/* Compute the equations for a point y and its partial derivatives dydph and + dydth in the Bednorz sphere eversion based on the sphere parameters phi + (longitude) and theta (latitude) and the shape parameters bsp. This + corresponds to equation (7) in the paper. */ +static inline void bednorz_get_p1(double phi, double theta, + bednorz_shape_par *bsp, double y[3], + double dydph[3], double dydth[3]) +{ + double kappa, xi, eta; + double x[3], dxdph[3], dxdth[3]; + double x0, x1, x2, x02, x12, x02px12, ex02px12, xipex02px12; + double ixipex02px122, ixipex02px12k, ixipex02px12kp1; + double x0dx0dphpx1dx1dph, x0dx0dthpx1dx1dth; + double tex0dx0dphpx1dx1dph, tex0dx0dthpx1dx1dth; + double ktex0dx0dphpx1dx1dph, ktex0dx0dthpx1dx1dth; + + kappa = bsp->kappa; + xi = bsp->xi; + eta = bsp->eta; + + bednorz_get_p0(phi,theta,bsp,x,dxdph,dxdth); + + x0 = x[0]; + x1 = x[1]; + x2 = x[2]; + x02 = x0*x0; + x12 = x1*x1; + x02px12 = x02+x12; + ex02px12 = eta*x02px12; + xipex02px12 = xi+ex02px12; + ixipex02px122 = 1.0/(xipex02px12*xipex02px12); + ixipex02px12k = 1.0/pow(xipex02px12,kappa); + ixipex02px12kp1 = ixipex02px12k/xipex02px12; + x0dx0dphpx1dx1dph = x0*dxdph[0]+x1*dxdph[1]; + x0dx0dthpx1dx1dth = x0*dxdth[0]+x1*dxdth[1]; + tex0dx0dphpx1dx1dph = 2.0*eta*x0dx0dphpx1dx1dph; + tex0dx0dthpx1dx1dth = 2.0*eta*x0dx0dthpx1dx1dth; + ktex0dx0dphpx1dx1dph = kappa*tex0dx0dphpx1dx1dph; + ktex0dx0dthpx1dx1dth = kappa*tex0dx0dthpx1dx1dth; + + y[0] = x0*ixipex02px12k; + y[1] = x1*ixipex02px12k; + y[2] = x2/xipex02px12; + dydph[0] = (dxdph[0]*xipex02px12-ktex0dx0dphpx1dx1dph*x0)*ixipex02px12kp1; + dydph[1] = (dxdph[1]*xipex02px12-ktex0dx0dphpx1dx1dph*x1)*ixipex02px12kp1; + dydph[2] = (dxdph[2]*xipex02px12-tex0dx0dphpx1dx1dph*x2)*ixipex02px122; + dydth[0] = (dxdth[0]*xipex02px12-ktex0dx0dthpx1dx1dth*x0)*ixipex02px12kp1; + dydth[1] = (dxdth[1]*xipex02px12-ktex0dx0dthpx1dx1dth*x1)*ixipex02px12kp1; + dydth[2] = (dxdth[2]*xipex02px12-tex0dx0dthpx1dx1dth*x2)*ixipex02px122; + + /* The same formulas written out in full glory: + y[0] = x[0]/pow(xi+eta*(ipow(x[0],2)+ipow(x[1],2)),kappa); + y[1] = x[1]/pow(xi+eta*(ipow(x[0],2)+ipow(x[1],2)),kappa); + y[2] = x[2]/(xi+eta*(ipow(x[0],2)+ipow(x[1],2))); + dydph[0] = ((dxdph[0]*(xi+eta*(ipow(x[0],2)+ipow(x[1],2)))- + 2.0*eta*kappa*x[0]*(x[0]*dxdph[0]+x[1]*dxdph[1]))/ + pow(xi+eta*(ipow(x[0],2)+ipow(x[1],2)),kappa+1.0)); + dydph[1] = ((dxdph[1]*(xi+eta*(ipow(x[0],2)+ipow(x[1],2)))- + 2.0*eta*kappa*x[1]*(x[0]*dxdph[0]+x[1]*dxdph[1]))/ + pow(xi+eta*(ipow(x[0],2)+ipow(x[1],2)),kappa+1.0)); + dydph[2] = ((dxdph[2]*(xi+eta*(ipow(x[0],2)+ipow(x[1],2)))- + 2.0*eta*x[2]*(x[0]*dxdph[0]+x[1]*dxdph[1]))/ + ipow(xi+eta*(ipow(x[0],2)+ipow(x[1],2)),2)); + dydth[0] = ((dxdth[0]*(xi+eta*(ipow(x[0],2)+ipow(x[1],2)))- + 2.0*eta*kappa*x[0]*(x[0]*dxdth[0]+x[1]*dxdth[1]))/ + pow(xi+eta*(ipow(x[0],2)+ipow(x[1],2)),kappa+1.0)); + dydth[1] = ((dxdth[1]*(xi+eta*(ipow(x[0],2)+ipow(x[1],2)))- + 2.0*eta*kappa*x[1]*(x[0]*dxdth[0]+x[1]*dxdth[1]))/ + pow(xi+eta*(ipow(x[0],2)+ipow(x[1],2)),kappa+1.0)); + dydth[2] = ((dxdth[2]*(xi+eta*(ipow(x[0],2)+ipow(x[1],2)))- + 2.0*eta*x[2]*(x[0]*dxdth[0]+x[1]*dxdth[1]))/ + ipow(xi+eta*(ipow(x[0],2)+ipow(x[1],2)),2)); + */ +} + + +/* Compute the equations for a point z and its partial derivatives dzdph and + dzdth in the Bednorz sphere eversion based on the sphere parameters phi + (longitude) and theta (latitude) and the shape parameters bsp. This + corresponds to equations (8) and (9) in the paper. */ +static void inline bednorz_get_p2(double phi, double theta, + bednorz_shape_par *bsp, double z[3], + double dzdph[3], double dzdth[3]) +{ + double alpha, beta, gamma; + double y[3], dydph[3], dydth[3]; + double y0, y1, y2, y02, y12, y02py12, egy2, apby02py12, amby02py12; + double iapby02py12, iapby02py122, igapby02py12, igapby02py122; + double ambogapb, egy2apby02py12, egy2amby02py12, tbegy2; + double y0dy0dphpy1dy1dph, y0dy0dthpy1dy1dth; + double iy02py12, iy02py122, ty0dy0dphpy1dy1dph, ty0dy0dthpy1dy1dth; + double tbegy2y0dy0dphpy1dy1dph, tbegy2y0dy0dthpy1dy1dth; + double gigapby02py122, gegy2amby02py12; + + alpha = bsp->alpha; + beta = bsp->beta; + gamma = bsp->gamma; + + bednorz_get_p1(phi,theta,bsp,y,dydph,dydth); + + y0 = y[0]; + y1 = y[1]; + y2 = y[2]; + y02 = y0*y0; + y12 = y1*y1; + y02py12 = y02+y12; + y0dy0dphpy1dy1dph = y0*dydph[0]+y1*dydph[1]; + y0dy0dthpy1dy1dth = y0*dydth[0]+y1*dydth[1]; + + if (alpha > 0.0) + { + /* For the north and south poles, the equations in bednorz_get_p0 + and bednorz_get_p1 become singular. Therefore, we include a special + treatment here. Furthermore, we compute the surface normal vector + based on the cross product of the partial derivative vectors in + bednorz_point_normal. For the north and south poles, the partial + derivative vectors are linearly dependent and thus don't yield a + useful normal vector. Therefore, we have to include a special + treatment for the two poles. */ + if (fabs(theta-M_PI/2.0) <= 1.0e-4) + { + z[0] = 0.0; + z[1] = 0.0; + z[2] = -sqrt(alpha/beta)/(alpha+beta); + dzdph[0] = 1.0; + dzdph[1] = 0.0; + dzdph[2] = 0.0; + dzdth[0] = 0.0; + dzdth[1] = 1.0; + dzdth[2] = 0.0; + } + else if (fabs(theta+M_PI/2.0) <= 1.0e-4) + { + z[0] = 0.0; + z[1] = 0.0; + z[2] = -sqrt(alpha/beta)/(alpha+beta); + dzdph[0] = 1.0; + dzdph[1] = 0.0; + dzdph[2] = 0.0; + dzdth[0] = 0.0; + dzdth[1] = -1.0; + dzdth[2] = 0.0; + } + else + { + egy2 = exp(gamma*y2); + apby02py12 = alpha+beta*y02py12; + amby02py12 = alpha-beta*y02py12; + iapby02py12 = 1.0/apby02py12; + iapby02py122 = iapby02py12*iapby02py12; + igapby02py12 = iapby02py12/gamma; + igapby02py122 = igapby02py12*igapby02py12; + ambogapb = (alpha-beta)/(gamma*(alpha+beta)); + egy2apby02py12 = egy2*apby02py12; + egy2amby02py12 = egy2*amby02py12; + tbegy2 = 2.0*beta*egy2; + tbegy2y0dy0dphpy1dy1dph = tbegy2*y0dy0dphpy1dy1dph; + tbegy2y0dy0dthpy1dy1dth = tbegy2*y0dy0dthpy1dy1dth; + gigapby02py122 = gamma*igapby02py122; + gegy2amby02py12 = gamma*egy2amby02py12; + + z[0] = y0*egy2*iapby02py12; + z[1] = y1*egy2*iapby02py12; + z[2] = egy2amby02py12*igapby02py12-ambogapb; + dzdph[0] = ((y0*gamma*dydph[2]+dydph[0])*egy2apby02py12- + y0*tbegy2y0dy0dphpy1dy1dph)*iapby02py122; + dzdph[1] = ((y1*gamma*dydph[2]+dydph[1])*egy2apby02py12- + y1*tbegy2y0dy0dphpy1dy1dph)*iapby02py122; + dzdph[2] = (((gegy2amby02py12*dydph[2]-tbegy2y0dy0dphpy1dy1dph)* + apby02py12-tbegy2y0dy0dphpy1dy1dph*amby02py12)* + gigapby02py122); + dzdth[0] = ((y0*gamma*dydth[2]+dydth[0])*egy2apby02py12- + y0*tbegy2y0dy0dthpy1dy1dth)*iapby02py122; + dzdth[1] = ((y1*gamma*dydth[2]+dydth[1])*egy2apby02py12- + y1*tbegy2y0dy0dthpy1dy1dth)*iapby02py122; + dzdth[2] = (((gegy2amby02py12*dydth[2]-tbegy2y0dy0dthpy1dy1dth)* + apby02py12-tbegy2y0dy0dthpy1dy1dth*amby02py12)* + gigapby02py122); + } + + /* The same formulas written out in full glory: + z[0] = (y[0]*exp(gamma*y[2]))/(alpha+beta*(ipow(y[0],2)+ipow(y[1],2))); + z[1] = (y[1]*exp(gamma*y[2]))/(alpha+beta*(ipow(y[0],2)+ipow(y[1],2))); + z[2] = ((exp(gamma*y[2])*(alpha-beta*(ipow(y[0],2)+ipow(y[1],2))))/ + (gamma*(alpha+beta*(ipow(y[0],2)+ipow(y[1],2))))- + ((alpha-beta)/(gamma*(alpha+beta)))); + dzdph[0] = (((exp(gamma*y[2])*(gamma*y[0]*dydph[2]+dydph[0])* + (alpha+beta*(ipow(y[0],2)+ipow(y[1],2))))- + (2.0*beta*exp(gamma*y[2])*y[0]* + (y[0]*dydph[0]+y[1]*dydph[1])))/ + ipow(alpha+beta*(ipow(y[0],2)+ipow(y[1],2)),2)); + dzdph[1] = (((exp(gamma*y[2])*(gamma*y[1]*dydph[2]+dydph[1])* + (alpha+beta*(ipow(y[0],2)+ipow(y[1],2))))- + (2.0*beta*exp(gamma*y[2])*y[1]* + (y[0]*dydph[0]+y[1]*dydph[1])))/ + ipow(alpha+beta*(ipow(y[0],2)+ipow(y[1],2)),2)); + dzdph[2] = (((gamma*exp(gamma*y[2])* + (alpha-beta*(ipow(y[0],2)+ipow(y[1],2)))*dydph[2]- + 2.0*beta*exp(gamma*y[2])*(y[0]*dydph[0]+y[1]*dydph[1]))* + (gamma*(alpha+beta*(ipow(y[0],2)+ipow(y[1],2))))- + (2.0*gamma*beta*exp(gamma*y[2])* + (alpha-beta*(ipow(y[0],2)+ipow(y[1],2)))* + (y[0]*dydph[0]+y[1]*dydph[1])))/ + ipow(gamma*(alpha+beta*(ipow(y[0],2)+ipow(y[1],2))),2)); + dzdth[0] = (((exp(gamma*y[2])*(gamma*y[0]*dydth[2]+dydth[0])* + (alpha+beta*(ipow(y[0],2)+ipow(y[1],2))))- + (2.0*beta*exp(gamma*y[2])*y[0]* + (y[0]*dydth[0]+y[1]*dydth[1])))/ + ipow(alpha+beta*(ipow(y[0],2)+ipow(y[1],2)),2)); + dzdth[1] = (((exp(gamma*y[2])*(gamma*y[1]*dydth[2]+dydth[1])* + (alpha+beta*(ipow(y[0],2)+ipow(y[1],2))))- + (2.0*beta*exp(gamma*y[2])*y[1]* + (y[0]*dydth[0]+y[1]*dydth[1])))/ + ipow(alpha+beta*(ipow(y[0],2)+ipow(y[1],2)),2)); + dzdth[2] = (((gamma*exp(gamma*y[2])* + (alpha-beta*(ipow(y[0],2)+ipow(y[1],2)))*dydth[2]- + 2.0*beta*exp(gamma*y[2])*(y[0]*dydth[0]+y[1]*dydth[1]))* + (gamma*(alpha+beta*(ipow(y[0],2)+ipow(y[1],2))))- + (2.0*gamma*beta*exp(gamma*y[2])* + (alpha-beta*(ipow(y[0],2)+ipow(y[1],2)))* + (y[0]*dydth[0]+y[1]*dydth[1])))/ + ipow(gamma*(alpha+beta*(ipow(y[0],2)+ipow(y[1],2))),2)); + */ + } + else + { + iy02py12 = 1.0/y02py12; + iy02py122 = iy02py12*iy02py12; + ty0dy0dphpy1dy1dph = 2.0*y0dy0dphpy1dy1dph; + ty0dy0dthpy1dy1dth = 2.0*y0dy0dthpy1dy1dth; + + z[0] = y0*iy02py12; + z[1] = y1*iy02py12; + z[2] = -y2; + + /* We compute the surface normal vector based on the cross product + of the partial derivative vectors in bednorz_point_normal. For the + north and south poles, the partial derivative vectors are linearly + dependent and thus don't yield a useful normal vector. Therefore, + we have to include a special treatment for the two poles. */ + if (fabs(theta-M_PI/2.0) <= 1.0e-4) + { + dzdph[0] = 1.0; + dzdph[1] = 0.0; + dzdph[2] = 0.0; + dzdth[0] = 0.0; + dzdth[1] = 1.0; + dzdth[2] = 0.0; + } + else if (fabs(theta+M_PI/2.0) <= 1.0e-4) + { + dzdph[0] = 1.0; + dzdph[1] = 0.0; + dzdph[2] = 0.0; + dzdth[0] = 0.0; + dzdth[1] = -1.0; + dzdth[2] = 0.0; + } + else + { + dzdph[0] = (dydph[0]*y02py12-y0*ty0dy0dphpy1dy1dph)*iy02py122; + dzdph[1] = (dydph[1]*y02py12-y1*ty0dy0dphpy1dy1dph)*iy02py122; + dzdph[2] = -dydph[2]; + dzdth[0] = (dydth[0]*y02py12-y0*ty0dy0dthpy1dy1dth)*iy02py122; + dzdth[1] = (dydth[1]*y02py12-y1*ty0dy0dthpy1dy1dth)*iy02py122; + dzdth[2] = -dydth[2]; + } + + /* The same formulas written out in full glory: + z[0] = y[0]/(ipow(y[0],2)+ipow(y[1],2)); + z[1] = y[1]/(ipow(y[0],2)+ipow(y[1],2)); + z[2] = -y[2]; + dzdph[0] = ((dydph[0]*(ipow(y[0],2)+ipow(y[1],2))- + 2.0*y[0]*(y[0]*dydph[0]+y[1]*dydph[1]))/ + ipow(ipow(y[0],2)+ipow(y[1],2),2)); + dzdph[1] = ((dydph[1]*(ipow(y[0],2)+ipow(y[1],2))- + 2.0*y[1]*(y[0]*dydph[0]+y[1]*dydph[1]))/ + ipow(ipow(y[0],2)+ipow(y[1],2),2)); + dzdph[2] = -dydph[2]; + dzdth[0] = ((dydth[0]*(ipow(y[0],2)+ipow(y[1],2))- + 2.0*y[0]*(y[0]*dydth[0]+y[1]*dydth[1]))/ + ipow(ipow(y[0],2)+ipow(y[1],2),2)); + dzdth[1] = ((dydth[1]*(ipow(y[0],2)+ipow(y[1],2))- + 2.0*y[1]*(y[0]*dydth[0]+y[1]*dydth[1]))/ + ipow(ipow(y[0],2)+ipow(y[1],2),2)); + dzdth[2] = -dydth[2]; + */ + } +} + + +/* Compute a point p and its surface normal n in the Bednorz sphere + eversion based on the sphere parameters phi (longitude) and theta + (latitude) and the shape parameters bsp. */ +static void inline bednorz_point_normal(double phi, double theta, + bednorz_shape_par *bsp, + float p[3], float n[3]) +{ + double z[3], dzdph[3], dzdth[3]; + float a[3], b[3], t; + float lambda, eps, oz; + int i; + + bednorz_get_p2(phi,theta,bsp,z,dzdph,dzdth); + + for (i=0; i<3; i++) + { + p[i] = z[i]; + a[i] = dzdph[i]; + b[i] = dzdth[i]; + } + + /* In the original version of the Bednorz sphere eversion, the regions + around the north and south poles of the sphere are deformed to points + that lie very close to each other. This leads to a significant amount + of z fighting, especially for higher eversion orders, which is visually + unpleasant. Therefore, we modify the shape of the deformed sphere very + slightly to avoid or at least ameliorate the z fighting. */ + lambda = bsp->lambda; + eps = bsp->eps; + if (lambda == 1.0) + oz = eps*sin(theta); + else + oz = 0.0f; + p[2] += oz; + + cross(a,b,n); + t = 1.0f/sqrtf(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]); + n[0] *= t; + n[1] *= t; + n[2] *= t; +} + + +/* Set up the surface colors for the main pass (i.e., index 0). */ +static void setup_surface_colors(ModeInfo *mi, float phi_min, float phi_max, + float theta_min, float theta_max, + int num_phi, int num_theta) +{ + int i, j, o; + float matc[3][3]; + float phi, theta, phi_range, theta_range; + sphereeversionstruct *se = &sphereeversion[MI_SCREEN(mi)]; + + if (se->colors[0] != COLORS_TWOSIDED) + { + /* Compute the rotation that rotates the color basis vectors. */ + for (i=0; i<3; i++) + for (j=0; j<3; j++) + matc[i][j] = (i==j); + rotatez(matc,-26.565051177078015); + rotatey(matc,-335.90515744788928); + rotatex(matc,-50.768479516407787); + + phi_range = phi_max-phi_min; + theta_range = theta_max-theta_min; + for (j=0; j<=num_phi; j++) + { + phi = phi_range*j/num_phi+phi_min; + for (i=0; i<=num_theta; i++) + { + o = j*(num_theta+1)+i; + theta = theta_range*i/num_theta+theta_min; + if (se->colors[0] == COLORS_PARALLEL) + color(se,(2.0f*theta+M_PI)*(2.0f/3.0f),matc,&se->colf[0][4*o], + &se->colb[0][4*o]); + else /* se->colors[0] == COLORS_MERIDIAN */ + color(se,phi+M_PI,matc,&se->colf[0][4*o],&se->colb[0][4*o]); + } + } + } +#ifdef VERTEXATTRIBARRAY_WORKAROUND + else /* se->colors[0] == COLORS_TWOSIDED */ + { + /* For some strange reason, the color buffer must be initialized + and used on macOS. Otherwise two-sided lighting will not + work. */ + int k; + for (j=0; j<=num_phi; j++) + { + for (i=0; i<=num_theta; i++) + { + o = j*(num_theta+1)+i; + for (k=0; k<4; k++) + { + se->colf[0][4*o+k] = 1.0f; + se->colb[0][4*o+k] = 1.0f; + } + } + } + } +#endif /* VERTEXATTRIBARRAY_WORKAROUND */ + +#ifdef HAVE_GLSL + if (se->use_shaders) + { + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_colorf_buffer[0]); + glBufferData(GL_ARRAY_BUFFER, + 4*(num_phi+1)*(num_theta+1)*sizeof(GLfloat), + se->colf[0],GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER,0); + + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_colorb_buffer[0]); + glBufferData(GL_ARRAY_BUFFER, + 4*(num_phi+1)*(num_theta+1)*sizeof(GLfloat), + se->colb[0],GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER,0); + } +#endif /* HAVE_GLSL */ +} + + +/* Draw the Bednorz sphere eversion using OpenGL's fixed + functionality. */ +static int bednorz_sphere_eversion_ff(ModeInfo *mi, float phi_min, + float phi_max, float theta_min, + float theta_max, int num_phi, + int num_theta, int numb_dist, + int numb_dir, int num_grid) +{ + 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 }; + static const GLfloat mat_diff_front[] = { 1.0, 0.0, 0.0, 1.0 }; + static const GLfloat mat_diff_back[] = { 0.0, 1.0, 0.0, 1.0 }; + static const GLfloat mat_diff_trans_front[] = { 1.0, 0.0, 0.0, 0.7 }; + static const GLfloat mat_diff_trans_back[] = { 0.0, 1.0, 0.0, 0.7 }; + float p[3], n[3], mat[3][3]; + int i, j, k, l, m, o; + int numb_dist_mask, numb_dist_min, numb_dist_max; + int numb_dir_mask, numb_dir_min, numb_dir_max; + float phi, theta, phi_range, theta_range; + float *xx, *xn, *cf, *cb; + float a, b, c, d, e, tau, tau_min, tau_max, r, s; + float x, y, z, zmin, zmax, rmax, scale, offset_z; + bednorz_shape_par bsp; + float qu[4], r1[3][3], r2[3][3]; + sphereeversionstruct *se = &sphereeversion[MI_SCREEN(mi)]; + int polys; + + tau = se->tau; + /* Apply easing functions to the different ranges of tau. */ + if (fabsf(tau) <= BEDNORZ_TAU4) + { + if (fabsf(tau) <= BEDNORZ_TAU1) + { + tau_min = 0.0f; + tau_max = BEDNORZ_TAU1; + } + else + if (fabsf(tau) <= BEDNORZ_TAU2) + { + tau_min = BEDNORZ_TAU1; + tau_max = BEDNORZ_TAU2; + } + else if (fabsf(tau) <= BEDNORZ_TAU3) + { + tau_min = BEDNORZ_TAU2; + tau_max = BEDNORZ_TAU3; + } + else /* fabsf(tau) <= BEDNORZ_TAU4 */ + { + tau_min = BEDNORZ_TAU3; + tau_max = BEDNORZ_TAU4; + } + e = 1.0f/(tau_min*tau_min-2.0f*tau_min*tau_max+tau_max*tau_max); + a = -2.0f*e; + b = 3.0f*(tau_min+tau_max)*e; + c = -6.0f*tau_min*tau_max*e; + d = tau_min*tau_max*(tau_min+tau_max)*e; + if (tau >= 0.0f) + tau = ((a*tau+b)*tau+c)*tau+d; + else + tau = ((a*tau-b)*tau+c)*tau-d; + } + /* Set up the shape parameters. */ + bsp.n = se->g; + bsp.kappa = bednorz_get_kappa(bsp.n); + bsp.omega = BEDNORZ_OMEGA; + bsp.t = bednorz_get_t(tau); + bsp.p = bednorz_get_p(tau); + bsp.q = bednorz_get_q(tau); + bsp.xi = bednorz_get_xi(tau); + bsp.eta = bednorz_get_eta(tau,se->eta_min); + bsp.alpha = bednorz_get_alpha(tau); + bsp.beta = bednorz_get_beta(tau,se->beta_max); + bsp.gamma = bednorz_get_gamma(bsp.alpha,bsp.beta); + bsp.lambda = bednorz_get_lambda(tau); + bsp.eps = bednorz_get_eps(tau,bsp.n); + + /* Compute the surface points and normals. */ + phi_range = phi_max-phi_min; + theta_range = theta_max-theta_min; + for (j=0; j<=num_phi; j++) + { + phi = phi_range*j/num_phi+phi_min; + for (i=0; i<=num_theta; i++) + { + o = j*(num_theta+1)+i; + theta = theta_range*i/num_theta+theta_min; + bednorz_point_normal(phi,theta,&bsp,&se->se[3*o],&se->sen[3*o]); + } + } + + /* Compute the z offset. */ + zmin = FLT_MAX; + zmax = -FLT_MAX; + for (j=0; j<=num_phi; j++) + { + for (i=0; i<=num_theta; i++) + { + o = j*(num_theta+1)+i; + z = se->se[3*o+2]; + if (z < zmin) + zmin = z; + if (z > zmax) + zmax = z; + } + } + offset_z = -0.5f*(zmin+zmax); + + /* Shift the surface in the z direction and compute the scale. */ + rmax = -FLT_MAX; + for (j=0; j<=num_phi; j++) + { + for (i=0; i<=num_theta; i++) + { + o = j*(num_theta+1)+i; + se->se[3*o+2] += offset_z; + x = se->se[3*o]; + y = se->se[3*o+1]; + z = se->se[3*o+2]; + r = x*x+y*y+z*z; + if (r > rmax) + rmax = r; + } + } + scale = 0.75f/sqrtf(rmax); + + /* Scale the surface. */ + for (j=0; j<=num_phi; j++) + { + for (i=0; i<=num_theta; i++) + { + o = j*(num_theta+1)+i; + se->se[3*o] *= scale; + se->se[3*o+1] *= scale; + se->se[3*o+2] *= scale; + } + } + + /* Compute the rotation that rotates the surface in 3D, including the + trackball rotations. */ + rotateall(se->alpha,se->beta,se->delta,r1); + + gltrackball_get_quaternion(se->trackball,qu); + quat_to_rotmat(qu,r2); + + mult_rotmat(r2,r1,mat); + + numb_dist_mask = numb_dist-1; + numb_dist_min = numb_dist/4; + numb_dist_max = 3*numb_dist/4; + numb_dir_mask = numb_dir-1; + numb_dir_min = numb_dir/4; + numb_dir_max = 3*numb_dir/4; + + glClearColor(0.0f,0.0f,0.0f,1.0f); + glClearDepth(1.0f); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + if (se->projection == DISP_PERSPECTIVE) + { + gluPerspective(60.0,se->aspect,0.1,10.0); + } + else + { + if (se->aspect >= 1.0) + glOrtho(-se->aspect,se->aspect,-1.0,1.0,0.1,10.0); + else + glOrtho(-1.0,1.0,-1.0/se->aspect,1.0/se->aspect,0.1,10.0); + } + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + if (se->display_mode[0] == DISP_SURFACE) + { + glDisable(GL_CULL_FACE); + 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 /* se->display_mode[0] == DISP_TRANSPARENT */ + { + glDisable(GL_CULL_FACE); + 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); + } + + if (se->colors[0] == COLORS_TWOSIDED) + { + glColor3fv(mat_diff_front); + if (se->display_mode[0] == DISP_TRANSPARENT) + { + glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_front); + glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_back); + } + else + { + glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_front); + glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_back); + } + } + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0f,1.0f); + + if (se->appearance[0] == APPEARANCE_PARALLEL_BANDS) + { + for (i=0; i<num_theta; i++) + { + if (((i & numb_dist_mask) >= numb_dist_min) && + ((i & numb_dist_mask) < numb_dist_max)) + continue; + glBegin(GL_TRIANGLE_STRIP); + for (j=num_phi; j>=0; j--) + { + for (k=0; k<=1; k++) + { + l = i+k; + m = j; + o = m*(num_theta+1)+l; + phi = phi_range*m/num_phi+phi_min; + theta = theta_range*l/num_theta+theta_min; + xx = &se->se[3*o]; + xn = &se->sen[3*o]; + for (l=0; l<3; l++) + { + r = 0.0f; + s = 0.0f; + for (m=0; m<3; m++) + { + r += mat[l][m]*xx[m]; + s += mat[l][m]*xn[m]; + } + p[l] = r+se->offset3d[l]; + n[l] = s; + } + if (se->colors[0] != COLORS_TWOSIDED) + { + cf = &se->colf[0][4*o]; + cb = &se->colb[0][4*o]; + glColor3fv(cf); + glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,cf); + glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,cb); + } + glNormal3fv(n); + glVertex3fv(p); + } + } + glEnd(); + } + polys = num_theta*(num_phi+1); + } + else /* se->appearance[0] != APPEARANCE_PARALLEL_BANDS */ + { + for (j=0; j<num_phi; j++) + { + if (se->appearance[0] == APPEARANCE_MERIDIAN_BANDS && + ((j & numb_dir_mask) >= numb_dir_min) && + ((j & numb_dir_mask) < numb_dir_max)) + continue; + glBegin(GL_TRIANGLE_STRIP); + for (i=0; i<=num_theta; i++) + { + for (k=0; k<=1; k++) + { + l = i; + m = j+k; + o = m*(num_theta+1)+l; + phi = phi_range*m/num_phi+phi_min; + theta = theta_range*l/num_theta+theta_min; + xx = &se->se[3*o]; + xn = &se->sen[3*o]; + for (l=0; l<3; l++) + { + r = 0.0f; + s = 0.0f; + for (m=0; m<3; m++) + { + r += mat[l][m]*xx[m]; + s += mat[l][m]*xn[m]; + } + p[l] = r+se->offset3d[l]; + n[l] = s; + } + if (se->colors[0] != COLORS_TWOSIDED) + { + cf = &se->colf[0][4*o]; + cb = &se->colb[0][4*o]; + glColor3fv(cf); + glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,cf); + glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,cb); + } + glNormal3fv(n); + glVertex3fv(p); + } + } + glEnd(); + } + polys = 2*num_phi*(num_theta+1); + if (se->appearance[0] == APPEARANCE_MERIDIAN_BANDS) + polys /= 2; + } + + glDisable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(0.0f,0.0f); + if (se->graticule[0]) + { + glColor4f(1.0f,1.0f,1.0f,1.0f); + glLineWidth(2.0f); + glEnable(GL_LINE_SMOOTH); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_LIGHTING); + glDisable(GL_LIGHT0); + glEnable(GL_BLEND); + glHint(GL_LINE_SMOOTH_HINT,GL_DONT_CARE); + /* Draw meridians. */ + for (j=0; j<num_phi; j+=num_grid) + { + glBegin(GL_LINE_STRIP); + for (i=0; i<=num_theta; i++) + { + o = j*(num_theta+1)+i; + phi = phi_range*j/num_phi+phi_min; + theta = theta_range*i/num_theta+theta_min; + xx = &se->se[3*o]; + for (l=0; l<3; l++) + { + r = 0.0f; + for (m=0; m<3; m++) + r += mat[l][m]*xx[m]; + p[l] = r+se->offset3d[l]; + } + glVertex3fv(p); + } + glEnd(); + } + /* Draw parallels. */ + for (i=num_grid; i<=num_theta-num_grid; i+=num_grid) + { + glBegin(GL_LINE_LOOP); + for (j=num_phi-1; j>=0; j--) + { + o = j*(num_theta+1)+i; + phi = phi_range*j/num_phi+phi_min; + theta = theta_range*i/num_theta+theta_min; + xx = &se->se[3*o]; + for (l=0; l<3; l++) + { + r = 0.0f; + for (m=0; m<3; m++) + r += mat[l][m]*xx[m]; + p[l] = r+se->offset3d[l]; + } + glVertex3fv(p); + } + glEnd(); + } + glLineWidth(1.0f); + glPolygonOffset(0.0f,0.0f); + glDisable(GL_LINE_SMOOTH); + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + if (se->display_mode[0] != DISP_TRANSPARENT) + glDisable(GL_BLEND); + } + + return polys; +} + + +#ifdef HAVE_GLSL + +/* Draw the Bednorz sphere eversion using OpenGL's programmable + functionality. */ +static int bednorz_sphere_eversion_pf(ModeInfo *mi, float phi_min, + float phi_max, float theta_min, + float theta_max, int num_phi, + int num_theta, int numb_dist, + int numb_dir, int num_grid) +{ + static const GLfloat light_model_ambient[] = { 0.2, 0.2, 0.2, 1.0 }; + 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 }; + static const GLfloat mat_diff_front[] = { 1.0, 0.0, 0.0, 1.0 }; + static const GLfloat mat_diff_back[] = { 0.0, 1.0, 0.0, 1.0 }; + static const GLfloat mat_diff_trans_front[] = { 1.0, 0.0, 0.0, 0.7 }; + static const GLfloat mat_diff_trans_back[] = { 0.0, 1.0, 0.0, 0.7 }; + static const GLfloat mat_diff_white[] = { 1.0, 1.0, 1.0, 1.0 }; + static const GLuint blend_indices[6] = { 0, 1, 2, 2, 3, 0 }; + static const GLfloat blend_p[4][2] = + { { -1.0, -1.0 }, { 1.0, -1.0 }, { 1.0, 1.0 }, { -1.0, 1.0 } }; + static const GLfloat blend_t[4][2] = + { { 0.0, 0.0 }, { 1.0, 0.0 }, { 1.0, 1.0 }, { 0.0, 1.0 } }; + GLfloat light_direction[3], half_vector[3], len; + GLfloat p_mat[16], mv_mat[16], rot_mat[16]; + float mat[3][3]; + int i, j, k, l, m, o, pass, num_passes, ni; + int numb_dist_mask, numb_dist_min, numb_dist_max; + int numb_dir_mask, numb_dir_min, numb_dir_max; + float phi, theta, phi_range, theta_range; + float a, b, c, d, e, tau, tau_min, tau_max, r, t; + float x, y, z, zmin, zmax, rmax, scale, offset_z; + bednorz_shape_par bsp; + float qu[4], r1[3][3], r2[3][3]; + GLint draw_buf, read_buf; + GLsizeiptr index_offset; + sphereeversionstruct *se = &sphereeversion[MI_SCREEN(mi)]; + int polys = 0; + + if (!se->use_shaders) + return 0; + + numb_dist_mask = numb_dist-1; + numb_dist_min = numb_dist/4; + numb_dist_max = 3*numb_dist/4; + numb_dir_mask = numb_dir-1; + numb_dir_min = numb_dir/4; + numb_dir_max = 3*numb_dir/4; + + if (!se->buffers_initialized) + { + /* The indices only need to be computed once. */ + /* Compute the solid indices. */ + ni = 0; + se->num_solid_strips = 0; + se->num_solid_triangles = 0; + for (j=0; j<num_phi; j++) + { + for (i=0; i<=num_theta; i++) + { + for (k=0; k<=1; k++) + { + l = i; + m = j+k; + o = m*(num_theta+1)+l; + se->solid_indices[ni++] = o; + } + } + se->num_solid_strips++; + } + se->num_solid_triangles = 2*(num_theta+1); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,se->solid_indices_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER,ni*sizeof(GLuint), + se->solid_indices,GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + + /* Compute the meridian indices. */ + ni = 0; + se->num_meridian_strips = 0; + se->num_meridian_triangles = 0; + for (j=0; j<num_phi; j++) + { + if (((j & numb_dir_mask) >= numb_dir_min) && + ((j & numb_dir_mask) < numb_dir_max)) + continue; + for (i=0; i<=num_theta; i++) + { + for (k=0; k<=1; k++) + { + l = i; + m = j+k; + o = m*(num_theta+1)+l; + se->meridian_indices[ni++] = o; + } + } + se->num_meridian_strips++; + } + se->num_meridian_triangles = 2*(num_theta+1); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,se->meridian_indices_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER,ni*sizeof(GLuint), + se->meridian_indices,GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + + /* Compute the parallel indices. */ + ni = 0; + se->num_parallel_strips = 0; + se->num_parallel_triangles = 0; + for (i=0; i<num_theta; i++) + { + if (((i & numb_dist_mask) >= numb_dist_min) && + ((i & numb_dist_mask) < numb_dist_max)) + continue; + for (j=num_phi; j>=0; j--) + { + for (k=0; k<=1; k++) + { + l = i+k; + m = j; + o = m*(num_theta+1)+l; + se->parallel_indices[ni++] = o; + } + } + se->num_parallel_strips++; + } + se->num_parallel_triangles = 2*(num_phi+1); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,se->parallel_indices_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER,ni*sizeof(GLuint), + se->parallel_indices,GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + + /* Compute the line indices. */ + ni = 0; + for (j=0; j<num_phi; j+=num_grid) + { + for (i=0; i<num_theta; i++) + { + se->line_indices[ni++] = j*(num_theta+1)+i; + se->line_indices[ni++] = j*(num_theta+1)+i+1; + } + } + for (i=num_grid; i<=num_theta-num_grid; i+=num_grid) + { + for (j=num_phi; j>0; j--) + { + o = j*(num_theta+1)+i; + se->line_indices[ni++] = j*(num_theta+1)+i; + se->line_indices[ni++] = (j-1)*(num_theta+1)+i; + } + } + se->num_lines = ni; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,se->line_indices_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER,ni*sizeof(GLuint), + se->line_indices,GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + + se->buffers_initialized = True; + } + + tau = se->tau; + /* Apply easing functions to the different ranges of tau. */ + if (fabsf(tau) <= BEDNORZ_TAU4) + { + if (fabsf(tau) <= BEDNORZ_TAU1) + { + tau_min = 0.0f; + tau_max = BEDNORZ_TAU1; + } + else + if (fabsf(tau) <= BEDNORZ_TAU2) + { + tau_min = BEDNORZ_TAU1; + tau_max = BEDNORZ_TAU2; + } + else if (fabsf(tau) <= BEDNORZ_TAU3) + { + tau_min = BEDNORZ_TAU2; + tau_max = BEDNORZ_TAU3; + } + else /* fabsf(tau) <= BEDNORZ_TAU4 */ + { + tau_min = BEDNORZ_TAU3; + tau_max = BEDNORZ_TAU4; + } + e = 1.0f/(tau_min*tau_min-2.0f*tau_min*tau_max+tau_max*tau_max); + a = -2.0f*e; + b = 3.0f*(tau_min+tau_max)*e; + c = -6.0f*tau_min*tau_max*e; + d = tau_min*tau_max*(tau_min+tau_max)*e; + if (tau >= 0.0f) + tau = ((a*tau+b)*tau+c)*tau+d; + else + tau = ((a*tau-b)*tau+c)*tau-d; + } + /* Set up the shape parameters. */ + bsp.n = se->g; + bsp.kappa = bednorz_get_kappa(bsp.n); + bsp.omega = BEDNORZ_OMEGA; + bsp.t = bednorz_get_t(tau); + bsp.p = bednorz_get_p(tau); + bsp.q = bednorz_get_q(tau); + bsp.xi = bednorz_get_xi(tau); + bsp.eta = bednorz_get_eta(tau,se->eta_min); + bsp.alpha = bednorz_get_alpha(tau); + bsp.beta = bednorz_get_beta(tau,se->beta_max); + bsp.gamma = bednorz_get_gamma(bsp.alpha,bsp.beta); + bsp.lambda = bednorz_get_lambda(tau); + bsp.eps = bednorz_get_eps(tau,bsp.n); + + /* Compute the surface points and normals. */ + phi_range = phi_max-phi_min; + theta_range = theta_max-theta_min; + for (j=0; j<=num_phi; j++) + { + phi = phi_range*j/num_phi+phi_min; + for (i=0; i<=num_theta; i++) + { + o = j*(num_theta+1)+i; + theta = theta_range*i/num_theta+theta_min; + bednorz_point_normal(phi,theta,&bsp,&se->se[3*o],&se->sen[3*o]); + } + } + + /* Compute the z offset. */ + zmin = FLT_MAX; + zmax = -FLT_MAX; + for (j=0; j<=num_phi; j++) + { + for (i=0; i<=num_theta; i++) + { + o = j*(num_theta+1)+i; + z = se->se[3*o+2]; + if (z < zmin) + zmin = z; + if (z > zmax) + zmax = z; + } + } + offset_z = -0.5f*(zmin+zmax); + + /* Shift the surface in the z direction and compute the scale. */ + rmax = -FLT_MAX; + for (j=0; j<=num_phi; j++) + { + for (i=0; i<=num_theta; i++) + { + o = j*(num_theta+1)+i; + se->se[3*o+2] += offset_z; + x = se->se[3*o]; + y = se->se[3*o+1]; + z = se->se[3*o+2]; + r = x*x+y*y+z*z; + if (r > rmax) + rmax = r; + } + } + scale = 0.75f/sqrtf(rmax); + + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_pos_buffer); + glBufferData(GL_ARRAY_BUFFER,3*(num_phi+1)*(num_theta+1)*sizeof(GLfloat), + se->se,GL_STREAM_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_normal_buffer); + glBufferData(GL_ARRAY_BUFFER,3*(num_phi+1)*(num_theta+1)*sizeof(GLfloat), + se->sen,GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER,0); + + /* Compute the rotation that rotates the surface in 3D, including the + trackball rotations. */ + rotateall(se->alpha,se->beta,se->delta,r1); + + gltrackball_get_quaternion(se->trackball,qu); + quat_to_rotmat(qu,r2); + + mult_rotmat(r2,r1,mat); + + glsl_Identity(p_mat); + if (se->projection == DISP_PERSPECTIVE) + { + glsl_Perspective(p_mat,60.0f,se->aspect,0.1f,10.0f); + } + else + { + if (se->aspect >= 1.0) + glsl_Orthographic(p_mat,-se->aspect,se->aspect,-1.0f,1.0f, + 0.1f,10.0f); + else + glsl_Orthographic(p_mat,-1.0f,1.0f,-1.0f/se->aspect,1.0f/se->aspect, + 0.1f,10.0f); + } + glsl_Identity(rot_mat); + for (i=0; i<3; i++) + for (j=0; j<3; j++) + rot_mat[GLSL__LINCOOR(i,j,4)] = mat[i][j]; + glsl_Identity(mv_mat); + glsl_Translate(mv_mat,se->offset3d[0],se->offset3d[1],se->offset3d[2]); + glsl_Scale(mv_mat,scale,scale,scale); + glsl_MultMatrix(mv_mat,rot_mat); + + len = sqrtf(light_position[0]*light_position[0]+ + light_position[1]*light_position[1]+ + light_position[2]*light_position[2]); + light_direction[0] = light_position[0]/len; + light_direction[1] = light_position[1]/len; + light_direction[2] = light_position[2]/len; + half_vector[0] = light_direction[0]; + half_vector[1] = light_direction[1]; + half_vector[2] = light_direction[2]+1.0f; + len = sqrtf(half_vector[0]*half_vector[0]+ + half_vector[1]*half_vector[1]+ + half_vector[2]*half_vector[2]); + half_vector[0] /= len; + half_vector[1] /= len; + half_vector[2] /= len; + + num_passes = se->anim_state == ANIM_TURN ? 2 : 1; + + for (pass=0; pass<num_passes; pass++) + { + glUseProgram(se->poly_shader_program); + + glClearColor(0.0f,0.0f,0.0f,1.0f); + glClearDepth(1.0f); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + glUniformMatrix4fv(se->poly_mv_index,1,GL_FALSE,mv_mat); + glUniformMatrix4fv(se->poly_proj_index,1,GL_FALSE,p_mat); + + glUniform4fv(se->poly_front_ambient_index,1,mat_diff_white); + glUniform4fv(se->poly_front_diffuse_index,1,mat_diff_white); + glUniform4fv(se->poly_back_ambient_index,1,mat_diff_white); + glUniform4fv(se->poly_back_diffuse_index,1,mat_diff_white); + glVertexAttrib4f(se->poly_colorf_index,1.0f,1.0f,1.0f,1.0f); + glVertexAttrib4f(se->poly_colorb_index,1.0f,1.0f,1.0f,1.0f); + + if (se->display_mode[pass] == DISP_SURFACE) + { + glDisable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); + glUniform4fv(se->poly_glbl_ambient_index,1,light_model_ambient); + glUniform4fv(se->poly_lt_ambient_index,1,light_ambient); + glUniform4fv(se->poly_lt_diffuse_index,1,light_diffuse); + glUniform4fv(se->poly_lt_specular_index,1,light_specular); + glUniform3fv(se->poly_lt_direction_index,1,light_direction); + glUniform3fv(se->poly_lt_halfvect_index,1,half_vector); + glUniform4fv(se->poly_specular_index,1,mat_specular); + glUniform1f(se->poly_shininess_index,50.0f); + } + else /* se->display_mode[pass] == DISP_TRANSPARENT */ + { + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + glUniform4fv(se->poly_glbl_ambient_index,1,light_model_ambient); + glUniform4fv(se->poly_lt_ambient_index,1,light_ambient); + glUniform4fv(se->poly_lt_diffuse_index,1,light_diffuse); + glUniform4fv(se->poly_lt_specular_index,1,light_specular); + glUniform3fv(se->poly_lt_direction_index,1,light_direction); + glUniform3fv(se->poly_lt_halfvect_index,1,half_vector); + glUniform4fv(se->poly_specular_index,1,mat_specular); + glUniform1f(se->poly_shininess_index,50.0f); + } + + if (se->colors[pass] == COLORS_TWOSIDED) + { + if (se->display_mode[pass] == DISP_TRANSPARENT) + { + glUniform4fv(se->poly_front_ambient_index,1,mat_diff_trans_front); + glUniform4fv(se->poly_front_diffuse_index,1,mat_diff_trans_front); + glUniform4fv(se->poly_back_ambient_index,1,mat_diff_trans_back); + glUniform4fv(se->poly_back_diffuse_index,1,mat_diff_trans_back); + } + else + { + glUniform4fv(se->poly_front_ambient_index,1,mat_diff_front); + glUniform4fv(se->poly_front_diffuse_index,1,mat_diff_front); + glUniform4fv(se->poly_back_ambient_index,1,mat_diff_back); + glUniform4fv(se->poly_back_diffuse_index,1,mat_diff_back); + } + } + + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0f,1.0f); + + glEnableVertexAttribArray(se->poly_pos_index); + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_pos_buffer); + glVertexAttribPointer(se->poly_pos_index,3,GL_FLOAT,GL_FALSE,0,0); + + glEnableVertexAttribArray(se->poly_normal_index); + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_normal_buffer); + glVertexAttribPointer(se->poly_normal_index,3,GL_FLOAT,GL_FALSE,0,0); + + if (se->colors[pass] != COLORS_TWOSIDED) + { + glEnableVertexAttribArray(se->poly_colorf_index); + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_colorf_buffer[pass]); + glVertexAttribPointer(se->poly_colorf_index,4,GL_FLOAT,GL_FALSE,0,0); + + glEnableVertexAttribArray(se->poly_colorb_index); + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_colorb_buffer[pass]); + glVertexAttribPointer(se->poly_colorb_index,4,GL_FLOAT,GL_FALSE,0,0); + } +#ifdef VERTEXATTRIBARRAY_WORKAROUND + else /* se->colors[pass] == COLORS_TWOSIDED */ + { + glEnableVertexAttribArray(se->poly_colorf_index); + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_colorf_buffer[pass]); + glVertexAttribPointer(se->poly_colorf_index,4,GL_FLOAT,GL_FALSE,0,0); + + glEnableVertexAttribArray(se->poly_colorb_index); + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_colorb_buffer[pass]); + glVertexAttribPointer(se->poly_colorb_index,4,GL_FLOAT,GL_FALSE,0,0); + } +#endif /* VERTEXATTRIBARRAY_WORKAROUND */ + + if (se->appearance[pass] == APPEARANCE_SOLID) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,se->solid_indices_buffer); + for (i=0; i<se->num_solid_strips; i++) + { + index_offset = se->num_solid_triangles*i*sizeof(GLuint); + glDrawElements(GL_TRIANGLE_STRIP,se->num_solid_triangles, + GL_UNSIGNED_INT,(const GLvoid *)index_offset); + } + polys += 2*num_phi*(num_theta+1); + } + else if (se->appearance[pass] == APPEARANCE_PARALLEL_BANDS) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,se->parallel_indices_buffer); + for (i=0; i<se->num_parallel_strips; i++) + { + index_offset = se->num_parallel_triangles*i*sizeof(GLuint); + glDrawElements(GL_TRIANGLE_STRIP,se->num_parallel_triangles, + GL_UNSIGNED_INT,(const GLvoid *)index_offset); + } + polys += num_theta*(num_phi+1); + } + else /* se->appearance[pass] == APPEARANCE_MERIDIAN_BANDS */ + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,se->meridian_indices_buffer); + for (i=0; i<se->num_meridian_strips; i++) + { + index_offset = se->num_meridian_triangles*i*sizeof(GLuint); + glDrawElements(GL_TRIANGLE_STRIP,se->num_meridian_triangles, + GL_UNSIGNED_INT,(const GLvoid *)index_offset); + } + polys += num_phi*(num_theta+1); + } + + glDisableVertexAttribArray(se->poly_pos_index); + glDisableVertexAttribArray(se->poly_normal_index); + if (se->colors[pass] != COLORS_TWOSIDED) + { + glDisableVertexAttribArray(se->poly_colorf_index); + glDisableVertexAttribArray(se->poly_colorb_index); + } +#ifdef VERTEXATTRIBARRAY_WORKAROUND + else /* se->colors[pass] != COLORS_TWOSIDED */ + { + glDisableVertexAttribArray(se->poly_colorf_index); + glDisableVertexAttribArray(se->poly_colorb_index); + } +#endif /* VERTEXATTRIBARRAY_WORKAROUND */ + glBindBuffer(GL_ARRAY_BUFFER,0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + + glDisable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(0.0f,0.0f); + + glUseProgram(0); + + if (se->graticule[pass]) + { + glUseProgram(se->line_shader_program); + + glUniformMatrix4fv(se->line_mv_index,1,GL_FALSE,mv_mat); + glUniformMatrix4fv(se->line_proj_index,1,GL_FALSE,p_mat); + glUniform4f(se->line_color_index,1.0f,1.0f,1.0f,1.0f); + + glLineWidth(2.0f); + + glEnableVertexAttribArray(se->line_pos_index); + glBindBuffer(GL_ARRAY_BUFFER,se->vertex_pos_buffer); + glVertexAttribPointer(se->line_pos_index,3,GL_FLOAT,GL_FALSE,0,0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,se->line_indices_buffer); + + index_offset = 0; + glDrawElements(GL_LINES,se->num_lines,GL_UNSIGNED_INT, + (const void *)index_offset); + + glDisableVertexAttribArray(se->line_pos_index); + glBindBuffer(GL_ARRAY_BUFFER,0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + + glUseProgram(0); + } + + if (num_passes == 2) + { + /* Copy the rendered image to a texture. */ + glGetIntegerv(GL_DRAW_BUFFER0,&draw_buf); + glGetIntegerv(GL_READ_BUFFER,&read_buf); + glReadBuffer(draw_buf); + glBindTexture(GL_TEXTURE_2D,se->color_textures[pass]); + glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,se->WindW,se->WindH); + glReadBuffer(read_buf); + } + } + + if (num_passes == 2) + { + t = (float)se->turn_step/(float)se->num_turn; + /* Apply an easing function to t. */ + t = (3.0-2.0*t)*t*t; + + glUseProgram(se->blend_shader_program); + + glClearColor(0.0f,0.0f,0.0f,1.0f); + glClearDepth(1.0f); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_BLEND); + + glUniform1f(se->blend_t_index,t); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D,se->color_textures[0]); + glUniform1i(se->blend_sampler0_index,0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D,se->color_textures[1]); + glUniform1i(se->blend_sampler1_index,1); + + glEnableVertexAttribArray(se->blend_vertex_p_index); + glVertexAttribPointer(se->blend_vertex_p_index,2,GL_FLOAT,GL_FALSE, + 2*sizeof(GLfloat),blend_p); + + glEnableVertexAttribArray(se->blend_vertex_t_index); + glVertexAttribPointer(se->blend_vertex_t_index,2,GL_FLOAT,GL_FALSE, + 2*sizeof(GLfloat),blend_t); + + glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,blend_indices); + + glActiveTexture(GL_TEXTURE0); + + glUseProgram(0); + } + + return polys; +} + + +static void init_glsl(ModeInfo *mi) +{ + sphereeversionstruct *se = &sphereeversion[MI_SCREEN(mi)]; + GLint gl_major, gl_minor, glsl_major, glsl_minor; + GLboolean gl_gles3; + int i, wt, ht; + const GLchar *poly_vertex_shader_source[3]; + const GLchar *poly_fragment_shader_source[4]; + const GLchar *line_vertex_shader_source[3]; + const GLchar *line_fragment_shader_source[4]; + const GLchar *blend_vertex_shader_source[3]; + const GLchar *blend_fragment_shader_source[4]; + + se->solid_indices = calloc(2*NUMPH*(NUMTH+1),sizeof(float)); + se->parallel_indices = calloc(NUMPH*(NUMTH+1),sizeof(float)); + se->meridian_indices = calloc((NUMPH+1)*NUMTH,sizeof(float)); + se->line_indices = calloc(4*NUMPH*NUMTH/NUMGRID,sizeof(float)); + + /* Determine whether to use shaders to render the sphere eversion. */ + se->use_shaders = False; + se->buffers_initialized = False; + se->poly_shader_program = 0; + se->line_shader_program = 0; + se->blend_shader_program = 0; + se->max_tex_size = -1; + + if (!glsl_GetGlAndGlslVersions(&gl_major,&gl_minor,&glsl_major,&glsl_minor, + &gl_gles3)) + return; + if (!gl_gles3) + { + if (gl_major < 3 || + (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 30))) + { + if ((gl_major < 2 || (gl_major == 2 && gl_minor < 1)) || + (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 20))) + return; + /* We have at least OpenGL 2.1 and at least GLSL 1.20. */ + poly_vertex_shader_source[0] = shader_version_2_1; + poly_vertex_shader_source[1] = poly_vertex_shader_attribs_2_1; + poly_vertex_shader_source[2] = poly_vertex_shader_main; + poly_fragment_shader_source[0] = shader_version_2_1; + poly_fragment_shader_source[1] = poly_fragment_shader_attribs_2_1; + poly_fragment_shader_source[2] = poly_fragment_shader_main; + poly_fragment_shader_source[3] = poly_fragment_shader_out_2_1; + + line_vertex_shader_source[0] = shader_version_2_1; + line_vertex_shader_source[1] = line_vertex_shader_attribs_2_1; + line_vertex_shader_source[2] = line_vertex_shader_main; + line_fragment_shader_source[0] = shader_version_2_1; + line_fragment_shader_source[1] = line_fragment_shader_attribs_2_1; + line_fragment_shader_source[2] = line_fragment_shader_main; + line_fragment_shader_source[3] = line_fragment_shader_out_2_1; + + blend_vertex_shader_source[0] = shader_version_2_1; + blend_vertex_shader_source[1] = blend_vertex_shader_attribs_2_1; + blend_vertex_shader_source[2] = blend_vertex_shader_main; + blend_fragment_shader_source[0] = shader_version_2_1; + blend_fragment_shader_source[1] = blend_fragment_shader_attribs_2_1; + blend_fragment_shader_source[2] = blend_fragment_shader_main; + blend_fragment_shader_source[3] = blend_fragment_shader_out_2_1; + } + else + { + /* We have at least OpenGL 3.0 and at least GLSL 1.30. */ + poly_vertex_shader_source[0] = shader_version_3_0; + poly_vertex_shader_source[1] = poly_vertex_shader_attribs_3_0; + poly_vertex_shader_source[2] = poly_vertex_shader_main; + poly_fragment_shader_source[0] = shader_version_3_0; + poly_fragment_shader_source[1] = poly_fragment_shader_attribs_3_0; + poly_fragment_shader_source[2] = poly_fragment_shader_main; + poly_fragment_shader_source[3] = poly_fragment_shader_out_3_0; + + line_vertex_shader_source[0] = shader_version_3_0; + line_vertex_shader_source[1] = line_vertex_shader_attribs_3_0; + line_vertex_shader_source[2] = line_vertex_shader_main; + line_fragment_shader_source[0] = shader_version_3_0; + line_fragment_shader_source[1] = line_fragment_shader_attribs_3_0; + line_fragment_shader_source[2] = line_fragment_shader_main; + line_fragment_shader_source[3] = line_fragment_shader_out_3_0; + + blend_vertex_shader_source[0] = shader_version_3_0; + blend_vertex_shader_source[1] = blend_vertex_shader_attribs_3_0; + blend_vertex_shader_source[2] = blend_vertex_shader_main; + blend_fragment_shader_source[0] = shader_version_3_0; + blend_fragment_shader_source[1] = blend_fragment_shader_attribs_3_0; + blend_fragment_shader_source[2] = blend_fragment_shader_main; + blend_fragment_shader_source[3] = blend_fragment_shader_out_3_0; + } + } + else /* gl_gles3 */ + { + if (gl_major < 3 || glsl_major < 3) + return; + /* We have at least OpenGL ES 3.0 and at least GLSL ES 3.0. */ + poly_vertex_shader_source[0] = shader_version_3_0_es; + poly_vertex_shader_source[1] = poly_vertex_shader_attribs_3_0; + poly_vertex_shader_source[2] = poly_vertex_shader_main; + poly_fragment_shader_source[0] = shader_version_3_0_es; + poly_fragment_shader_source[1] = poly_fragment_shader_attribs_3_0; + poly_fragment_shader_source[2] = poly_fragment_shader_main; + poly_fragment_shader_source[3] = poly_fragment_shader_out_3_0; + + line_vertex_shader_source[0] = shader_version_3_0_es; + line_vertex_shader_source[1] = line_vertex_shader_attribs_3_0; + line_vertex_shader_source[2] = line_vertex_shader_main; + line_fragment_shader_source[0] = shader_version_3_0_es; + line_fragment_shader_source[1] = line_fragment_shader_attribs_3_0; + line_fragment_shader_source[2] = line_fragment_shader_main; + line_fragment_shader_source[3] = line_fragment_shader_out_3_0; + + blend_vertex_shader_source[0] = shader_version_3_0_es; + blend_vertex_shader_source[1] = blend_vertex_shader_attribs_3_0; + blend_vertex_shader_source[2] = blend_vertex_shader_main; + blend_fragment_shader_source[0] = shader_version_3_0_es; + blend_fragment_shader_source[1] = blend_fragment_shader_attribs_3_0; + blend_fragment_shader_source[2] = blend_fragment_shader_main; + blend_fragment_shader_source[3] = blend_fragment_shader_out_3_0; + } + + if (!glsl_CompileAndLinkShaders(3,poly_vertex_shader_source, + 4,poly_fragment_shader_source, + &se->poly_shader_program)) + return; + se->poly_pos_index = glGetAttribLocation(se->poly_shader_program, + "VertexPosition"); + se->poly_normal_index = glGetAttribLocation(se->poly_shader_program, + "VertexNormal"); + se->poly_colorf_index = glGetAttribLocation(se->poly_shader_program, + "VertexColorF"); + se->poly_colorb_index = glGetAttribLocation(se->poly_shader_program, + "VertexColorB"); + se->poly_mv_index = glGetUniformLocation(se->poly_shader_program, + "MatModelView"); + se->poly_proj_index = glGetUniformLocation(se->poly_shader_program, + "MatProj"); + se->poly_glbl_ambient_index = glGetUniformLocation(se->poly_shader_program, + "LtGlblAmbient"); + se->poly_lt_ambient_index = glGetUniformLocation(se->poly_shader_program, + "LtAmbient"); + se->poly_lt_diffuse_index = glGetUniformLocation(se->poly_shader_program, + "LtDiffuse"); + se->poly_lt_specular_index = glGetUniformLocation(se->poly_shader_program, + "LtSpecular"); + se->poly_lt_direction_index = glGetUniformLocation(se->poly_shader_program, + "LtDirection"); + se->poly_lt_halfvect_index = glGetUniformLocation(se->poly_shader_program, + "LtHalfVector"); + se->poly_front_ambient_index = glGetUniformLocation(se->poly_shader_program, + "MatFrontAmbient"); + se->poly_back_ambient_index = glGetUniformLocation(se->poly_shader_program, + "MatBackAmbient"); + se->poly_front_diffuse_index = glGetUniformLocation(se->poly_shader_program, + "MatFrontDiffuse"); + se->poly_back_diffuse_index = glGetUniformLocation(se->poly_shader_program, + "MatBackDiffuse"); + se->poly_specular_index = glGetUniformLocation(se->poly_shader_program, + "MatSpecular"); + se->poly_shininess_index = glGetUniformLocation(se->poly_shader_program, + "MatShininess"); + if (se->poly_pos_index == -1 || + se->poly_normal_index == -1 || + se->poly_colorf_index == -1 || + se->poly_colorb_index == -1 || + se->poly_mv_index == -1 || + se->poly_proj_index == -1 || + se->poly_glbl_ambient_index == -1 || + se->poly_lt_ambient_index == -1 || + se->poly_lt_diffuse_index == -1 || + se->poly_lt_specular_index == -1 || + se->poly_lt_direction_index == -1 || + se->poly_lt_halfvect_index == -1 || + se->poly_front_ambient_index == -1 || + se->poly_back_ambient_index == -1 || + se->poly_front_diffuse_index == -1 || + se->poly_back_diffuse_index == -1 || + se->poly_specular_index == -1 || + se->poly_shininess_index == -1) + { + glDeleteProgram(se->poly_shader_program); + return; + } + + if (!glsl_CompileAndLinkShaders(3,line_vertex_shader_source, + 4,line_fragment_shader_source, + &se->line_shader_program)) + { + glDeleteProgram(se->poly_shader_program); + return; + } + se->line_pos_index = glGetAttribLocation(se->line_shader_program, + "VertexPosition"); + se->line_color_index = glGetUniformLocation(se->line_shader_program, + "LineColor"); + se->line_mv_index = glGetUniformLocation(se->line_shader_program, + "MatModelView"); + se->line_proj_index = glGetUniformLocation(se->line_shader_program, + "MatProj"); + if (se->line_pos_index == -1 || + se->line_color_index == -1 || + se->line_mv_index == -1 || + se->line_proj_index == -1) + { + glDeleteProgram(se->poly_shader_program); + glDeleteProgram(se->line_shader_program); + return; + } + + if (!glsl_CompileAndLinkShaders(3,blend_vertex_shader_source, + 4,blend_fragment_shader_source, + &se->blend_shader_program)) + { + glDeleteProgram(se->poly_shader_program); + glDeleteProgram(se->line_shader_program); + return; + } + se->blend_vertex_p_index = glGetAttribLocation(se->blend_shader_program, + "VertexP"); + se->blend_vertex_t_index = glGetAttribLocation(se->blend_shader_program, + "VertexT"); + se->blend_t_index = glGetUniformLocation(se->blend_shader_program, + "T"); + se->blend_sampler0_index = glGetUniformLocation(se->blend_shader_program, + "TextureSampler0"); + se->blend_sampler1_index = glGetUniformLocation(se->blend_shader_program, + "TextureSampler1"); + if (se->blend_vertex_p_index == -1 || + se->blend_vertex_t_index == -1 || + se->blend_t_index == -1 || + se->blend_sampler0_index == -1 || + se->blend_sampler1_index == -1) + { + glDeleteProgram(se->poly_shader_program); + glDeleteProgram(se->line_shader_program); + glDeleteProgram(se->blend_shader_program); + return; + } + + glGetIntegerv(GL_MAX_TEXTURE_SIZE,&se->max_tex_size); + if (se->WindW <= se->max_tex_size || se->WindH <= se->max_tex_size) + { + wt = se->WindW; + ht = se->WindH; + } + else + { + wt = MIN(se->max_tex_size,se->WindW); + ht = MIN(se->max_tex_size,se->WindH); + } + + glGenTextures(2,se->color_textures); + for (i=0; i<2; i++) + { + glBindTexture(GL_TEXTURE_2D,se->color_textures[i]); + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,wt,ht,0,GL_RGBA,GL_UNSIGNED_BYTE, + NULL); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + } + + glGenBuffers(1,&se->vertex_pos_buffer); + glGenBuffers(1,&se->vertex_normal_buffer); + glGenBuffers(2,se->vertex_colorf_buffer); + glGenBuffers(2,se->vertex_colorb_buffer); + glGenBuffers(1,&se->solid_indices_buffer); + glGenBuffers(1,&se->parallel_indices_buffer); + glGenBuffers(1,&se->meridian_indices_buffer); + glGenBuffers(1,&se->line_indices_buffer); + + se->use_shaders = True; +} + +#endif /* HAVE_GLSL */ + + +static void init(ModeInfo *mi) +{ + sphereeversionstruct *se = &sphereeversion[MI_SCREEN(mi)]; + + if (deform_speed == 0.0f) + deform_speed = 10.0f; + + se->alpha = frand(120.0)-60.0; + se->beta = frand(120.0)-60.0; + se->delta = frand(360.0); + + se->eta_min = BEDNORZ_ETA_MIN; + se->beta_max = BEDNORZ_BETA_MAX; + + se->anim_state = ANIM_DEFORM; + se->tau = BEDNORZ_TAU_MAX; + se->defdir = -1; + se->turn_step = 0; + se->num_turn = 0; + + se->offset3d[0] = 0.0f; + se->offset3d[1] = 0.0f; + se->offset3d[2] = -1.8f; + + se->se = calloc(3*(NUMPH+1)*(NUMTH+1),sizeof(float)); + se->sen = calloc(3*(NUMPH+1)*(NUMTH+1),sizeof(float)); + se->colf[0] = calloc(4*(NUMPH+1)*(NUMTH+1),sizeof(float)); + se->colf[1] = calloc(4*(NUMPH+1)*(NUMTH+1),sizeof(float)); + se->colb[0] = calloc(4*(NUMPH+1)*(NUMTH+1),sizeof(float)); + se->colb[1] = calloc(4*(NUMPH+1)*(NUMTH+1),sizeof(float)); + +#ifdef HAVE_GLSL + init_glsl(mi); +#endif /* HAVE_GLSL */ + + setup_surface_colors(mi,-M_PI,M_PI,-M_PI/2.0,M_PI/2.0,NUMPH,NUMTH); +} + + +/* Redisplay the deformed surface. */ +static void display_sphereeversion(ModeInfo *mi) +{ + float alpha, beta, delta, dot, a, t, q[4]; + float *colt; + sphereeversionstruct *se = &sphereeversion[MI_SCREEN(mi)]; +#ifdef HAVE_GLSL + GLuint vertex_colort_buffer; +#endif /* HAVE_GLSL */ + + if (!se->button_pressed) + { + if (se->anim_state == ANIM_DEFORM) + { + se->tau += se->defdir*deform_speed*0.001; + if (se->tau < BEDNORZ_TAU_MIN) + { + se->tau = BEDNORZ_TAU_MIN; + se->defdir = -se->defdir; + se->anim_state = ANIM_TURN; + } + if (se->tau > BEDNORZ_TAU_MAX) + { + se->tau = BEDNORZ_TAU_MAX; + se->defdir = -se->defdir; + se->anim_state = ANIM_TURN; + } + if (se->anim_state == ANIM_TURN) + { + /* Convert the current rotation angles to a quaternion. */ + angles_to_quat(se->alpha,se->beta,se->delta,se->qs); + /* Determine random target rotation angles and convert them to the + target quaternion. */ + alpha = frand(120.0)-60.0; + beta = frand(120.0)-60.0; + delta = frand(360.0); + angles_to_quat(alpha,beta,delta,se->qe); + /* Compute the angle between qs and qe. */ + dot = (se->qs[0]*se->qe[0]+se->qs[1]*se->qe[1]+ + se->qs[2]*se->qe[2]+se->qs[3]*se->qe[3]); + if (dot < 0.0f) + { + se->qe[0] = -se->qe[0]; + se->qe[1] = -se->qe[1]; + se->qe[2] = -se->qe[2]; + se->qe[3] = -se->qe[3]; + dot = -dot; + } + a = 180.0/M_PI*acos(dot); + se->num_turn = ceil(a/TURN_STEP); + + /* Change the parameters randomly after one full eversion when a + turn to the new orientation starts. */ + /* Copy the current display modes to the values of the second pass. */ + se->display_mode[1] = se->display_mode[0]; + se->appearance[1] = se->appearance[0]; + se->colors[1] = se->colors[0]; + se->graticule[1] = se->graticule[0]; + /* Swap the front and back color buffers. */ + colt = se->colf[1]; + se->colf[1] = se->colf[0]; + se->colf[0] = colt; + colt = se->colb[1]; + se->colb[1] = se->colb[0]; + se->colb[0] = colt; +#ifdef HAVE_GLSL + /* Swap the OpenGL front and back color buffers. */ + if (se->use_shaders) + { + vertex_colort_buffer = se->vertex_colorf_buffer[1]; + se->vertex_colorf_buffer[1] = se->vertex_colorf_buffer[0]; + se->vertex_colorf_buffer[0] = vertex_colort_buffer; + vertex_colort_buffer = se->vertex_colorb_buffer[1]; + se->vertex_colorb_buffer[1] = se->vertex_colorb_buffer[0]; + se->vertex_colorb_buffer[0] = vertex_colort_buffer; + } +#endif /* HAVE_GLSL */ + /* Randomly select new display modes for the main pass. */ + if (se->random_display_mode) + se->display_mode[0] = random() % NUM_DISPLAY_MODES; + if (se->random_appearance) + se->appearance[0] = random() % NUM_APPEARANCES; + if (se->random_colors) + se->colors[0] = random() % NUM_COLORS; + if (se->random_graticule) + se->graticule[0] = random() & 1; + if (se->random_g) + se->g = random() % 4 + 2; + setup_surface_colors(mi,-M_PI,M_PI,-M_PI/2.0,M_PI/2.0,NUMPH,NUMTH); + } + } + else /* se->anim_state == ANIM_TURN */ + { + t = (float)se->turn_step/(float)se->num_turn; + /* Apply an easing function to t. */ + t = (3.0-2.0*t)*t*t; + quat_slerp(t,se->qs,se->qe,q); + quat_to_angles(q,&se->alpha,&se->beta,&se->delta); + se->turn_step++; + if (se->turn_step > se->num_turn) + { + se->turn_step = 0; + se->anim_state = ANIM_DEFORM; + } + } + + if (se->anim_state == ANIM_DEFORM) + { + se->alpha += speed_x*se->speed_scale; + if (se->alpha >= 360.0f) + se->alpha -= 360.0f; + se->beta += speed_y*se->speed_scale; + if (se->beta >= 360.0f) + se->beta -= 360.0f; + se->delta += speed_z*se->speed_scale; + if (se->delta >= 360.0f) + se->delta -= 360.0f; + } + } + +#ifdef HAVE_GLSL + if (se->use_shaders) + mi->polygon_count = bednorz_sphere_eversion_pf(mi,-M_PI,M_PI,-M_PI/2.0, + M_PI/2.0,NUMPH,NUMTH, + NUMBDIST,NUMBDIR, + NUMGRID); + else +#endif /* HAVE_GLSL */ + mi->polygon_count = bednorz_sphere_eversion_ff(mi,-M_PI,M_PI,-M_PI/2.0, + M_PI/2.0,NUMPH,NUMTH, + NUMBDIST,NUMBDIR, + NUMGRID); +} + + +ENTRYPOINT void reshape_sphereeversion(ModeInfo *mi, int width, int height) +{ + sphereeversionstruct *se = &sphereeversion[MI_SCREEN(mi)]; + int y = 0; +#ifdef HAVE_GLSL + int i, wt, ht; +#endif /* HAVE_GLSL */ + + if (width > height * 5) { /* tiny window: show middle */ + height = width; + y = -height/2; + } + + se->WindW = (GLint)width; + se->WindH = (GLint)height; + glViewport(0,y,width,height); + se->aspect = (GLfloat)width/(GLfloat)height; +#ifdef HAVE_GLSL + if (se->use_shaders) + { + if (se->WindW <= se->max_tex_size || se->WindH <= se->max_tex_size) + { + wt = se->WindW; + ht = se->WindH; + } + else + { + wt = MIN(se->max_tex_size,se->WindW); + ht = MIN(se->max_tex_size,se->WindH); + } + for (i=0; i<2; i++) + { + glBindTexture(GL_TEXTURE_2D,se->color_textures[i]); + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,wt,ht,0,GL_RGBA,GL_UNSIGNED_BYTE, + NULL); + } + } +#endif /* HAVE_GLSL */ +} + + +ENTRYPOINT Bool sphereeversion_handle_event(ModeInfo *mi, XEvent *event) +{ + sphereeversionstruct *se = &sphereeversion[MI_SCREEN(mi)]; + + if (event->xany.type == ButtonPress && event->xbutton.button == Button1) + { + se->button_pressed = True; + gltrackball_start(se->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) + { + se->button_pressed = False; + return True; + } + else if (event->xany.type == MotionNotify && se->button_pressed) + { + gltrackball_track(se->trackball, event->xmotion.x, event->xmotion.y, + MI_WIDTH(mi), MI_HEIGHT(mi)); + return True; + } + + return False; +} + + +/* + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + * Xlock hooks. + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + */ + +/* + *----------------------------------------------------------------------------- + * Initialize sphereeversion. Called each time the window changes. + *----------------------------------------------------------------------------- + */ + +ENTRYPOINT void init_sphereeversion(ModeInfo *mi) +{ + sphereeversionstruct *se; + + MI_INIT(mi,sphereeversion); + se = &sphereeversion[MI_SCREEN(mi)]; + + se->trackball = gltrackball_init(True); + se->button_pressed = False; + + /* Set the surface order. */ + if (!strcasecmp(surface_order,"2")) + { + se->g = 2; + se->random_g = False; + } + else if (!strcasecmp(surface_order,"3")) + { + se->g = 3; + se->random_g = False; + } + else if (!strcasecmp(surface_order,"4")) + { + se->g = 4; + se->random_g = False; + } + else if (!strcasecmp(surface_order,"5")) + { + se->g = 5; + se->random_g = False; + } + else + { + se->g = random() % 4 + 2; + se->random_g = True; + } + + /* Set the display mode. */ + if (!strcasecmp(mode,"surface")) + { + se->display_mode[0] = DISP_SURFACE; + se->random_display_mode = False; + } + else if (!strcasecmp(mode,"transparent")) + { + se->display_mode[0] = DISP_TRANSPARENT; + se->random_display_mode = False; + } + else + { + se->display_mode[0] = random() % NUM_DISPLAY_MODES; + se->random_display_mode = True; + } + + /* Set the appearance. */ + if (!strcasecmp(appear,"solid")) + { + se->appearance[0] = APPEARANCE_SOLID; + se->random_appearance = False; + } + else if (!strcasecmp(appear,"parallel-bands")) + { + se->appearance[0] = APPEARANCE_PARALLEL_BANDS; + se->random_appearance = False; + } + else if (!strcasecmp(appear,"meridian-bands")) + { + se->appearance[0] = APPEARANCE_MERIDIAN_BANDS; + se->random_appearance = False; + } + else + { + se->appearance[0] = random() % NUM_APPEARANCES; + se->random_appearance = True; + } + + /* Set the color mode. */ + if (!strcasecmp(color_mode,"two-sided")) + { + se->colors[0] = COLORS_TWOSIDED; + se->random_colors = False; + } + else if (!strcasecmp(color_mode,"parallel")) + { + se->colors[0] = COLORS_PARALLEL; + se->random_colors = False; + } + else if (!strcasecmp(color_mode,"meridian")) + { + se->colors[0] = COLORS_MERIDIAN; + se->random_colors = False; + } + else + { + se->colors[0] = random() % NUM_COLORS; + se->random_colors = True; + } + + /* Set the graticule mode. */ + if (!strcasecmp(graticule,"on")) + { + se->graticule[0] = True; + se->random_graticule = False; + } + else if (!strcasecmp(graticule,"off")) + { + se->graticule[0] = False; + se->random_graticule = False; + } + else + { + se->graticule[0] = random() & 1; + se->random_graticule = True; + } + + /* Set the 3d projection mode. */ + if (!strcasecmp(proj,"perspective")) + { + se->projection = DISP_PERSPECTIVE; + } + else if (!strcasecmp(proj,"orthographic")) + { + se->projection = DISP_ORTHOGRAPHIC; + } + else + { + se->projection = random() % NUM_DISP_MODES; + } + + /* Make multiple screens rotate at slightly different rates. */ + se->speed_scale = 0.9+frand(0.3); + + if ((se->glx_context = init_GL(mi)) != NULL) + { + reshape_sphereeversion(mi,MI_WIDTH(mi),MI_HEIGHT(mi)); + init(mi); + } + else + { + MI_CLEARWINDOW(mi); + } +} + +/* + *----------------------------------------------------------------------------- + * Called by the mainline code periodically to update the display. + *----------------------------------------------------------------------------- + */ +ENTRYPOINT void draw_sphereeversion(ModeInfo *mi) +{ + Display *display = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + sphereeversionstruct *se; + + if (sphereeversion == NULL) + return; + se = &sphereeversion[MI_SCREEN(mi)]; + + MI_IS_DRAWN(mi) = True; + if (!se->glx_context) + return; + + glXMakeCurrent(display, window, *se->glx_context); + + display_sphereeversion(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. + *----------------------------------------------------------------------------- + */ + + +#ifndef STANDALONE +ENTRYPOINT void change_sphereeversion(ModeInfo *mi) +{ + sphereeversionstruct *ev = &sphereeversion[MI_SCREEN(mi)]; + + if (!ev->glx_context) + return; + + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *ev->glx_context); + init(mi); +} +#endif /* !STANDALONE */ + + +ENTRYPOINT void free_sphereeversion(ModeInfo *mi) +{ + sphereeversionstruct *se = &sphereeversion[MI_SCREEN(mi)]; + + if (!se->glx_context) return; + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *se->glx_context); + + if (se->se) free(se->se); + if (se->sen) free(se->sen); + if (se->colf[0]) free(se->colf[0]); + if (se->colf[1]) free(se->colf[1]); + if (se->colb[0]) free(se->colb[0]); + if (se->colb[1]) free(se->colb[1]); + gltrackball_free(se->trackball); +#ifdef HAVE_GLSL + if (se->solid_indices) free(se->solid_indices); + if (se->parallel_indices) free(se->parallel_indices); + if (se->meridian_indices) free(se->meridian_indices); + if (se->line_indices) free(se->line_indices); + if (se->use_shaders) + { + glUseProgram(0); + if (se->poly_shader_program != 0) + glDeleteProgram(se->poly_shader_program); + if (se->line_shader_program != 0) + glDeleteProgram(se->line_shader_program); + if (se->blend_shader_program != 0) + glDeleteProgram(se->blend_shader_program); + glDeleteTextures(2,se->color_textures); + glDeleteBuffers(1,&se->vertex_pos_buffer); + glDeleteBuffers(1,&se->vertex_normal_buffer); + glDeleteBuffers(2,se->vertex_colorf_buffer); + glDeleteBuffers(2,se->vertex_colorb_buffer); + glDeleteBuffers(1,&se->solid_indices_buffer); + glDeleteBuffers(1,&se->parallel_indices_buffer); + glDeleteBuffers(1,&se->meridian_indices_buffer); + glDeleteBuffers(1,&se->line_indices_buffer); + } +#endif /* HAVE_GLSL */ +} + + +XSCREENSAVER_MODULE ("SphereEversion", sphereeversion) + +#endif /* USE_GL */ |