/*
- * freeglut_teapot.c
+ * fg_teapot.c
*
* Teapot(tm) rendering code.
*
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-/*
- * Original teapot code copyright follows:
- */
-
-/*
- * (c) Copyright 1993, Silicon Graphics, Inc.
- *
- * ALL RIGHTS RESERVED
- *
- * Permission to use, copy, modify, and distribute this software
- * for any purpose and without fee is hereby granted, provided
- * that the above copyright notice appear in all copies and that
- * both the copyright notice and this permission notice appear in
- * supporting documentation, and that the name of Silicon
- * Graphics, Inc. not be used in advertising or publicity
- * pertaining to distribution of the software without specific,
- * written prior permission.
- *
- * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU
- * "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR
- * OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO
- * EVENT SHALL SILICON GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE
- * ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER,
- * INCLUDING WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE,
- * SAVINGS OR REVENUE, OR THE CLAIMS OF THIRD PARTIES, WHETHER OR
- * NOT SILICON GRAPHICS, INC. HAS BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH LOSS, HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * ARISING OUT OF OR IN CONNECTION WITH THE POSSESSION, USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- *
- * US Government Users Restricted Rights
- *
- * Use, duplication, or disclosure by the Government is subject to
- * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
- * (c)(1)(ii) of the Rights in Technical Data and Computer
- * Software clause at DFARS 252.227-7013 and/or in similar or
- * successor clauses in the FAR or the DOD or NASA FAR
- * Supplement. Unpublished-- rights reserved under the copyright
- * laws of the United States. Contractor/manufacturer is Silicon
- * Graphics, Inc., 2011 N. Shoreline Blvd., Mountain View, CA
- * 94039-7311.
- *
- * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
+/* notes:
+ * the (very little) required math is found here: http://www.gamasutra.com/view/feature/131848/tessellation_of_4x4_bezier_patches_.php?print=1
+ * a much more optimized version is here, didn't bother to implement that: http://www.gamasutra.com/view/feature/131794/an_indepth_look_at_bicubic_bezier_.php?print=1
*/
#include <GL/freeglut.h>
#include "fg_internal.h"
#include "fg_teapot_data.h"
+/* -- STATIC VARS: CACHES ---------------------------------------------------- */
+
+/* General defs */
+#define GLUT_SOLID_N_SUBDIV 8
+#define GLUT_WIRE_N_SUBDIV 10
+
+/* Bernstein coefficients only have to be precomputed once (number of patch subdivisions is fixed)
+ * Can thus define arrays for them here, they will be filled upon first use.
+ * 3rd order Bezier surfaces have 4 Bernstein coeffs.
+ * Have separate caches for solid and wire as they use a different number of subdivisions
+ * _0 is for Bernstein polynomials, _1 for their first derivative (which we need for normals)
+ */
+static GLfloat bernWire_0 [GLUT_WIRE_N_SUBDIV] [4];
+static GLfloat bernWire_1 [GLUT_WIRE_N_SUBDIV] [4];
+static GLfloat bernSolid_0[GLUT_SOLID_N_SUBDIV][4];
+static GLfloat bernSolid_1[GLUT_SOLID_N_SUBDIV][4];
+
+/* Teapot defs */
+#define GLUT_TEAPOT_N_PATCHES (6*4 + 4*2) /* 6 patches are reproduced (rotated) 4 times, 4 patches (flipped) 2 times */
+#define GLUT_SOLID_TEAPOT_N_VERT GLUT_SOLID_N_SUBDIV*GLUT_SOLID_N_SUBDIV * GLUT_TEAPOT_N_PATCHES /* N_SUBDIV^2 vertices per patch */
+#define GLUT_SOLID_TEAPOT_N_TRI (GLUT_SOLID_N_SUBDIV-1)*(GLUT_SOLID_N_SUBDIV-1) * GLUT_TEAPOT_N_PATCHES * 2 /* if e.g. 7x7 vertices for each patch, there are 6*6 squares for each patch. Each square is decomposed into 2 triangles */
+
+#define GLUT_WIRE_TEAPOT_N_VERT GLUT_WIRE_N_SUBDIV*GLUT_WIRE_N_SUBDIV * GLUT_TEAPOT_N_PATCHES /* N_SUBDIV^2 vertices per patch */
+
+/* Bit of caching:
+ * vertex indices and normals only need to be generated once for
+ * a given number of subdivisions as they don't change with scale.
+ * Vertices can be cached and reused if scale didn't change.
+ */
+static GLushort vertIdxsTeapotS[GLUT_SOLID_TEAPOT_N_TRI*3];
+static GLfloat normsTeapotS [GLUT_SOLID_TEAPOT_N_VERT*3];
+static GLfloat vertsTeapotS [GLUT_SOLID_TEAPOT_N_VERT*3];
+static GLfloat texcsTeapotS [GLUT_SOLID_TEAPOT_N_VERT*2];
+static GLfloat lastScaleTeapotS = 0.f;
+static GLboolean initedTeapotS = GL_FALSE;
+
+static GLushort vertIdxsTeapotW[GLUT_WIRE_TEAPOT_N_VERT*2];
+static GLfloat normsTeapotW [GLUT_WIRE_TEAPOT_N_VERT*3];
+static GLfloat vertsTeapotW [GLUT_WIRE_TEAPOT_N_VERT*3];
+static GLfloat lastScaleTeapotW = 0.f;
+static GLboolean initedTeapotW = GL_FALSE;
+
+
+/* Teacup defs */
+#define GLUT_TEACUP_N_PATCHES (6*4 + 1*2) /* 6 patches are reproduced (rotated) 4 times, 1 patch (flipped) 2 times */
+#define GLUT_SOLID_TEACUP_N_VERT GLUT_SOLID_N_SUBDIV*GLUT_SOLID_N_SUBDIV * GLUT_TEACUP_N_PATCHES /* N_SUBDIV^2 vertices per patch */
+#define GLUT_SOLID_TEACUP_N_TRI (GLUT_SOLID_N_SUBDIV-1)*(GLUT_SOLID_N_SUBDIV-1) * GLUT_TEACUP_N_PATCHES * 2 /* if e.g. 7x7 vertices for each patch, there are 6*6 squares for each patch. Each square is decomposed into 2 triangles */
+
+#define GLUT_WIRE_TEACUP_N_VERT GLUT_WIRE_N_SUBDIV*GLUT_WIRE_N_SUBDIV * GLUT_TEACUP_N_PATCHES /* N_SUBDIV^2 vertices per patch */
+
+/* Bit of caching:
+ * vertex indices and normals only need to be generated once for
+ * a given number of subdivisions as they don't change with scale.
+ * Vertices can be cached and reused if scale didn't change.
+ */
+static GLushort vertIdxsTeacupS[GLUT_SOLID_TEACUP_N_TRI*3];
+static GLfloat normsTeacupS [GLUT_SOLID_TEACUP_N_VERT*3];
+static GLfloat vertsTeacupS [GLUT_SOLID_TEACUP_N_VERT*3];
+static GLfloat texcsTeacupS [GLUT_SOLID_TEACUP_N_VERT*2];
+static GLfloat lastScaleTeacupS = 0.f;
+static GLboolean initedTeacupS = GL_FALSE;
+
+static GLushort vertIdxsTeacupW[GLUT_WIRE_TEACUP_N_VERT*2];
+static GLfloat normsTeacupW [GLUT_WIRE_TEACUP_N_VERT*3];
+static GLfloat vertsTeacupW [GLUT_WIRE_TEACUP_N_VERT*3];
+static GLfloat lastScaleTeacupW = 0.f;
+static GLboolean initedTeacupW = GL_FALSE;
+
+
+/* Teaspoon defs */
+#define GLUT_TEASPOON_N_PATCHES GLUT_TEASPOON_N_INPUT_PATCHES
+#define GLUT_SOLID_TEASPOON_N_VERT GLUT_SOLID_N_SUBDIV*GLUT_SOLID_N_SUBDIV * GLUT_TEASPOON_N_PATCHES /* N_SUBDIV^2 vertices per patch */
+#define GLUT_SOLID_TEASPOON_N_TRI (GLUT_SOLID_N_SUBDIV-1)*(GLUT_SOLID_N_SUBDIV-1) * GLUT_TEASPOON_N_PATCHES * 2 /* if e.g. 7x7 vertices for each patch, there are 6*6 squares for each patch. Each square is decomposed into 2 triangles */
+
+#define GLUT_WIRE_TEASPOON_N_VERT GLUT_WIRE_N_SUBDIV*GLUT_WIRE_N_SUBDIV * GLUT_TEASPOON_N_PATCHES /* N_SUBDIV^2 vertices per patch */
+
+/* Bit of caching:
+ * vertex indices and normals only need to be generated once for
+ * a given number of subdivisions as they don't change with scale.
+ * Vertices can be cached and reused if scale didn't change.
+ */
+static GLushort vertIdxsTeaspoonS[GLUT_SOLID_TEASPOON_N_TRI*3];
+static GLfloat normsTeaspoonS [GLUT_SOLID_TEASPOON_N_VERT*3];
+static GLfloat vertsTeaspoonS [GLUT_SOLID_TEASPOON_N_VERT*3];
+static GLfloat texcsTeaspoonS [GLUT_SOLID_TEASPOON_N_VERT*2];
+static GLfloat lastScaleTeaspoonS = 0.f;
+static GLboolean initedTeaspoonS = GL_FALSE;
+
+static GLushort vertIdxsTeaspoonW[GLUT_WIRE_TEASPOON_N_VERT*2];
+static GLfloat normsTeaspoonW [GLUT_WIRE_TEASPOON_N_VERT*3];
+static GLfloat vertsTeaspoonW [GLUT_WIRE_TEASPOON_N_VERT*3];
+static GLfloat lastScaleTeaspoonW = 0.f;
+static GLboolean initedTeaspoonW = GL_FALSE;
+
+
+
/* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
+extern void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
+ GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart);
+extern void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
+ GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
+ GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2);
+
+/* evaluate 3rd order Bernstein polynomial and its 1st deriv */
+static void bernstein3(int i, GLfloat x, GLfloat *r0, GLfloat *r1)
+{
+ float invx = 1.f - x;
+ /* r0: zero order coeff, r1: first deriv coeff */
+ switch (i)
+ {
+ GLfloat temp;
+ case 0:
+ temp = invx*invx;
+ *r0 = invx * temp; /* invx * invx * invx */
+ *r1 = -3 * temp; /* -3 * invx * invx */
+ break;
+ case 1:
+ temp = invx*invx;
+ *r0 = 3 * x * temp; /* 3 * x * invx * invx */
+ *r1 = 3 * temp - 6 * x * invx; /* 3 * invx * invx - 6 * x * invx */
+ break;
+ case 2:
+ temp = x*x;
+ *r0 = 3 * temp * invx; /* 3 * x * x * invx */
+ *r1 = 6 * x * invx - 3 * temp; /* 6 * x * invx - 3 * x * x */
+ break;
+ case 3:
+ temp = x*x;
+ *r0 = x * temp; /* x * x * x */
+ *r1 = 3 * temp; /* 3 * x * x */
+ break;
+ default:
+ *r0 = *r1 = 0;
+ }
+}
-static void fghTeapot( GLint grid, GLdouble scale, GLenum type )
+static void pregenBernstein(int nSubDivs, GLfloat (*bern_0)[4], GLfloat (*bern_1)[4])
{
-#if defined(_WIN32_WCE)
- int i, numV=sizeof(strip_vertices)/4, numI=sizeof(strip_normals)/4;
-#else
- double p[4][4][3], q[4][4][3], r[4][4][3], s[4][4][3];
- long i, j, k, l;
-#endif
-
- glPushAttrib( GL_ENABLE_BIT | GL_EVAL_BIT );
- glEnable( GL_AUTO_NORMAL );
- glEnable( GL_NORMALIZE );
- glEnable( GL_MAP2_VERTEX_3 );
- glEnable( GL_MAP2_TEXTURE_COORD_2 );
-
- glPushMatrix();
- glRotated( 270.0, 1.0, 0.0, 0.0 );
- glScaled( 0.5 * scale, 0.5 * scale, 0.5 * scale );
- glTranslated( 0.0, 0.0, -1.5 );
-
-#if defined(_WIN32_WCE)
- glRotated( 90.0, 1.0, 0.0, 0.0 );
- glBegin( GL_TRIANGLE_STRIP );
-
- for( i = 0; i < numV-1; i++ )
+ int s,i;
+ for (s=0; s<nSubDivs; s++)
{
- int vidx = strip_vertices[i],
- nidx = strip_normals[i];
+ GLfloat x = s/(nSubDivs-1.f);
+ for (i=0; i<4; i++) /* 3rd order polynomial */
+ bernstein3(i,x,bern_0[s]+i,bern_1[s]+i);
+ }
+}
+
+/* based on flag either rotate patches around y axis to other 3 quadrants (flag=4) or reflect patch across x-y plane (flag=2) */
+static void rotOrReflect(int flag, int nVals, int nSubDivs, GLfloat *vals)
+{
+ int u,i,o;
- if( vidx != -1 )
+ if (flag==4)
+ {
+ int i1=nVals, i2=nVals*2, i3=nVals*3;
+ for (o=0; o<nVals; o+=3)
{
- glNormal3fv( normals[nidx] );
- glVertex3fv( vertices[vidx] );
+ /* 90° rotation */
+ vals[i1+o+0] = vals[o+2];
+ vals[i1+o+1] = vals[o+1];
+ vals[i1+o+2] = -vals[o+0];
+ /* 180° rotation */
+ vals[i2+o+0] = -vals[o+0];
+ vals[i2+o+1] = vals[o+1];
+ vals[i2+o+2] = -vals[o+2];
+ /* 270° rotation */
+ vals[i3+o+0] = -vals[o+2];
+ vals[i3+o+1] = vals[o+1];
+ vals[i3+o+2] = vals[o+0];
}
- else
+ }
+ else if (flag==2)
+ {
+ /* copy over values, reversing row order to keep winding correct, and negating z to perform the flip */
+ for (u=0; u<nSubDivs; u++) /* per row */
{
- glEnd();
- glBegin( GL_TRIANGLE_STRIP );
+ int off = (nSubDivs-u-1)*nSubDivs*3; /* read last row first from the already existing rows */
+ o = nVals + u *nSubDivs*3; /* write last row as first row to output */
+ for (i=0; i<nSubDivs*3; i+=3, o+=3) /* each row has nSubDivs points consisting of three values */
+ {
+ vals[o+0] = vals[off+i+0];
+ vals[o+1] = vals[off+i+1];
+ vals[o+2] = -vals[off+i+2];
+ }
}
}
+}
+
+/* verts array should be initialized to 0! */
+static int evalBezierWithNorm(GLfloat cp[4][4][3], int nSubDivs, float (*bern_0)[4], float (*bern_1)[4], int flag, int normalFix, GLfloat *verts, GLfloat *norms)
+{
+ 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 normals, get two tangents at the vertex using partial derivatives of 2D Bezier grid */
+ float tan1[3]={0}, tan2[3]={0}, len;
+ for (i=0; i<=3; i++)
+ {
+ float vert_0[3]={0}, vert_1[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];
- glEnd();
-#else
- for (i = 0; i < 10; i++) {
- for (j = 0; j < 4; j++) {
- for (k = 0; k < 4; k++) {
- for (l = 0; l < 3; l++) {
- p[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l];
- q[j][k][l] = cpdata[patchdata[i][j * 4 + (3 - k)]][l];
- if (l == 1)
- q[j][k][l] *= -1.0;
- if (i < 6) {
- r[j][k][l] =
- cpdata[patchdata[i][j * 4 + (3 - k)]][l];
- if (l == 0)
- r[j][k][l] *= -1.0;
- s[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l];
- if (l == 0)
- s[j][k][l] *= -1.0;
- if (l == 1)
- s[j][k][l] *= -1.0;
+ vert_1[0] += bern_1[v][j] * cp[i][j][0];
+ vert_1[1] += bern_1[v][j] * cp[i][j][1];
+ vert_1[2] += bern_1[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];
+
+ tan1[0] += bern_0[u][i]*vert_1[0];
+ tan1[1] += bern_0[u][i]*vert_1[1];
+ tan1[2] += bern_0[u][i]*vert_1[2];
+ tan2[0] += bern_1[u][i]*vert_0[0];
+ tan2[1] += bern_1[u][i]*vert_0[1];
+ tan2[2] += bern_1[u][i]*vert_0[2];
}
- }
+ /* get normal through cross product of the two tangents of the vertex */
+ norms[o+0] = tan1[1] * tan2[2] - tan1[2] * tan2[1];
+ norms[o+1] = tan1[2] * tan2[0] - tan1[0] * tan2[2];
+ norms[o+2] = tan1[0] * tan2[1] - tan1[1] * tan2[0];
+ len = (GLfloat)sqrt(norms[o+0] * norms[o+0] + norms[o+1] * norms[o+1] + norms[o+2] * norms[o+2]);
+ norms[o+0] /= len;
+ norms[o+1] /= len;
+ norms[o+2] /= len;
+ }
+ }
+
+ /* Fix normal vector if needed */
+ if (normalFix)
+ {
+ for (o=0; o<nSubDivs*3; o+=3) /* whole first row (first nSubDivs normals) is broken: replace normals for the whole row */
+ {
+ norms[o+0] = 0.f;
+ norms[o+1] = normalFix==1? 1.f:-1.f;
+ norms[o+2] = 0.f;
}
- }
-
- glMap2d(GL_MAP2_TEXTURE_COORD_2, 0.0, 1.0, 2, 2, 0.0, 1.0, 4, 2,
- &tex[0][0][0]);
- glMap2d(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4,
- &p[0][0][0]);
- glMapGrid2d(grid, 0.0, 1.0, grid, 0.0, 1.0);
- glEvalMesh2(type, 0, grid, 0, grid);
- glMap2d(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4,
- &q[0][0][0]);
- glEvalMesh2(type, 0, grid, 0, grid);
- if (i < 6) {
- glMap2d(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4,
- &r[0][0][0]);
- glEvalMesh2(type, 0, grid, 0, grid);
- glMap2d(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4,
- &s[0][0][0]);
- glEvalMesh2(type, 0, grid, 0, grid);
- }
}
-#endif /* defined(_WIN32_WCE) */
- glPopMatrix();
- glPopAttrib();
+ /* 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);
}
/* -- INTERFACE FUNCTIONS -------------------------------------------------- */
/*
- * Renders a beautiful wired teapot...
+ * Renders a wired teapot...
*/
-void FGAPIENTRY glutWireTeapot( GLdouble size )
+void FGAPIENTRY glutWireTeapot( double size )
{
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeapot" );
- /* We will use the general teapot rendering code */
- fghTeapot( 10, size, GL_LINE );
+ fghTeaset( (GLfloat)size, GL_TRUE,
+ cpdata_teapot, patchdata_teapot,
+ vertIdxsTeapotW,
+ vertsTeapotW, normsTeapotW, NULL,
+ &lastScaleTeapotW, &initedTeapotW,
+ GL_TRUE, GL_TRUE, 1.575f,
+ GLUT_WIRE_TEAPOT_N_VERT, GLUT_TEAPOT_N_INPUT_PATCHES, GLUT_TEAPOT_N_PATCHES, 0);
}
/*
- * Renders a beautiful filled teapot...
+ * Renders a filled teapot...
*/
-void FGAPIENTRY glutSolidTeapot( GLdouble size )
+void FGAPIENTRY glutSolidTeapot( double size )
{
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeapot" );
- /* We will use the general teapot rendering code */
- fghTeapot( 7, size, GL_FILL );
+ fghTeaset( (GLfloat)size, GL_FALSE,
+ cpdata_teapot, patchdata_teapot,
+ vertIdxsTeapotS,
+ vertsTeapotS, normsTeapotS, texcsTeapotS,
+ &lastScaleTeapotS, &initedTeapotS,
+ GL_TRUE, GL_TRUE, 1.575f,
+ GLUT_SOLID_TEAPOT_N_VERT, GLUT_TEAPOT_N_INPUT_PATCHES, GLUT_TEAPOT_N_PATCHES, GLUT_SOLID_TEAPOT_N_TRI);
}
-/*** END OF FILE ***/
+/*
+ * Renders a wired teacup...
+ */
+void FGAPIENTRY glutWireTeacup( double size )
+{
+ FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeacup" );
+ fghTeaset( (GLfloat)size/2.5f, GL_TRUE,
+ cpdata_teacup, patchdata_teacup,
+ vertIdxsTeacupW,
+ vertsTeacupW, normsTeacupW, NULL,
+ &lastScaleTeacupW, &initedTeacupW,
+ GL_FALSE, GL_TRUE, 1.5121f,
+ GLUT_WIRE_TEACUP_N_VERT, GLUT_TEACUP_N_INPUT_PATCHES, GLUT_TEACUP_N_PATCHES, 0);
+}
+/*
+ * Renders a filled teacup...
+ */
+void FGAPIENTRY glutSolidTeacup( double size )
+{
+ FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeacup" );
+ fghTeaset( (GLfloat)size/2.5f, GL_FALSE,
+ cpdata_teacup, patchdata_teacup,
+ vertIdxsTeacupS,
+ vertsTeacupS, normsTeacupS, texcsTeacupS,
+ &lastScaleTeacupS, &initedTeacupS,
+ GL_FALSE, GL_TRUE, 1.5121f,
+ GLUT_SOLID_TEACUP_N_VERT, GLUT_TEACUP_N_INPUT_PATCHES, GLUT_TEACUP_N_PATCHES, GLUT_SOLID_TEACUP_N_TRI);
+}
+/*
+ * Renders a wired teaspoon...
+ */
+void FGAPIENTRY glutWireTeaspoon( double size )
+{
+ FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeaspoon" );
+ fghTeaset( (GLfloat)size/2.5f, GL_TRUE,
+ cpdata_teaspoon, patchdata_teaspoon,
+ vertIdxsTeaspoonW,
+ vertsTeaspoonW, normsTeaspoonW, NULL,
+ &lastScaleTeaspoonW, &initedTeaspoonW,
+ GL_FALSE, GL_FALSE, -0.0315f,
+ GLUT_WIRE_TEASPOON_N_VERT, GLUT_TEASPOON_N_INPUT_PATCHES, GLUT_TEASPOON_N_PATCHES, 0);
+}
+
+/*
+ * Renders a filled teaspoon...
+ */
+void FGAPIENTRY glutSolidTeaspoon( double size )
+{
+ FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeaspoon" );
+ fghTeaset( (GLfloat)size/2.5f, GL_FALSE,
+ cpdata_teaspoon, patchdata_teaspoon,
+ vertIdxsTeaspoonS,
+ vertsTeaspoonS, normsTeaspoonS, texcsTeaspoonS,
+ &lastScaleTeaspoonS, &initedTeaspoonS,
+ GL_FALSE, GL_FALSE, -0.0315f,
+ GLUT_SOLID_TEASPOON_N_VERT, GLUT_TEASPOON_N_INPUT_PATCHES, GLUT_TEASPOON_N_PATCHES, GLUT_SOLID_TEASPOON_N_TRI);
+}
+/*** END OF FILE ***/