4 * Teapot(tm) rendering code.
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
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:
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
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.
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 * teacup and teaspoon data: ftp://ftp.funet.fi/pub/sci/graphics/packages/objects/teaset.tar.Z (figure out this sampling scheme)
34 #include <GL/freeglut.h>
35 #include "fg_internal.h"
36 #include "fg_teapot_data.h"
38 /* -- STATIC VARS: CACHES ---------------------------------------------------- */
41 #define GLUT_TEAPOT_N_PATCHES (6*4 + 4*2) /* 6 patches are reproduced (rotated) 4 times, 4 patches (flipped) 2 times */
42 #define GLUT_SOLID_TEAPOT_N_SUBDIV 10
43 #define GLUT_SOLID_TEAPOT_N_VERT GLUT_SOLID_TEAPOT_N_SUBDIV*GLUT_SOLID_TEAPOT_N_SUBDIV * GLUT_TEAPOT_N_PATCHES /* N_SUBDIV^2 vertices per patch */
44 #define GLUT_SOLID_TEAPOT_N_TRI (GLUT_SOLID_TEAPOT_N_SUBDIV-1)*(GLUT_SOLID_TEAPOT_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 */
46 #define GLUT_WIRE_TEAPOT_N_SUBDIV 7
47 #define GLUT_WIRE_TEAPOT_N_VERT GLUT_WIRE_TEAPOT_N_SUBDIV*GLUT_WIRE_TEAPOT_N_SUBDIV * GLUT_TEAPOT_N_PATCHES /* N_SUBDIV^2 vertices per patch */
49 /* Bernstein coefficients only have to be precomputed once (number of patch subdivisions is fixed)
50 * Can thus define arrays for them here, they will be filled upon first use.
51 * 3rd order Bezier surfaces have 4 Bernstein coeffs.
52 * Have separate caches for solid and wire as they use a different number of subdivisions
53 * _0 is for Bernstein polynomials, _1 for their first derivative (which we need for normals)
55 static GLfloat bernWire_0 [GLUT_WIRE_TEAPOT_N_SUBDIV] [4];
56 static GLfloat bernWire_1 [GLUT_WIRE_TEAPOT_N_SUBDIV] [4];
57 static GLfloat bernSolid_0[GLUT_SOLID_TEAPOT_N_SUBDIV][4];
58 static GLfloat bernSolid_1[GLUT_SOLID_TEAPOT_N_SUBDIV][4];
61 * vertex indices and normals only need to be generated once for
62 * a given number of subdivisions as they don't change with scale.
63 * Vertices can be cached and reused if scale didn't change.
65 static GLushort vertIdxsSolid[GLUT_SOLID_TEAPOT_N_TRI*3];
66 static GLfloat normsSolid [GLUT_SOLID_TEAPOT_N_VERT*3];
67 static GLfloat vertsSolid [GLUT_SOLID_TEAPOT_N_VERT*3];
68 static GLfloat texcsSolid [GLUT_SOLID_TEAPOT_N_VERT*2];
69 static GLfloat lastScaleSolid = 0.f;
70 static GLboolean initedSolid = GL_FALSE;
72 static GLushort vertIdxsWire [GLUT_WIRE_TEAPOT_N_VERT*2];
73 static GLfloat normsWire [GLUT_WIRE_TEAPOT_N_VERT*3];
74 static GLfloat vertsWire [GLUT_WIRE_TEAPOT_N_VERT*3];
75 static GLfloat lastScaleWire = 0.f;
76 static GLboolean initedWire = GL_FALSE;
78 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
79 extern void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
80 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart);
81 extern void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
82 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
83 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2);
85 /* evaluate 3rd order Bernstein polynomial and its 1st deriv */
86 static void bernstein3(int i, GLfloat x, GLfloat *r0, GLfloat *r1)
90 /* r0: zero order coeff, r1: first deriv coeff */
96 *r0 = invx * temp; /* invx * invx * invx */
97 *r1 = -3 * temp; /* -3 * invx * invx */
101 *r0 = 3 * x * temp; /* 3 * x * invx * invx */
102 *r1 = 3 * temp - 6 * x * invx; /* 3 * invx * invx - 6 * x * invx */
106 *r0 = 3 * temp * invx; /* 3 * x * x * invx */
107 *r1 = 6 * x * invx - 3 * temp; /* 6 * x * invx - 3 * x * x */
111 *r0 = x * temp; /* x * x * x */
112 *r1 = 3 * temp; /* 3 * x * x */
119 static void pregenBernstein(int nSubDivs, GLfloat (*bern_0)[4], GLfloat (*bern_1)[4])
122 for (s=0; s<nSubDivs; s++)
124 GLfloat x = s/(nSubDivs-1.f);
125 for (i=0; i<4; i++) /* 3rd order polynomial */
126 bernstein3(i,x,bern_0[s]+i,bern_1[s]+i);
130 /* based on flag either rotate patches around y axis to other 3 quadrants (flag=4) or reflect patch across x-y plane (flag=2) */
131 static void rotOrReflect(int flag, int nVals, int nSubDivs, GLfloat *vals)
137 int i1=nVals, i2=nVals*2, i3=nVals*3;
138 for (o=0; o<nVals; o+=3)
141 vals[i1+o+0] = vals[o+2];
142 vals[i1+o+1] = vals[o+1];
143 vals[i1+o+2] = -vals[o+0];
145 vals[i2+o+0] = -vals[o+0];
146 vals[i2+o+1] = vals[o+1];
147 vals[i2+o+2] = -vals[o+2];
149 vals[i3+o+0] = -vals[o+2];
150 vals[i3+o+1] = vals[o+1];
151 vals[i3+o+2] = vals[o+0];
156 /* copy over values, reversing row order to keep winding correct, and negating z to perform the flip */
157 for (u=0; u<nSubDivs; u++) /* per row */
159 int off = (nSubDivs-u-1)*nSubDivs*3; /* read last row first from the already existing rows */
160 o = nVals + u *nSubDivs*3; /* write last row as first row to output */
161 for (i=0; i<nSubDivs*3; i+=3, o+=3) /* each row has nSubDivs points consisting of three values */
163 vals[o+0] = vals[off+i+0];
164 vals[o+1] = vals[off+i+1];
165 vals[o+2] = -vals[off+i+2];
171 /* verts array should be initialized to 0! */
172 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)
174 int nVerts = nSubDivs*nSubDivs;
175 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 */
178 /* generate vertices and coordinates for the patch */
179 for (u=0,o=0; u<nSubDivs; u++)
181 for (v=0; v<nSubDivs; v++, o+=3)
183 /* for normals, get two tangents at the vertex using partial derivatives of 2D Bezier grid */
184 float tan1[3]={0}, tan2[3]={0}, len;
187 float vert_0[3]={0}, vert_1[3]={0};
190 vert_0[0] += bern_0[v][j] * cp[i][j][0];
191 vert_0[1] += bern_0[v][j] * cp[i][j][1];
192 vert_0[2] += bern_0[v][j] * cp[i][j][2];
194 vert_1[0] += bern_1[v][j] * cp[i][j][0];
195 vert_1[1] += bern_1[v][j] * cp[i][j][1];
196 vert_1[2] += bern_1[v][j] * cp[i][j][2];
199 verts[o+0] += bern_0[u][i]*vert_0[0];
200 verts[o+1] += bern_0[u][i]*vert_0[1];
201 verts[o+2] += bern_0[u][i]*vert_0[2];
203 tan1[0] += bern_0[u][i]*vert_1[0];
204 tan1[1] += bern_0[u][i]*vert_1[1];
205 tan1[2] += bern_0[u][i]*vert_1[2];
206 tan2[0] += bern_1[u][i]*vert_0[0];
207 tan2[1] += bern_1[u][i]*vert_0[1];
208 tan2[2] += bern_1[u][i]*vert_0[2];
210 /* get normal through cross product of the two tangents of the vertex */
211 norms[o+0] = tan1[1] * tan2[2] - tan1[2] * tan2[1];
212 norms[o+1] = tan1[2] * tan2[0] - tan1[0] * tan2[2];
213 norms[o+2] = tan1[0] * tan2[1] - tan1[1] * tan2[0];
214 len = (GLfloat)sqrt(norms[o+0] * norms[o+0] + norms[o+1] * norms[o+1] + norms[o+2] * norms[o+2]);
221 /* Fix normal vector if needed */
224 for (o=0; o<nSubDivs*3; o+=3) /* whole first row (first nSubDivs normals) is broken: replace normals for the whole row */
227 norms[o+1] = normalFix==1? 1.f:-1.f;
232 /* 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) */
233 rotOrReflect(flag, nVertVals, nSubDivs, verts);
234 rotOrReflect(flag, nVertVals, nSubDivs, norms);
236 return nVertVals*flag;
239 /* verts array should be initialized to 0! */
240 static int evalBezier(GLfloat cp[4][4][3], int nSubDivs, float (*bern_0)[4], int flag, GLfloat *verts)
242 int nVerts = nSubDivs*nSubDivs;
243 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 */
246 /* generate vertices and coordinates for the patch */
247 for (u=0,o=0; u<nSubDivs; u++)
249 for (v=0; v<nSubDivs; v++, o+=3)
256 vert_0[0] += bern_0[v][j] * cp[i][j][0];
257 vert_0[1] += bern_0[v][j] * cp[i][j][1];
258 vert_0[2] += bern_0[v][j] * cp[i][j][2];
261 verts[o+0] += bern_0[u][i]*vert_0[0];
262 verts[o+1] += bern_0[u][i]*vert_0[1];
263 verts[o+2] += bern_0[u][i]*vert_0[2];
268 /* 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) */
269 rotOrReflect(flag, nVertVals, nSubDivs, verts);
271 return nVertVals*flag;
274 static void fghTeapot( GLfloat scale, GLboolean useWireMode )
276 /* for internal use */
279 /* to hold pointers to static vars/arrays */
280 GLfloat (*bern_0)[4], (*bern_1)[4];
281 GLfloat *verts, *norms, *lastScale;
284 int nSubDivs, nVerts;
286 /* Get relevant static arrays and variables */
287 bern_0 = useWireMode ? bernWire_0 : bernSolid_0;
288 bern_1 = useWireMode ? bernWire_1 : bernSolid_1;
289 verts = useWireMode ? vertsWire : vertsSolid;
290 norms = useWireMode ? normsWire : normsSolid;
291 lastScale = useWireMode ? &lastScaleWire : &lastScaleSolid;
292 vertIdxs = useWireMode ? vertIdxsWire : vertIdxsSolid;
293 inited = useWireMode ? &initedWire : &initedSolid;
294 nSubDivs = useWireMode ? GLUT_WIRE_TEAPOT_N_SUBDIV : GLUT_SOLID_TEAPOT_N_SUBDIV;
295 nVerts = useWireMode ? GLUT_WIRE_TEAPOT_N_VERT : GLUT_SOLID_TEAPOT_N_VERT;
297 /* check if need to generate vertices */
298 if (!*inited || scale != *lastScale)
301 /* set vertex array to all 0 (not necessary for normals and vertex indices) */
302 memset(verts,0,nVerts*3*sizeof(GLfloat));
304 /* pregen Berstein polynomials and their first derivatives (for normals) */
306 pregenBernstein(nSubDivs,bern_0,bern_1);
308 /* generate vertices and normals */
309 for (p=0, o=0; p<GLUT_TEAPOT_N_INPUT_PATCHES; p++)
311 /* set flags for evalBezier function */
312 int flag = p<6?4:2; /* first six patches get 3 copies (rotations), last four get 2 copies (flips) */
313 int normalFix = p==3?1:p==5?2:0; /* 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 */
315 /* collect control points */
319 /* Original code draw with a 270° rot around X axis, a scaling and a translation along the Z-axis.
320 * Incorporating these in the control points is much cheaper than transforming all the vertices.
322 * glRotated( 270.0, 1.0, 0.0, 0.0 );
323 * glScaled( 0.5 * scale, 0.5 * scale, 0.5 * scale );
324 * glTranslated( 0.0, 0.0, -1.5 );
326 cp[i/4][i%4][0] = cpdata[patchdata[p][i]][0] *scale/2.f;
327 cp[i/4][i%4][1] = (cpdata[patchdata[p][i]][2]-1.5f)*scale/2.f;
328 cp[i/4][i%4][2] = -cpdata[patchdata[p][i]][1] *scale/2.f;
331 /* eval bezier patch */
332 if (!*inited) /* first time, generate normals as well */
333 o += evalBezierWithNorm(cp,nSubDivs,bern_0,bern_1, flag, normalFix, verts+o,norms+o);
334 else /* only need to regen vertices */
335 o += evalBezier(cp,nSubDivs,bern_0, flag, verts+o);
342 /* generate texture coordinates if solid teapot */
345 /* generate for first patch */
346 for (r=0,o=0; r<nSubDivs; r++)
348 GLfloat u = r/(nSubDivs-1.f);
349 for (c=0; c<nSubDivs; c++, o+=2)
351 GLfloat v = c/(nSubDivs-1.f);
356 /* copy it over for all the other patches */
357 for (p=1; p<GLUT_TEAPOT_N_PATCHES; p++)
358 memcpy(texcsSolid+p*nSubDivs*nSubDivs*2,texcsSolid,nSubDivs*nSubDivs*2*sizeof(GLfloat));
361 /* build vertex index array */
364 /* build vertex indices to draw teapot as line strips */
365 /* first strips along increasing u, constant v */
366 for (p=0, o=0; p<GLUT_TEAPOT_N_PATCHES; p++)
368 int idx = nSubDivs*nSubDivs*p;
369 for (c=0; c<nSubDivs; c++)
370 for (r=0; r<nSubDivs; r++, o++)
371 vertIdxs[o] = idx+r*nSubDivs+c;
374 /* then strips along increasing v, constant u */
375 for (p=0; p<GLUT_TEAPOT_N_PATCHES; p++) /* don't reset o, we continue appending! */
377 int idx = nSubDivs*nSubDivs*p;
378 for (r=0; r<nSubDivs; r++)
380 int loc = r*nSubDivs;
381 for (c=0; c<nSubDivs; c++, o++)
382 vertIdxs[o] = idx+loc+c;
388 /* build vertex indices to draw teapot as triangles */
389 for (p=0,o=0; p<GLUT_TEAPOT_N_PATCHES; p++)
391 int idx = nSubDivs*nSubDivs*p;
392 for (r=0; r<nSubDivs-1; r++)
394 int loc = r*nSubDivs;
395 for (c=0; c<nSubDivs-1; c++, o+=6)
397 /* ABC ACD, where B and C are one row lower */
398 int row1 = idx+loc+c;
399 int row2 = row1+nSubDivs;
401 vertIdxs[o+0] = row1+0;
402 vertIdxs[o+1] = row2+0;
403 vertIdxs[o+2] = row2+1;
405 vertIdxs[o+3] = row1+0;
406 vertIdxs[o+4] = row2+1;
407 vertIdxs[o+5] = row1+1;
418 // TODO: texture coordinates
420 fghDrawGeometryWire(verts, norms, nVerts, vertIdxs, GLUT_TEAPOT_N_PATCHES*nSubDivs*2, nSubDivs, GL_LINE_STRIP, NULL,0,0);
422 fghDrawGeometrySolid(verts,norms,texcsSolid,nVerts,vertIdxs,1,GLUT_SOLID_TEAPOT_N_TRI*3);
426 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
429 * Renders a wired teapot...
431 void FGAPIENTRY glutWireTeapot( double size )
433 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeapot" );
434 /* We will use the general teapot rendering code */
435 fghTeapot( (GLfloat)size, GL_TRUE );
439 * Renders a filled teapot...
441 void FGAPIENTRY glutSolidTeapot( double size )
443 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeapot" );
444 /* We will use the general teapot rendering code */
445 fghTeapot( (GLfloat)size, GL_FALSE );
448 /*** END OF FILE ***/