Chapter 8 covers texturing, the process of applying an image to a surface, instead of simply making it a solid color. This can improve an image by adding a lot of detail without adding a huge number of triangles with their attendant supply of vertexes, normals and color data.
The first sample program applies a stone pattern to a pyramid. I adjusted the proportions of the pyramid to match the Great Pyramid at Giza, and then modified it to look like the dollar bill pyramid, with the apex floating above the base, like this:
The basic pyramid has five points and six triangles (four faces and two on the square base), but the floating-apex pyramid requires a lot more effort. I wrote a triangle-drawing method that takes three vertex index values, which then set the normal to the triangle, calculated the texture coordinates for each vertex, and then drew the three vertexes. That made the drawPyramid method much clearer: it just had to draw triangles, and the heavy lifting got done behind the scenes. Here’s the code:
- (void)drawTrianglePointA:(int)iA
pointB:(int)iB
pointC:(int)iC {
M3DVector3f vNormal;
m3dFindNormal(vNormal, vCorners[iA], vCorners[iB], vCorners[iC]);
// NSLog(@"Triangle %i, %i, %i with normal (%f, %f, %f)",
// iA, iB, iC, vNormal[0], vNormal[1], vNormal[2]);
int sAxis, tAxis;
float sFactor, tFactor;
float sBias, tBias;
float s, t;
if (vNormal[1] > 0.8 | vNormal[1] < -0.8) { // top of apex
sAxis = 0;
tAxis = 2;
sFactor = tFactor = vNormal[1]/(2.0f*width);
sBias = tBias = 0.5f;
} else if (vNormal[0] > 0.7f) {
sAxis = 2;
tAxis = 1;
sFactor = -0.5f/width;
sBias = 0.5f;
tFactor = 1.0f/height;
tBias = 0.0f;
} else if (vNormal[0] < -0.7f) {
sAxis = 2;
tAxis = 1;
sFactor = +0.5f/width;
sBias = 0.5f;
tFactor = 1.0f/height;
tBias = 0.0f;
} else if (vNormal[2] > 0.7f) {
sAxis = 0;
tAxis = 1;
sFactor = +0.5f/width;
sBias = 0.5f;
tFactor = 1.0f/height;
tBias = 0.0f;
} else {
sAxis = 0;
tAxis = 1;
sFactor = -0.5f/width;
sBias = 0.5f;
tFactor = 1.0f/height;
tBias = 0.0f;
}
// NSLog(@"sAxis = %i, tAxis = %i, sFactor, sBias = %f, %f; tFactor, tBias = %f, %f",
// sAxis, tAxis, sFactor, sBias, tFactor, tBias);
glNormal3fv(vNormal);
s = vCorners[iA][sAxis]*sFactor + sBias;
t = vCorners[iA][tAxis]*tFactor + tBias;
// NSLog(@"s = %f, t = %f", s, t);
glTexCoord2f(s, t);
glVertex3fv(vCorners[iA]);
s = vCorners[iB][sAxis]*sFactor + sBias;
t = vCorners[iB][tAxis]*tFactor + tBias;
// NSLog(@"s = %f, t = %f", s, t);
glTexCoord2f(s, t);
glVertex3fv(vCorners[iB]);
s = vCorners[iC][sAxis]*sFactor + sBias;
t = vCorners[iC][tAxis]*tFactor + tBias;
// NSLog(@"s = %f, t = %f", s, t);
glTexCoord2f(s, t);
glVertex3fv(vCorners[iC]);
}
- (void)drawPyramid {
// bottom of apex
[self drawTrianglePointA:2 pointB:4 pointC:1];
[self drawTrianglePointA:2 pointB:3 pointC:4];
// faces of apex
[self drawTrianglePointA:0 pointB:4 pointC:3];
[self drawTrianglePointA:0 pointB:1 pointC:4];
[self drawTrianglePointA:0 pointB:2 pointC:1];
[self drawTrianglePointA:0 pointB:3 pointC:2];
// Top of base
[self drawTrianglePointA:6 pointB:5 pointC:8];
[self drawTrianglePointA:8 pointB:7 pointC:6];
// faces of base
[self drawTrianglePointA:8 pointB:12 pointC:7];
[self drawTrianglePointA:7 pointB:12 pointC:11];
[self drawTrianglePointA:5 pointB:9 pointC:8];
[self drawTrianglePointA:8 pointB:9 pointC:12];
[self drawTrianglePointA:6 pointB:10 pointC:5];
[self drawTrianglePointA:5 pointB:10 pointC:9];
[self drawTrianglePointA:7 pointB:11 pointC:6];
[self drawTrianglePointA:6 pointB:11 pointC:10];
// bottom of base
[self drawTrianglePointA:10 pointB:12 pointC:9 ];
[self drawTrianglePointA:10 pointB:11 pointC:12];
}
Here are the coordinates:
vCorners = {{ 0.000000, 280.000000, 0.000000},
{ -52.380951, 213.333344, -52.380951},
{ 52.380951, 213.333344, -52.380951},
{ 52.380951, 213.333344, 52.380951},
{ -52.380951, 213.333344, 52.380951},
{ -83.809525, 173.333344, -83.809525},
{ 83.809525, 173.333344, -83.809525},
{ 83.809525, 173.333344, 83.809525},
{ -83.809525, 173.333344, 83.809525},
{ -220.000000, 0.000000, -220.000000},
{ 220.000000, 0.000000, -220.000000},
{ 220.000000, 0.000000, 220.000000},
{ -220.000000, 0.000000, 220.000000}};
The apex really needs to be casting a shadow to make the image more believable.
Now I need to add an eye texture to one face of the apex, and maybe “Novus Ordo Seclorum” or “MDCCLXXVI” at the bottom of the base. Or I could add it to SphereWorld and make the apex rotate…