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 | |