/* 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 . */ /* * 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 #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= 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; jappearance[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; jse[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; jsolid_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= 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= 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; jline_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; passpoly_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; inum_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; inum_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; inum_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 */