| 1 | /* | 
|---|
| 2 | * fg_teapot.c | 
|---|
| 3 | * | 
|---|
| 4 | * Teapot(tm) rendering code. | 
|---|
| 5 | * | 
|---|
| 6 | * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. | 
|---|
| 7 | * Written by Pawel W. Olszta, <olszta@sourceforge.net> | 
|---|
| 8 | * Creation date: Fri Dec 24 1999 | 
|---|
| 9 | * | 
|---|
| 10 | * Permission is hereby granted, free of charge, to any person obtaining a | 
|---|
| 11 | * copy of this software and associated documentation files (the "Software"), | 
|---|
| 12 | * to deal in the Software without restriction, including without limitation | 
|---|
| 13 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
|---|
| 14 | * and/or sell copies of the Software, and to permit persons to whom the | 
|---|
| 15 | * Software is furnished to do so, subject to the following conditions: | 
|---|
| 16 | * | 
|---|
| 17 | * The above copyright notice and this permission notice shall be included | 
|---|
| 18 | * in all copies or substantial portions of the Software. | 
|---|
| 19 | * | 
|---|
| 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | 
|---|
| 21 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|---|
| 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | 
|---|
| 23 | * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | 
|---|
| 24 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
|---|
| 25 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
|---|
| 26 | */ | 
|---|
| 27 |  | 
|---|
| 28 | /* notes: | 
|---|
| 29 | * the (very little) required math is found here: http://www.gamasutra.com/view/feature/131848/tessellation_of_4x4_bezier_patches_.php?print=1 | 
|---|
| 30 | * 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 | 
|---|
| 31 | */ | 
|---|
| 32 |  | 
|---|
| 33 | #include <GL/freeglut.h> | 
|---|
| 34 | #include "fg_internal.h" | 
|---|
| 35 | #include "fg_teapot_data.h" | 
|---|
| 36 |  | 
|---|
| 37 | /* -- STATIC VARS: CACHES ---------------------------------------------------- */ | 
|---|
| 38 |  | 
|---|
| 39 | /* General defs */ | 
|---|
| 40 | #define GLUT_SOLID_N_SUBDIV  8 | 
|---|
| 41 | #define GLUT_WIRE_N_SUBDIV   10 | 
|---|
| 42 |  | 
|---|
| 43 | /* Bernstein coefficients only have to be precomputed once (number of patch subdivisions is fixed) | 
|---|
| 44 | * Can thus define arrays for them here, they will be filled upon first use. | 
|---|
| 45 | * 3rd order Bezier surfaces have 4 Bernstein coeffs. | 
|---|
| 46 | * Have separate caches for solid and wire as they use a different number of subdivisions | 
|---|
| 47 | * _0 is for Bernstein polynomials, _1 for their first derivative (which we need for normals) | 
|---|
| 48 | */ | 
|---|
| 49 | static GLfloat bernWire_0 [GLUT_WIRE_N_SUBDIV] [4]; | 
|---|
| 50 | static GLfloat bernWire_1 [GLUT_WIRE_N_SUBDIV] [4]; | 
|---|
| 51 | static GLfloat bernSolid_0[GLUT_SOLID_N_SUBDIV][4]; | 
|---|
| 52 | static GLfloat bernSolid_1[GLUT_SOLID_N_SUBDIV][4]; | 
|---|
| 53 |  | 
|---|
| 54 | /* Teapot defs */ | 
|---|
| 55 | #define GLUT_TEAPOT_N_PATCHES       (6*4 + 4*2)                                                                     /* 6 patches are reproduced (rotated) 4 times, 4 patches (flipped) 2 times */ | 
|---|
| 56 | #define GLUT_SOLID_TEAPOT_N_VERT    GLUT_SOLID_N_SUBDIV*GLUT_SOLID_N_SUBDIV * GLUT_TEAPOT_N_PATCHES                 /* N_SUBDIV^2 vertices per patch */ | 
|---|
| 57 | #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 */ | 
|---|
| 58 |  | 
|---|
| 59 | #define GLUT_WIRE_TEAPOT_N_VERT     GLUT_WIRE_N_SUBDIV*GLUT_WIRE_N_SUBDIV * GLUT_TEAPOT_N_PATCHES                   /* N_SUBDIV^2 vertices per patch */ | 
|---|
| 60 |  | 
|---|
| 61 | /* Bit of caching: | 
|---|
| 62 | * vertex indices and normals only need to be generated once for | 
|---|
| 63 | * a given number of subdivisions as they don't change with scale. | 
|---|
| 64 | * Vertices can be cached and reused if scale didn't change. | 
|---|
| 65 | */ | 
|---|
| 66 | static GLushort vertIdxsTeapotS[GLUT_SOLID_TEAPOT_N_TRI*3]; | 
|---|
| 67 | static GLfloat  normsTeapotS   [GLUT_SOLID_TEAPOT_N_VERT*3]; | 
|---|
| 68 | static GLfloat  vertsTeapotS   [GLUT_SOLID_TEAPOT_N_VERT*3]; | 
|---|
| 69 | static GLfloat  texcsTeapotS   [GLUT_SOLID_TEAPOT_N_VERT*2]; | 
|---|
| 70 | static GLfloat  lastScaleTeapotS = 0.f; | 
|---|
| 71 | static GLboolean initedTeapotS   = GL_FALSE; | 
|---|
| 72 |  | 
|---|
| 73 | static GLushort vertIdxsTeapotW[GLUT_WIRE_TEAPOT_N_VERT*2]; | 
|---|
| 74 | static GLfloat  normsTeapotW   [GLUT_WIRE_TEAPOT_N_VERT*3]; | 
|---|
| 75 | static GLfloat  vertsTeapotW   [GLUT_WIRE_TEAPOT_N_VERT*3]; | 
|---|
| 76 | static GLfloat  lastScaleTeapotW = 0.f; | 
|---|
| 77 | static GLboolean initedTeapotW   = GL_FALSE; | 
|---|
| 78 |  | 
|---|
| 79 |  | 
|---|
| 80 | /* Teacup defs */ | 
|---|
| 81 | #define GLUT_TEACUP_N_PATCHES       (6*4 + 1*2)                                                                     /* 6 patches are reproduced (rotated) 4 times, 1 patch (flipped) 2 times */ | 
|---|
| 82 | #define GLUT_SOLID_TEACUP_N_VERT    GLUT_SOLID_N_SUBDIV*GLUT_SOLID_N_SUBDIV * GLUT_TEACUP_N_PATCHES                 /* N_SUBDIV^2 vertices per patch */ | 
|---|
| 83 | #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 */ | 
|---|
| 84 |  | 
|---|
| 85 | #define GLUT_WIRE_TEACUP_N_VERT     GLUT_WIRE_N_SUBDIV*GLUT_WIRE_N_SUBDIV * GLUT_TEACUP_N_PATCHES                   /* N_SUBDIV^2 vertices per patch */ | 
|---|
| 86 |  | 
|---|
| 87 | /* Bit of caching: | 
|---|
| 88 | * vertex indices and normals only need to be generated once for | 
|---|
| 89 | * a given number of subdivisions as they don't change with scale. | 
|---|
| 90 | * Vertices can be cached and reused if scale didn't change. | 
|---|
| 91 | */ | 
|---|
| 92 | static GLushort vertIdxsTeacupS[GLUT_SOLID_TEACUP_N_TRI*3]; | 
|---|
| 93 | static GLfloat  normsTeacupS   [GLUT_SOLID_TEACUP_N_VERT*3]; | 
|---|
| 94 | static GLfloat  vertsTeacupS   [GLUT_SOLID_TEACUP_N_VERT*3]; | 
|---|
| 95 | static GLfloat  texcsTeacupS   [GLUT_SOLID_TEACUP_N_VERT*2]; | 
|---|
| 96 | static GLfloat  lastScaleTeacupS = 0.f; | 
|---|
| 97 | static GLboolean initedTeacupS   = GL_FALSE; | 
|---|
| 98 |  | 
|---|
| 99 | static GLushort vertIdxsTeacupW[GLUT_WIRE_TEACUP_N_VERT*2]; | 
|---|
| 100 | static GLfloat  normsTeacupW   [GLUT_WIRE_TEACUP_N_VERT*3]; | 
|---|
| 101 | static GLfloat  vertsTeacupW   [GLUT_WIRE_TEACUP_N_VERT*3]; | 
|---|
| 102 | static GLfloat  lastScaleTeacupW = 0.f; | 
|---|
| 103 | static GLboolean initedTeacupW   = GL_FALSE; | 
|---|
| 104 |  | 
|---|
| 105 |  | 
|---|
| 106 | /* Teaspoon defs */ | 
|---|
| 107 | #define GLUT_TEASPOON_N_PATCHES     GLUT_TEASPOON_N_INPUT_PATCHES | 
|---|
| 108 | #define GLUT_SOLID_TEASPOON_N_VERT  GLUT_SOLID_N_SUBDIV*GLUT_SOLID_N_SUBDIV * GLUT_TEASPOON_N_PATCHES               /* N_SUBDIV^2 vertices per patch */ | 
|---|
| 109 | #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 */ | 
|---|
| 110 |  | 
|---|
| 111 | #define GLUT_WIRE_TEASPOON_N_VERT   GLUT_WIRE_N_SUBDIV*GLUT_WIRE_N_SUBDIV * GLUT_TEASPOON_N_PATCHES                 /* N_SUBDIV^2 vertices per patch */ | 
|---|
| 112 |  | 
|---|
| 113 | /* Bit of caching: | 
|---|
| 114 | * vertex indices and normals only need to be generated once for | 
|---|
| 115 | * a given number of subdivisions as they don't change with scale. | 
|---|
| 116 | * Vertices can be cached and reused if scale didn't change. | 
|---|
| 117 | */ | 
|---|
| 118 | static GLushort vertIdxsTeaspoonS[GLUT_SOLID_TEASPOON_N_TRI*3]; | 
|---|
| 119 | static GLfloat  normsTeaspoonS   [GLUT_SOLID_TEASPOON_N_VERT*3]; | 
|---|
| 120 | static GLfloat  vertsTeaspoonS   [GLUT_SOLID_TEASPOON_N_VERT*3]; | 
|---|
| 121 | static GLfloat  texcsTeaspoonS   [GLUT_SOLID_TEASPOON_N_VERT*2]; | 
|---|
| 122 | static GLfloat  lastScaleTeaspoonS = 0.f; | 
|---|
| 123 | static GLboolean initedTeaspoonS   = GL_FALSE; | 
|---|
| 124 |  | 
|---|
| 125 | static GLushort vertIdxsTeaspoonW[GLUT_WIRE_TEASPOON_N_VERT*2]; | 
|---|
| 126 | static GLfloat  normsTeaspoonW   [GLUT_WIRE_TEASPOON_N_VERT*3]; | 
|---|
| 127 | static GLfloat  vertsTeaspoonW   [GLUT_WIRE_TEASPOON_N_VERT*3]; | 
|---|
| 128 | static GLfloat  lastScaleTeaspoonW = 0.f; | 
|---|
| 129 | static GLboolean initedTeaspoonW   = GL_FALSE; | 
|---|
| 130 |  | 
|---|
| 131 |  | 
|---|
| 132 |  | 
|---|
| 133 | /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */ | 
|---|
| 134 | extern void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices, | 
|---|
| 135 | GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart); | 
|---|
| 136 | extern void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numVertices, | 
|---|
| 137 | GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode, | 
|---|
| 138 | GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2); | 
|---|
| 139 |  | 
|---|
| 140 | /* evaluate 3rd order Bernstein polynomial and its 1st deriv */ | 
|---|
| 141 | static void bernstein3(int i, GLfloat x, GLfloat *r0, GLfloat *r1) | 
|---|
| 142 | { | 
|---|
| 143 | float invx = 1.f - x; | 
|---|
| 144 |  | 
|---|
| 145 | /* r0: zero order coeff, r1: first deriv coeff */ | 
|---|
| 146 | switch (i) | 
|---|
| 147 | { | 
|---|
| 148 | GLfloat temp; | 
|---|
| 149 | case 0: | 
|---|
| 150 | temp = invx*invx; | 
|---|
| 151 | *r0 = invx * temp;                  /* invx * invx * invx */ | 
|---|
| 152 | *r1 = -3 * temp;                    /*   -3 * invx * invx */ | 
|---|
| 153 | break; | 
|---|
| 154 | case 1: | 
|---|
| 155 | temp = invx*invx; | 
|---|
| 156 | *r0 = 3 * x * temp;                 /* 3 * x * invx * invx */ | 
|---|
| 157 | *r1 = 3 * temp  -  6 * x * invx;    /* 3 * invx * invx  -  6 * x * invx */ | 
|---|
| 158 | break; | 
|---|
| 159 | case 2: | 
|---|
| 160 | temp = x*x; | 
|---|
| 161 | *r0 = 3 * temp * invx;              /* 3 * x * x * invx */ | 
|---|
| 162 | *r1 = 6 * x * invx  -  3 * temp;    /* 6 * x * invx  -  3 * x * x */ | 
|---|
| 163 | break; | 
|---|
| 164 | case 3: | 
|---|
| 165 | temp = x*x; | 
|---|
| 166 | *r0 = x * temp;                     /* x * x * x */ | 
|---|
| 167 | *r1 = 3 * temp;                     /* 3 * x * x */ | 
|---|
| 168 | break; | 
|---|
| 169 | default: | 
|---|
| 170 | *r0 = *r1 = 0; | 
|---|
| 171 | } | 
|---|
| 172 | } | 
|---|
| 173 |  | 
|---|
| 174 | static void pregenBernstein(int nSubDivs, GLfloat (*bern_0)[4], GLfloat (*bern_1)[4]) | 
|---|
| 175 | { | 
|---|
| 176 | int s,i; | 
|---|
| 177 | for (s=0; s<nSubDivs; s++) | 
|---|
| 178 | { | 
|---|
| 179 | GLfloat x = s/(nSubDivs-1.f); | 
|---|
| 180 | for (i=0; i<4; i++) /* 3rd order polynomial */ | 
|---|
| 181 | bernstein3(i,x,bern_0[s]+i,bern_1[s]+i); | 
|---|
| 182 | } | 
|---|
| 183 | } | 
|---|
| 184 |  | 
|---|
| 185 | /* based on flag either rotate patches around y axis to other 3 quadrants (flag=4) or reflect patch across x-y plane (flag=2) */ | 
|---|
| 186 | static void rotOrReflect(int flag, int nVals, int nSubDivs, GLfloat *vals) | 
|---|
| 187 | { | 
|---|
| 188 | int u,i,o; | 
|---|
| 189 |  | 
|---|
| 190 | if (flag==4) | 
|---|
| 191 | { | 
|---|
| 192 | int i1=nVals, i2=nVals*2, i3=nVals*3; | 
|---|
| 193 | for (o=0; o<nVals; o+=3) | 
|---|
| 194 | { | 
|---|
| 195 | /* 90° rotation */ | 
|---|
| 196 | vals[i1+o+0] =  vals[o+2]; | 
|---|
| 197 | vals[i1+o+1] =  vals[o+1]; | 
|---|
| 198 | vals[i1+o+2] = -vals[o+0]; | 
|---|
| 199 | /* 180° rotation */ | 
|---|
| 200 | vals[i2+o+0] = -vals[o+0]; | 
|---|
| 201 | vals[i2+o+1] =  vals[o+1]; | 
|---|
| 202 | vals[i2+o+2] = -vals[o+2]; | 
|---|
| 203 | /* 270° rotation */ | 
|---|
| 204 | vals[i3+o+0] = -vals[o+2]; | 
|---|
| 205 | vals[i3+o+1] =  vals[o+1]; | 
|---|
| 206 | vals[i3+o+2] =  vals[o+0]; | 
|---|
| 207 | } | 
|---|
| 208 | } | 
|---|
| 209 | else if (flag==2) | 
|---|
| 210 | { | 
|---|
| 211 | /* copy over values, reversing row order to keep winding correct, and negating z to perform the flip */ | 
|---|
| 212 | for (u=0; u<nSubDivs; u++)  /* per row */ | 
|---|
| 213 | { | 
|---|
| 214 | int off =   (nSubDivs-u-1)*nSubDivs*3;  /* read last row first from the already existing rows */ | 
|---|
| 215 | o       = nVals + u   *nSubDivs*3;      /* write last row as first row to output */ | 
|---|
| 216 | for (i=0; i<nSubDivs*3; i+=3, o+=3)     /* each row has nSubDivs points consisting of three values */ | 
|---|
| 217 | { | 
|---|
| 218 | vals[o+0] =  vals[off+i+0]; | 
|---|
| 219 | vals[o+1] =  vals[off+i+1]; | 
|---|
| 220 | vals[o+2] = -vals[off+i+2]; | 
|---|
| 221 | } | 
|---|
| 222 | } | 
|---|
| 223 | } | 
|---|
| 224 | } | 
|---|
| 225 |  | 
|---|
| 226 | /* verts array should be initialized to 0! */ | 
|---|
| 227 | 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) | 
|---|
| 228 | { | 
|---|
| 229 | int nVerts    = nSubDivs*nSubDivs; | 
|---|
| 230 | 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 */ | 
|---|
| 231 | int u,v,i,j,o; | 
|---|
| 232 |  | 
|---|
| 233 | /* generate vertices and coordinates for the patch */ | 
|---|
| 234 | for (u=0,o=0; u<nSubDivs; u++) | 
|---|
| 235 | { | 
|---|
| 236 | for (v=0; v<nSubDivs; v++, o+=3) | 
|---|
| 237 | { | 
|---|
| 238 | /* for normals, get two tangents at the vertex using partial derivatives of 2D Bezier grid */ | 
|---|
| 239 | float tan1[3]={0}, tan2[3]={0}, len; | 
|---|
| 240 | for (i=0; i<=3; i++) | 
|---|
| 241 | { | 
|---|
| 242 | float vert_0[3]={0}, vert_1[3]={0}; | 
|---|
| 243 | for (j=0; j<=3; j++) | 
|---|
| 244 | { | 
|---|
| 245 | vert_0[0] += bern_0[v][j] * cp[i][j][0]; | 
|---|
| 246 | vert_0[1] += bern_0[v][j] * cp[i][j][1]; | 
|---|
| 247 | vert_0[2] += bern_0[v][j] * cp[i][j][2]; | 
|---|
| 248 |  | 
|---|
| 249 | vert_1[0] += bern_1[v][j] * cp[i][j][0]; | 
|---|
| 250 | vert_1[1] += bern_1[v][j] * cp[i][j][1]; | 
|---|
| 251 | vert_1[2] += bern_1[v][j] * cp[i][j][2]; | 
|---|
| 252 | } | 
|---|
| 253 |  | 
|---|
| 254 | verts[o+0] += bern_0[u][i]*vert_0[0]; | 
|---|
| 255 | verts[o+1] += bern_0[u][i]*vert_0[1]; | 
|---|
| 256 | verts[o+2] += bern_0[u][i]*vert_0[2]; | 
|---|
| 257 |  | 
|---|
| 258 | tan1[0] += bern_0[u][i]*vert_1[0]; | 
|---|
| 259 | tan1[1] += bern_0[u][i]*vert_1[1]; | 
|---|
| 260 | tan1[2] += bern_0[u][i]*vert_1[2]; | 
|---|
| 261 | tan2[0] += bern_1[u][i]*vert_0[0]; | 
|---|
| 262 | tan2[1] += bern_1[u][i]*vert_0[1]; | 
|---|
| 263 | tan2[2] += bern_1[u][i]*vert_0[2]; | 
|---|
| 264 | } | 
|---|
| 265 | /* get normal through cross product of the two tangents of the vertex */ | 
|---|
| 266 | norms[o+0] = tan1[1] * tan2[2] - tan1[2] * tan2[1]; | 
|---|
| 267 | norms[o+1] = tan1[2] * tan2[0] - tan1[0] * tan2[2]; | 
|---|
| 268 | norms[o+2] = tan1[0] * tan2[1] - tan1[1] * tan2[0]; | 
|---|
| 269 | len = (GLfloat)sqrt(norms[o+0] * norms[o+0] + norms[o+1] * norms[o+1] + norms[o+2] * norms[o+2]); | 
|---|
| 270 | norms[o+0] /= len; | 
|---|
| 271 | norms[o+1] /= len; | 
|---|
| 272 | norms[o+2] /= len; | 
|---|
| 273 | } | 
|---|
| 274 | } | 
|---|
| 275 |  | 
|---|
| 276 | /* Fix normal vector if needed */ | 
|---|
| 277 | if (normalFix) | 
|---|
| 278 | { | 
|---|
| 279 | for (o=0; o<nSubDivs*3; o+=3) /* whole first row (first nSubDivs normals) is broken: replace normals for the whole row */ | 
|---|
| 280 | { | 
|---|
| 281 | norms[o+0] = 0.f; | 
|---|
| 282 | norms[o+1] = normalFix==1? 1.f:-1.f; | 
|---|
| 283 | norms[o+2] = 0.f; | 
|---|
| 284 | } | 
|---|
| 285 | } | 
|---|
| 286 |  | 
|---|
| 287 | /* 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) */ | 
|---|
| 288 | rotOrReflect(flag, nVertVals, nSubDivs, verts); | 
|---|
| 289 | rotOrReflect(flag, nVertVals, nSubDivs, norms); | 
|---|
| 290 |  | 
|---|
| 291 | return nVertVals*flag; | 
|---|
| 292 | } | 
|---|
| 293 |  | 
|---|
| 294 | /* verts array should be initialized to 0! */ | 
|---|
| 295 | static int evalBezier(GLfloat cp[4][4][3], int nSubDivs, float (*bern_0)[4], int flag, GLfloat *verts) | 
|---|
| 296 | { | 
|---|
| 297 | int nVerts    = nSubDivs*nSubDivs; | 
|---|
| 298 | 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 */ | 
|---|
| 299 | int u,v,i,j,o; | 
|---|
| 300 |  | 
|---|
| 301 | /* generate vertices and coordinates for the patch */ | 
|---|
| 302 | for (u=0,o=0; u<nSubDivs; u++) | 
|---|
| 303 | { | 
|---|
| 304 | for (v=0; v<nSubDivs; v++, o+=3) | 
|---|
| 305 | { | 
|---|
| 306 | for (i=0; i<=3; i++) | 
|---|
| 307 | { | 
|---|
| 308 | float vert_0[3]={0}; | 
|---|
| 309 | for (j=0; j<=3; j++) | 
|---|
| 310 | { | 
|---|
| 311 | vert_0[0] += bern_0[v][j] * cp[i][j][0]; | 
|---|
| 312 | vert_0[1] += bern_0[v][j] * cp[i][j][1]; | 
|---|
| 313 | vert_0[2] += bern_0[v][j] * cp[i][j][2]; | 
|---|
| 314 | } | 
|---|
| 315 |  | 
|---|
| 316 | verts[o+0] += bern_0[u][i]*vert_0[0]; | 
|---|
| 317 | verts[o+1] += bern_0[u][i]*vert_0[1]; | 
|---|
| 318 | verts[o+2] += bern_0[u][i]*vert_0[2]; | 
|---|
| 319 | } | 
|---|
| 320 | } | 
|---|
| 321 | } | 
|---|
| 322 |  | 
|---|
| 323 | /* 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) */ | 
|---|
| 324 | rotOrReflect(flag, nVertVals, nSubDivs, verts); | 
|---|
| 325 |  | 
|---|
| 326 | return nVertVals*flag; | 
|---|
| 327 | } | 
|---|
| 328 |  | 
|---|
| 329 | static void fghTeaset( GLfloat scale, GLboolean useWireMode, | 
|---|
| 330 | GLfloat (*cpdata)[3], int (*patchdata)[16], | 
|---|
| 331 | GLushort *vertIdxs, | 
|---|
| 332 | GLfloat *verts, GLfloat *norms, GLfloat *texcs, | 
|---|
| 333 | GLfloat *lastScale, GLboolean *inited, | 
|---|
| 334 | GLboolean needNormalFix, GLboolean rotFlip, GLfloat zOffset, | 
|---|
| 335 | int nVerts, int nInputPatches, int nPatches, int nTriangles ) | 
|---|
| 336 | { | 
|---|
| 337 | /* for internal use */ | 
|---|
| 338 | int p,o; | 
|---|
| 339 | GLfloat cp[4][4][3]; | 
|---|
| 340 | /* to hold pointers to static vars/arrays */ | 
|---|
| 341 | GLfloat (*bern_0)[4], (*bern_1)[4]; | 
|---|
| 342 | int nSubDivs; | 
|---|
| 343 |  | 
|---|
| 344 | /* Get relevant static arrays and variables */ | 
|---|
| 345 | bern_0      = useWireMode ? bernWire_0                : bernSolid_0; | 
|---|
| 346 | bern_1      = useWireMode ? bernWire_1                : bernSolid_1; | 
|---|
| 347 | nSubDivs    = useWireMode ? GLUT_WIRE_N_SUBDIV        : GLUT_SOLID_N_SUBDIV; | 
|---|
| 348 |  | 
|---|
| 349 | /* check if need to generate vertices */ | 
|---|
| 350 | if (!*inited || scale != *lastScale) | 
|---|
| 351 | { | 
|---|
| 352 | /* set vertex array to all 0 (not necessary for normals and vertex indices) */ | 
|---|
| 353 | memset(verts,0,nVerts*3*sizeof(GLfloat)); | 
|---|
| 354 |  | 
|---|
| 355 | /* pregen Berstein polynomials and their first derivatives (for normals) */ | 
|---|
| 356 | if (!*inited) | 
|---|
| 357 | pregenBernstein(nSubDivs,bern_0,bern_1); | 
|---|
| 358 |  | 
|---|
| 359 | /* generate vertices and normals */ | 
|---|
| 360 | for (p=0, o=0; p<nInputPatches; p++) | 
|---|
| 361 | { | 
|---|
| 362 | /* set flags for evalBezier function */ | 
|---|
| 363 | 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 */ | 
|---|
| 364 | 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 */ | 
|---|
| 365 |  | 
|---|
| 366 | /* collect control points */ | 
|---|
| 367 | int i; | 
|---|
| 368 | for (i=0; i<16; i++) | 
|---|
| 369 | { | 
|---|
| 370 | /* Original code draws with a 270° rot around X axis, a scaling and a translation along the Z-axis. | 
|---|
| 371 | * Incorporating these in the control points is much cheaper than transforming all the vertices. | 
|---|
| 372 | * Original: | 
|---|
| 373 | * glRotated( 270.0, 1.0, 0.0, 0.0 ); | 
|---|
| 374 | * glScaled( 0.5 * scale, 0.5 * scale, 0.5 * scale ); | 
|---|
| 375 | * 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 | 
|---|
| 376 | */ | 
|---|
| 377 | cp[i/4][i%4][0] =  cpdata[patchdata[p][i]][0]         *scale/2.f; | 
|---|
| 378 | cp[i/4][i%4][1] = (cpdata[patchdata[p][i]][2]-zOffset)*scale/2.f; | 
|---|
| 379 | cp[i/4][i%4][2] = -cpdata[patchdata[p][i]][1]         *scale/2.f; | 
|---|
| 380 | } | 
|---|
| 381 |  | 
|---|
| 382 | /* eval bezier patch */ | 
|---|
| 383 | if (!*inited)   /* first time, generate normals as well */ | 
|---|
| 384 | o += evalBezierWithNorm(cp,nSubDivs,bern_0,bern_1, flag, normalFix, verts+o,norms+o); | 
|---|
| 385 | else            /* only need to regen vertices */ | 
|---|
| 386 | o += evalBezier(cp,nSubDivs,bern_0, flag, verts+o); | 
|---|
| 387 | } | 
|---|
| 388 | *lastScale = scale; | 
|---|
| 389 |  | 
|---|
| 390 | if (!*inited) | 
|---|
| 391 | { | 
|---|
| 392 | int r,c; | 
|---|
| 393 | /* generate texture coordinates if solid teapot/teacup/teaspoon */ | 
|---|
| 394 | if (!useWireMode) | 
|---|
| 395 | { | 
|---|
| 396 | /* generate for first patch */ | 
|---|
| 397 | for (r=0,o=0; r<nSubDivs; r++) | 
|---|
| 398 | { | 
|---|
| 399 | GLfloat u = r/(nSubDivs-1.f); | 
|---|
| 400 | for (c=0; c<nSubDivs; c++, o+=2) | 
|---|
| 401 | { | 
|---|
| 402 | GLfloat v = c/(nSubDivs-1.f); | 
|---|
| 403 | texcs[o+0] = u; | 
|---|
| 404 | texcs[o+1] = v; | 
|---|
| 405 | } | 
|---|
| 406 | } | 
|---|
| 407 | /* copy it over for all the other patches */ | 
|---|
| 408 | for (p=1; p<nPatches; p++) | 
|---|
| 409 | memcpy(texcs+p*nSubDivs*nSubDivs*2,texcs,nSubDivs*nSubDivs*2*sizeof(GLfloat)); | 
|---|
| 410 | } | 
|---|
| 411 |  | 
|---|
| 412 | /* build vertex index array */ | 
|---|
| 413 | if (useWireMode) | 
|---|
| 414 | { | 
|---|
| 415 | /* build vertex indices to draw teapot/teacup/teaspoon as line strips */ | 
|---|
| 416 | /* first strips along increasing u, constant v */ | 
|---|
| 417 | for (p=0, o=0; p<nPatches; p++) | 
|---|
| 418 | { | 
|---|
| 419 | int idx = nSubDivs*nSubDivs*p; | 
|---|
| 420 | for (c=0; c<nSubDivs; c++) | 
|---|
| 421 | for (r=0; r<nSubDivs; r++, o++) | 
|---|
| 422 | vertIdxs[o] = idx+r*nSubDivs+c; | 
|---|
| 423 | } | 
|---|
| 424 |  | 
|---|
| 425 | /* then strips along increasing v, constant u */ | 
|---|
| 426 | for (p=0; p<nPatches; p++) /* don't reset o, we continue appending! */ | 
|---|
| 427 | { | 
|---|
| 428 | int idx = nSubDivs*nSubDivs*p; | 
|---|
| 429 | for (r=0; r<nSubDivs; r++) | 
|---|
| 430 | { | 
|---|
| 431 | int loc = r*nSubDivs; | 
|---|
| 432 | for (c=0; c<nSubDivs; c++, o++) | 
|---|
| 433 | vertIdxs[o] = idx+loc+c; | 
|---|
| 434 | } | 
|---|
| 435 | } | 
|---|
| 436 | } | 
|---|
| 437 | else | 
|---|
| 438 | { | 
|---|
| 439 | /* build vertex indices to draw teapot/teacup/teaspoon as triangles */ | 
|---|
| 440 | for (p=0,o=0; p<nPatches; p++) | 
|---|
| 441 | { | 
|---|
| 442 | int idx = nSubDivs*nSubDivs*p; | 
|---|
| 443 | for (r=0; r<nSubDivs-1; r++) | 
|---|
| 444 | { | 
|---|
| 445 | int loc = r*nSubDivs; | 
|---|
| 446 | for (c=0; c<nSubDivs-1; c++, o+=6) | 
|---|
| 447 | { | 
|---|
| 448 | /* ABC ACD, where B and C are one row lower */ | 
|---|
| 449 | int row1 = idx+loc+c; | 
|---|
| 450 | int row2 = row1+nSubDivs; | 
|---|
| 451 |  | 
|---|
| 452 | vertIdxs[o+0] = row1+0; | 
|---|
| 453 | vertIdxs[o+1] = row2+0; | 
|---|
| 454 | vertIdxs[o+2] = row2+1; | 
|---|
| 455 |  | 
|---|
| 456 | vertIdxs[o+3] = row1+0; | 
|---|
| 457 | vertIdxs[o+4] = row2+1; | 
|---|
| 458 | vertIdxs[o+5] = row1+1; | 
|---|
| 459 | } | 
|---|
| 460 | } | 
|---|
| 461 | } | 
|---|
| 462 | } | 
|---|
| 463 |  | 
|---|
| 464 | *inited = GL_TRUE; | 
|---|
| 465 | } | 
|---|
| 466 | } | 
|---|
| 467 |  | 
|---|
| 468 | /* draw */ | 
|---|
| 469 | if (useWireMode) | 
|---|
| 470 | fghDrawGeometryWire (verts, norms,        nVerts, vertIdxs, nPatches*nSubDivs*2, nSubDivs, GL_LINE_STRIP, NULL,0,0); | 
|---|
| 471 | else | 
|---|
| 472 | fghDrawGeometrySolid(verts, norms, texcs, nVerts, vertIdxs,1,nTriangles*3); | 
|---|
| 473 | } | 
|---|
| 474 |  | 
|---|
| 475 |  | 
|---|
| 476 | /* -- INTERFACE FUNCTIONS -------------------------------------------------- */ | 
|---|
| 477 |  | 
|---|
| 478 | /* | 
|---|
| 479 | * Renders a wired teapot... | 
|---|
| 480 | */ | 
|---|
| 481 | void FGAPIENTRY glutWireTeapot( double size ) | 
|---|
| 482 | { | 
|---|
| 483 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeapot"); | 
|---|
| 484 | fghTeaset( (GLfloat)size, GL_TRUE, | 
|---|
| 485 | cpdata_teapot, patchdata_teapot, | 
|---|
| 486 | vertIdxsTeapotW, | 
|---|
| 487 | vertsTeapotW, normsTeapotW, NULL, | 
|---|
| 488 | &lastScaleTeapotW, &initedTeapotW, | 
|---|
| 489 | GL_TRUE, GL_TRUE, 1.575f, | 
|---|
| 490 | GLUT_WIRE_TEAPOT_N_VERT, GLUT_TEAPOT_N_INPUT_PATCHES, GLUT_TEAPOT_N_PATCHES, 0); | 
|---|
| 491 | } | 
|---|
| 492 |  | 
|---|
| 493 | /* | 
|---|
| 494 | * Renders a filled teapot... | 
|---|
| 495 | */ | 
|---|
| 496 | void FGAPIENTRY glutSolidTeapot( double size ) | 
|---|
| 497 | { | 
|---|
| 498 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeapot"); | 
|---|
| 499 | fghTeaset( (GLfloat)size, GL_FALSE, | 
|---|
| 500 | cpdata_teapot, patchdata_teapot, | 
|---|
| 501 | vertIdxsTeapotS, | 
|---|
| 502 | vertsTeapotS, normsTeapotS, texcsTeapotS, | 
|---|
| 503 | &lastScaleTeapotS, &initedTeapotS, | 
|---|
| 504 | GL_TRUE, GL_TRUE, 1.575f, | 
|---|
| 505 | GLUT_SOLID_TEAPOT_N_VERT, GLUT_TEAPOT_N_INPUT_PATCHES, GLUT_TEAPOT_N_PATCHES, GLUT_SOLID_TEAPOT_N_TRI); | 
|---|
| 506 | } | 
|---|
| 507 |  | 
|---|
| 508 |  | 
|---|
| 509 | /* | 
|---|
| 510 | * Renders a wired teacup... | 
|---|
| 511 | */ | 
|---|
| 512 | void FGAPIENTRY glutWireTeacup( double size ) | 
|---|
| 513 | { | 
|---|
| 514 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeacup"); | 
|---|
| 515 | fghTeaset( (GLfloat)size/2.5f, GL_TRUE, | 
|---|
| 516 | cpdata_teacup, patchdata_teacup, | 
|---|
| 517 | vertIdxsTeacupW, | 
|---|
| 518 | vertsTeacupW, normsTeacupW, NULL, | 
|---|
| 519 | &lastScaleTeacupW, &initedTeacupW, | 
|---|
| 520 | GL_FALSE, GL_TRUE, 1.5121f, | 
|---|
| 521 | GLUT_WIRE_TEACUP_N_VERT, GLUT_TEACUP_N_INPUT_PATCHES, GLUT_TEACUP_N_PATCHES, 0); | 
|---|
| 522 | } | 
|---|
| 523 |  | 
|---|
| 524 | /* | 
|---|
| 525 | * Renders a filled teacup... | 
|---|
| 526 | */ | 
|---|
| 527 | void FGAPIENTRY glutSolidTeacup( double size ) | 
|---|
| 528 | { | 
|---|
| 529 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeacup"); | 
|---|
| 530 | fghTeaset( (GLfloat)size/2.5f, GL_FALSE, | 
|---|
| 531 | cpdata_teacup, patchdata_teacup, | 
|---|
| 532 | vertIdxsTeacupS, | 
|---|
| 533 | vertsTeacupS, normsTeacupS, texcsTeacupS, | 
|---|
| 534 | &lastScaleTeacupS, &initedTeacupS, | 
|---|
| 535 | GL_FALSE, GL_TRUE, 1.5121f, | 
|---|
| 536 | GLUT_SOLID_TEACUP_N_VERT, GLUT_TEACUP_N_INPUT_PATCHES, GLUT_TEACUP_N_PATCHES, GLUT_SOLID_TEACUP_N_TRI); | 
|---|
| 537 | } | 
|---|
| 538 |  | 
|---|
| 539 |  | 
|---|
| 540 | /* | 
|---|
| 541 | * Renders a wired teaspoon... | 
|---|
| 542 | */ | 
|---|
| 543 | void FGAPIENTRY glutWireTeaspoon( double size ) | 
|---|
| 544 | { | 
|---|
| 545 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeaspoon"); | 
|---|
| 546 | fghTeaset( (GLfloat)size/2.5f, GL_TRUE, | 
|---|
| 547 | cpdata_teaspoon, patchdata_teaspoon, | 
|---|
| 548 | vertIdxsTeaspoonW, | 
|---|
| 549 | vertsTeaspoonW, normsTeaspoonW, NULL, | 
|---|
| 550 | &lastScaleTeaspoonW, &initedTeaspoonW, | 
|---|
| 551 | GL_FALSE, GL_FALSE, -0.0315f, | 
|---|
| 552 | GLUT_WIRE_TEASPOON_N_VERT, GLUT_TEASPOON_N_INPUT_PATCHES, GLUT_TEASPOON_N_PATCHES, 0); | 
|---|
| 553 | } | 
|---|
| 554 |  | 
|---|
| 555 | /* | 
|---|
| 556 | * Renders a filled teaspoon... | 
|---|
| 557 | */ | 
|---|
| 558 | void FGAPIENTRY glutSolidTeaspoon( double size ) | 
|---|
| 559 | { | 
|---|
| 560 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeaspoon"); | 
|---|
| 561 | fghTeaset( (GLfloat)size/2.5f, GL_FALSE, | 
|---|
| 562 | cpdata_teaspoon, patchdata_teaspoon, | 
|---|
| 563 | vertIdxsTeaspoonS, | 
|---|
| 564 | vertsTeaspoonS, normsTeaspoonS, texcsTeaspoonS, | 
|---|
| 565 | &lastScaleTeaspoonS, &initedTeaspoonS, | 
|---|
| 566 | GL_FALSE, GL_FALSE, -0.0315f, | 
|---|
| 567 | GLUT_SOLID_TEASPOON_N_VERT, GLUT_TEASPOON_N_INPUT_PATCHES, GLUT_TEASPOON_N_PATCHES, GLUT_SOLID_TEASPOON_N_TRI); | 
|---|
| 568 | } | 
|---|
| 569 |  | 
|---|
| 570 | /*** END OF FILE ***/ | 
|---|
| 571 |  | 
|---|