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