void DoVertexF(const GLfloat *v, // vector const GLfloat *c, // color const GLfloat *n, // normals const GLfloat *t // texcoörds ) { glColor3fv(c); glTexCoord2f(t[0], t[1]); glNormal3fv(n); glVertex3fv(v); } void DoTriangleF(const GLfloat *v0, const GLfloat *v1, const GLfloat *v2, const GLfloat *c0, const GLfloat *c1, const GLfloat *c2, const GLfloat *n0, const GLfloat *n1, const GLfloat *n2, const GLfloat *t0, const GLfloat *t1, const GLfloat *t2 ) { DoVertexF(v0, c0, n0, t0); DoVertexF(v1, c1, n1, t1); DoVertexF(v2, c2, n2, t2); } void CanonicalizeNormal3f(GLfloat *nxp, GLfloat *nyp, GLfloat *nzp) { GLfloat magnitude = sqrt(*nxp * *nxp + *nyp * *nyp + *nzp * *nzp); *nxp /= magnitude; *nyp /= magnitude; *nzp /= magnitude; return; } unsigned long int DoSphericF(int more, const GLfloat *v0, const GLfloat *v1, const GLfloat *v2, const GLfloat *c0, const GLfloat *c1, const GLfloat *c2, const GLfloat *n0, const GLfloat *n1, const GLfloat *n2 ) { unsigned long int fi = 0; /* faces rendered */ int i; if(more < 0) { cerr << "DoSphericF: invalid (negative) more parameter" << endl; } else if(more == 0) { // texture coördinates generated based on v{0,1,2} around origin const GLfloat *vv[3] = { v0, v1, v2 }; GLfloat tc[3][2]; // texture coördinates int i; for(i = 0 ; i < 3 ; ++i) { // express s, t in range [0,1] - length of hypotenuse is 1 float x = vv[i][0]; float y = vv[i][1]; float z = vv[i][2]; float hyp_xz = sqrt(pow(x,2) + pow(z,2)); float hyp_xyz = sqrt(pow(x,2) + pow(y,2) + pow(z,2)); // use negative z because in right-handed system for map wrap float cosine = Cage(-1.0, x/hyp_xz, 1.0); float s = acos(cosine); // [0, π] if(z > 0) // s = 2*M_PI - s; // [π, 2π) - completes upper ½ of range tc[i][0] = s / (2*M_PI); // [0, 1) - we now have the s coördinate float sine = Cage(-1.0, y/hyp_xyz, 1.0); float t = asin(sine); // [-π/2, π/2] t += M_PI_2; // [0, π] tc[i][1] = t / M_PI; // [0, 1] - we now have the t coördinate } DoTriangleF(v0, v1, v2, c0, c1, c2, n0, n1, n2, tc[0], tc[1], tc[2]); ++fi; } else if(more > 0) { /* find midpoints of edges, then push out to 1 by normalization */ /* v0 CCW v0-v1-v2 * a c CCW a-b-c * v1 b v2 CCW v0-a-c v1-b-a- v2-c-b */ GLfloat a[3] = { (v0[0]+v1[0])/2,(v0[1]+v1[1])/2,(v0[2]+v1[2])/2 }; GLfloat b[3] = { (v1[0]+v2[0])/2,(v1[1]+v2[1])/2,(v1[2]+v2[2])/2 }; GLfloat c[3] = { (v2[0]+v0[0])/2,(v2[1]+v0[1])/2,(v2[2]+v0[2])/2 }; GLfloat ca[3]= { (c0[0]+c1[0])/2,(c0[1]+c1[1])/2,(c0[2]+c1[2])/2 }; GLfloat cb[3]= { (c1[0]+c2[0])/2,(c1[1]+c2[1])/2,(c1[2]+c2[2])/2 }; GLfloat cc[3]= { (c2[0]+c0[0])/2,(c2[1]+c0[1])/2,(c2[2]+c0[2])/2 }; CanonicalizeNormal3f(&a[0], &a[1], &a[2]); CanonicalizeNormal3f(&b[0], &b[1], &b[2]); CanonicalizeNormal3f(&c[0], &c[1], &c[2]); #if 0 /* a little chaos */ { /* modified--vv */ fi += DoSphericF(more-1, v0,a,c,c0,ca,cb,n0,a,c); fi += DoSphericF(more-1, v1,b,a,c1,cb,cc,n1,b,a); fi += DoSphericF(more-1, v2,c,b,c2,cc,ca,n2,c,b); fi += DoSphericF(more-1, a,b,c,ca,cb,cc, a,b,c); } #else { fi += DoSphericF(more-1, v0,a,c,c0,ca,cc,n0,a,c); fi += DoSphericF(more-1, v1,b,a,c1,cb,ca,n1,b,a); fi += DoSphericF(more-1, v2,c,b,c2,cc,cb,n2,c,b); fi += DoSphericF(more-1, a,b,c,ca,cb,cc, a,b,c); } #endif } return fi; } unsigned long int Ball() { unsigned long int fi = 0; /* faces initialized */ static GLuint me = 0; const int detail_level = 5; if( ! me) { me = glGenLists(1); glNewList(me, GL_COMPILE); { /* diameter of order-infinity tetrahedron: 1 * side of order-1 tetrahedron: diameter * sqrt(2) / sqrt(3) * offset along edge = diam / 2 / sqrt(3) * sqrt(2) * offset along core = diam / 2 / sqrt(3) */ const GLfloat radius = 1.0; /* diameter of order-inf tetra */ const GLfloat diam = radius * 2; /* const GLfloat oc = diam / 2.0 / sqrt(3.0); /* offset along core */ const GLfloat oc = radius / sqrt(3.0); /* offset along core */ const GLfloat oe = oc * sqrt(2.0); /* offset along edge */ enum { vertmax = 3 }; const GLfloat vertices[vertmax + 1][3] = { { -oc, 0, -oe }, /* left rear */ { -oc, 0, +oe }, /* left front */ { +oc, +oe, 0 }, /* right top */ { +oc, -oe, 0 }, /* right base */ }; const GLfloat colors[vertmax + 1][3] = { #if 1 { 1.0 , 1.0 , 1.0 }, /* white */ { 1.0 , 1.0 , 1.0 }, /* white */ { 1.0 , 1.0 , 1.0 }, /* white */ { 1.0 , 1.0 , 1.0 }, /* white */ #else { 1.0 , 0.0 , 0.0 }, /* violet */ { 0.0 , 1.0 , 0.0 }, /* green */ { 1.0 , 1.0 , 1.0 }, /* white */ { 0.0 , 0.0 , 1.0 }, /* blue */ #endif }; enum { lr = 0, lf = 1, rt = 2, rb = 3 }; GLfloat normals[vertmax + 1][3] = { { -oc, 0, -oe }, /* left rear */ { -oc, 0, +oe }, /* left front */ { +oc, +oe, 0 }, /* right top */ { +oc, -oe, 0 }, /* right base */ }; /* normal`s x component along core to top face center */ const GLfloat ncx = diam / 4 / sqrt(6.0); const GLfloat ncy = diam / 4 / sqrt(2.0); GLfloat normals_faces[vertmax + 1][3] = { { -ncx, +ncy, 0 }, /* top */ { +ncx, 0, +ncy }, /* front */ { +ncx, 0, -ncy }, /* rear */ { -ncx, -ncy, 0 }, /* base */ }; enum { trimax = 3 }; const GLint triangles[trimax + 1][3] = { { lr, lf, rt }, /* top CCW */ { rt, lf, rb }, /* front */ { rt, rb, lr }, /* rear */ { lf, lr, rb }, /* base */ }; int tri, vert; for(vert = 0 ; vert <= vertmax ; ++vert) CanonicalizeNormal3f(&normals[vert][0], &normals[vert][1], &normals[vert][2]); for(tri = 0 ; tri <= trimax ; ++tri) CanonicalizeNormal3f(&normals_faces[tri][0], &normals_faces[tri][1], &normals_faces[tri][2]); glBegin(GL_TRIANGLES); for(tri = 0 ; tri <= trimax ; ++tri) { fi += DoSphericF(detail_level, &vertices[triangles[tri][0]][0], &vertices[triangles[tri][1]][0], &vertices[triangles[tri][2]][0], &colors[triangles[tri][0]][0], &colors[triangles[tri][1]][0], &colors[triangles[tri][2]][0], &normals[triangles[tri][0]][0], &normals[triangles[tri][1]][0], &normals[triangles[tri][2]][0] ); } glEnd(); } cerr << __PRETTY_FUNCTION__ << " - one sphere, faces initialized: " << fi << endl; glEndList(); } glPushMatrix(); glCallList(me); glPopMatrix(); return fi; }