/* Copyright (c) Mark J. Kilgard, 1998. */ /* This program is freely distributable without licensing fees and is provided without guarantee or warrantee expressed or implied. This program is -not- in the public domain. */ /* makemesh.c - generates mesh geometry for sphere map construction */ #include #include #include #include #include #include "smapmesh.h" /* (x,y,z) reflection vector --> (s,t) sphere map coordinates */ void rvec2st(float v[3], float st[2]) { double m; /* In Section 2.10.4 ("Generating texture coordinates") of the OpenGL 1.1 specification, you will find the GL_SPHERE_MAP equations: n' = normal after transformation to eye coordinates u = unit vector from origin to vertex in eye coordinates (rx, ry, rz) = u - 2 * n' * transpose(n') * u m = 2 * sqrt(rx^2 + ry^2 + (rz + 1)^2)) s = rx/m + 0.5 t = ry/m + 0.5 The equation for calculating (rx, ry, rz) is the equation for calculating the reflection vector for a surface and observer. The explanation and derivation for this equation is found in Roger's "Procedural Elements for Computer Graphics" 2nd ed. in Section 5-5 ("Determining the Reflection Vector"). Note that Roger's convention has the Z axis in the opposite direction from the OpenGL convention. */ m = 2 * sqrt(v[0]*v[0] + v[1]*v[1] + (v[2]+1)*(v[2]+1)); st[0] = v[0]/m + 0.5; st[1] = v[1]/m + 0.5; } /* (s,t) sphere map coordinate --> reflection verctor (x,y,z) */ void st2rvec(float s, float t, float *xp, float *yp, float *zp) { double rx, ry, rz; double tmp1, tmp2; /* Using algebra to invert the sphere mapping equations shown above in rvec2st, you get: rx = 2*sqrt(-4*s^2 + 4*s - 4*t^2 + 4*t - 1)*(2*s-1) ry = 2*sqrt(-4*s^2 + 4*s - 4*t^2 + 4*t - 1)*(2*t-1) rz = -8*s^2 + 8*s - 8*t^2 + 8*t - 3 The C code below eliminates common subexpressions. */ tmp1 = s*(1-s) + t*(1-t); tmp2 = 2 * sqrt(4*tmp1 - 1); rx = tmp2 * (2*s-1); ry = tmp2 * (2*t-1); rz = 8 * tmp1 - 3; *xp = (float) rx; *yp = (float) ry; *zp = (float) rz; } /* For best results (ie, to avoid cracks in the sphere map construction, XSTEPS, YSTEPS, and SPOKES should all be equal. */ /* Increasing the nSTEPS and RINGS constants below will give you a better approximation to the sphere map image warp at the cost of more polygons to render the image warp. My bet is that no will be able to the improved quality of a higher level of tessellation. */ STXY face[5][YSTEPS][XSTEPS]; STXY back[4][RINGS+EDGE_EXTEND][SPOKES]; static struct { int xl; int yl; int zl; int swap; /* swap final (s,t) */ int flip; /* flip final s or t, ie. [0..1] --> [1..0] */ float dir; } faceInfo[5] = { { 0, 1, 2, 0, 1.0, 1.0 }, /* front */ { 0, 2, 1, 0, -1.0, 1.0 }, /* top */ { 0, 2, 1, 0, 1.0, -1.0 }, /* bottom */ { 1, 2, 0, 1, -1.0, 1.0 }, /* left */ { 1, 2, 0, 1, 1.0, -1.0 }, /* right */ }; static struct { int xl; int yl; float dir; } edgeInfo[4] = { { 0, 1, -1.0 }, { 0, 1, 1.0 }, { 1, 0, -1.0 }, { 1, 0, 1.0 } }; void makeSphereMapMesh(void) { float st[2]; /* (s,t) coordinate */ /* range=[0..1,0..1] */ float v[3]; /* (x,y,z) location on cube map */ /* range=[-1..1,-1..1,-1..1] */ float rv[3]; /* reflection vector, ie. cube map location */ /* normalized onto unit sphere */ float len; /* distance from v[3] to origin */ /* for converting to rv[3] */ int side; /* which of 5 faces (all but back face) */ int i, j; int xl, yl, zl; /* renamed X, Y, Z index */ int swap; int flip; int edge; /* which edge of back face */ float sc, tc; /* singularity (s,t) on back face circle */ /* for the front and four side faces */ for (side=0; side<5; side++) { /* use faceInfo to parameterize face construction */ xl = faceInfo[side].xl; yl = faceInfo[side].yl; zl = faceInfo[side].zl; swap = faceInfo[side].swap; flip = faceInfo[side].flip; /* cube map "Z" coordinate */ v[zl] = faceInfo[side].dir; for (i=0; i 2) { float s, t; /* interpolated (s,t) */ float ds, dt; /* delta s and delta t */ float x, y, z; /* Start interpolating from the edge. */ s = st[0]; t = st[1]; /* Calculate delta s and delta t for interpolation. */ ds = (sc - s) / (RINGS-1); dt = (tc - t) / (RINGS-1); for (i=1; i