+ /* now based on flag either rotate patches around y axis to other 3 quadrants (flag=4) or reflect patch across x-y plane (flag=2) */
+ rotOrReflect(flag, nVertVals, nSubDivs, verts);
+ rotOrReflect(flag, nVertVals, nSubDivs, norms);
+
+ return nVertVals*flag;
+}
+
+/* verts array should be initialized to 0! */
+static int evalBezier(GLfloat cp[4][4][3], int nSubDivs, float (*bern_0)[4], int flag, GLfloat *verts)
+{
+ int nVerts = nSubDivs*nSubDivs;
+ int nVertVals = nVerts*3; /* number of values output for one patch, flag (2 or 4) indicates how many times we will write this to output */
+ int u,v,i,j,o;
+
+ /* generate vertices and coordinates for the patch */
+ for (u=0,o=0; u<nSubDivs; u++)
+ {
+ for (v=0; v<nSubDivs; v++, o+=3)
+ {
+ for (i=0; i<=3; i++)
+ {
+ float vert_0[3]={0};
+ for (j=0; j<=3; j++)
+ {
+ vert_0[0] += bern_0[v][j] * cp[i][j][0];
+ vert_0[1] += bern_0[v][j] * cp[i][j][1];
+ vert_0[2] += bern_0[v][j] * cp[i][j][2];
+ }
+
+ verts[o+0] += bern_0[u][i]*vert_0[0];
+ verts[o+1] += bern_0[u][i]*vert_0[1];
+ verts[o+2] += bern_0[u][i]*vert_0[2];
+ }
+ }
+ }
+
+ /* now based on flag either rotate patches around y axis to other 3 quadrants (flag=4) or reflect patch across x-y plane (flag=2) */
+ rotOrReflect(flag, nVertVals, nSubDivs, verts);
+
+ return nVertVals*flag;
+}
+
+static void fghTeaset( GLfloat scale, GLboolean useWireMode,
+ GLfloat (*cpdata)[3], int (*patchdata)[16],
+ GLushort *vertIdxs,
+ GLfloat *verts, GLfloat *norms, GLfloat *texcs,
+ GLfloat *lastScale, GLboolean *inited,
+ GLboolean needNormalFix, GLboolean rotFlip, GLfloat zOffset,
+ int nVerts, int nInputPatches, int nPatches, int nTriangles )
+{
+ /* for internal use */
+ int p,o;
+ GLfloat cp[4][4][3];
+ /* to hold pointers to static vars/arrays */
+ GLfloat (*bern_0)[4], (*bern_1)[4];
+ int nSubDivs;
+
+ /* Get relevant static arrays and variables */
+ bern_0 = useWireMode ? bernWire_0 : bernSolid_0;
+ bern_1 = useWireMode ? bernWire_1 : bernSolid_1;
+ nSubDivs = useWireMode ? GLUT_WIRE_N_SUBDIV : GLUT_SOLID_N_SUBDIV;
+
+ /* check if need to generate vertices */
+ if (!*inited || scale != *lastScale)
+ {
+ /* set vertex array to all 0 (not necessary for normals and vertex indices) */
+ memset(verts,0,nVerts*3*sizeof(GLfloat));
+
+ /* pregen Berstein polynomials and their first derivatives (for normals) */
+ if (!*inited)
+ pregenBernstein(nSubDivs,bern_0,bern_1);
+
+ /* generate vertices and normals */
+ for (p=0, o=0; p<nInputPatches; p++)
+ {
+ /* set flags for evalBezier function */
+ int flag = rotFlip?p<6?4:2:1; /* For teapot and teacup, first six patches get 3 copies (rotations), others get 2 copies (flips). No rotating or flipping at all for teaspoon */
+ int normalFix = needNormalFix?p==3?1:p==5?2:0:0; /* For teapot, fix normal vectors for vertices on top of lid (patch 4) and on middle of bottom (patch 6). Different flag value as different normal needed */
+
+ /* collect control points */
+ int i;
+ for (i=0; i<16; i++)
+ {
+ /* Original code draws with a 270° rot around X axis, a scaling and a translation along the Z-axis.
+ * Incorporating these in the control points is much cheaper than transforming all the vertices.
+ * Original:
+ * glRotated( 270.0, 1.0, 0.0, 0.0 );
+ * glScaled( 0.5 * scale, 0.5 * scale, 0.5 * scale );
+ * glTranslated( 0.0, 0.0, -zOffset ); -> was 1.5 for teapot, but should be 1.575 to center it on the Z axis. Teacup and teaspoon have different offsets
+ */
+ cp[i/4][i%4][0] = cpdata[patchdata[p][i]][0] *scale/2.f;
+ cp[i/4][i%4][1] = (cpdata[patchdata[p][i]][2]-zOffset)*scale/2.f;
+ cp[i/4][i%4][2] = -cpdata[patchdata[p][i]][1] *scale/2.f;
+ }
+
+ /* eval bezier patch */
+ if (!*inited) /* first time, generate normals as well */
+ o += evalBezierWithNorm(cp,nSubDivs,bern_0,bern_1, flag, normalFix, verts+o,norms+o);
+ else /* only need to regen vertices */
+ o += evalBezier(cp,nSubDivs,bern_0, flag, verts+o);
+ }
+ *lastScale = scale;
+
+ if (!*inited)
+ {
+ int r,c;
+ /* generate texture coordinates if solid teapot/teacup/teaspoon */
+ if (!useWireMode)
+ {
+ /* generate for first patch */
+ for (r=0,o=0; r<nSubDivs; r++)
+ {
+ GLfloat u = r/(nSubDivs-1.f);
+ for (c=0; c<nSubDivs; c++, o+=2)
+ {
+ GLfloat v = c/(nSubDivs-1.f);
+ texcs[o+0] = u;
+ texcs[o+1] = v;
+ }
+ }
+ /* copy it over for all the other patches */
+ for (p=1; p<nPatches; p++)
+ memcpy(texcs+p*nSubDivs*nSubDivs*2,texcs,nSubDivs*nSubDivs*2*sizeof(GLfloat));
+ }
+
+ /* build vertex index array */
+ if (useWireMode)
+ {
+ /* build vertex indices to draw teapot/teacup/teaspoon as line strips */
+ /* first strips along increasing u, constant v */
+ for (p=0, o=0; p<nPatches; p++)
+ {
+ int idx = nSubDivs*nSubDivs*p;
+ for (c=0; c<nSubDivs; c++)
+ for (r=0; r<nSubDivs; r++, o++)
+ vertIdxs[o] = idx+r*nSubDivs+c;
+ }
+
+ /* then strips along increasing v, constant u */
+ for (p=0; p<nPatches; p++) /* don't reset o, we continue appending! */
+ {
+ int idx = nSubDivs*nSubDivs*p;
+ for (r=0; r<nSubDivs; r++)
+ {
+ int loc = r*nSubDivs;
+ for (c=0; c<nSubDivs; c++, o++)
+ vertIdxs[o] = idx+loc+c;
+ }
+ }
+ }
+ else
+ {
+ /* build vertex indices to draw teapot/teacup/teaspoon as triangles */
+ for (p=0,o=0; p<nPatches; p++)
+ {
+ int idx = nSubDivs*nSubDivs*p;
+ for (r=0; r<nSubDivs-1; r++)
+ {
+ int loc = r*nSubDivs;
+ for (c=0; c<nSubDivs-1; c++, o+=6)
+ {
+ /* ABC ACD, where B and C are one row lower */
+ int row1 = idx+loc+c;
+ int row2 = row1+nSubDivs;
+
+ vertIdxs[o+0] = row1+0;
+ vertIdxs[o+1] = row2+0;
+ vertIdxs[o+2] = row2+1;
+
+ vertIdxs[o+3] = row1+0;
+ vertIdxs[o+4] = row2+1;
+ vertIdxs[o+5] = row1+1;
+ }
+ }
+ }
+ }
+
+ *inited = GL_TRUE;
+ }
+ }
+
+ /* draw */
+ if (useWireMode)
+ fghDrawGeometryWire (verts, norms, nVerts, vertIdxs, nPatches*nSubDivs*2, nSubDivs, GL_LINE_STRIP, NULL,0,0);
+ else
+ fghDrawGeometrySolid(verts, norms, texcs, nVerts, vertIdxs,1,nTriangles*3);