teapot now using vertex arrays or vertex attribute arrays, using handwritten Bezier...
[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 /* Teapot defs */
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 */
45
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 */
48
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)
54  */
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];
59
60 /* Bit of caching:
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.
64  */
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;
71
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;
77
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);
84
85 /* evaluate 3rd order Bernstein polynomial and its 1st deriv */
86 static void bernstein3(int i, GLfloat x, GLfloat *r0, GLfloat *r1)
87 {
88     float invx = 1.f - x;
89
90     /* r0: zero order coeff, r1: first deriv coeff */
91     switch (i)
92     {
93         GLfloat temp;
94     case 0:
95         temp = invx*invx;
96         *r0 = invx * temp;                  /* invx * invx * invx */
97         *r1 = -3 * temp;                    /*   -3 * invx * invx */
98         break;
99     case 1:
100         temp = invx*invx;
101         *r0 = 3 * x * temp;                 /* 3 * x * invx * invx */
102         *r1 = 3 * temp  -  6 * x * invx;    /* 3 * invx * invx  -  6 * x * invx */
103         break;
104     case 2:
105         temp = x*x;
106         *r0 = 3 * temp * invx;              /* 3 * x * x * invx */
107         *r1 = 6 * x * invx  -  3 * temp;    /* 6 * x * invx  -  3 * x * x */
108         break;
109     case 3:
110         temp = x*x;
111         *r0 = x * temp;                     /* x * x * x */
112         *r1 = 3 * temp;                     /* 3 * x * x */
113         break;
114     default:
115         *r0 = *r1 = 0;
116     }
117 }
118
119 static void pregenBernstein(int nSubDivs, GLfloat (*bern_0)[4], GLfloat (*bern_1)[4])
120 {
121     int s,i;
122     for (s=0; s<nSubDivs; s++)
123     {
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);
127     }
128 }
129
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)
132 {
133     int u,i,o;
134
135     if (flag==4)
136     {
137         int i1=nVals, i2=nVals*2, i3=nVals*3;
138         for (o=0; o<nVals; o+=3)
139         {
140             /* 90° rotation */
141             vals[i1+o+0] =  vals[o+2];
142             vals[i1+o+1] =  vals[o+1];
143             vals[i1+o+2] = -vals[o+0];
144             /* 180° rotation */
145             vals[i2+o+0] = -vals[o+0];
146             vals[i2+o+1] =  vals[o+1];
147             vals[i2+o+2] = -vals[o+2];
148             /* 270° rotation */
149             vals[i3+o+0] = -vals[o+2];
150             vals[i3+o+1] =  vals[o+1];
151             vals[i3+o+2] =  vals[o+0];
152         }
153     }
154     else if (flag==2)
155     {
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 */
158         {
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 */
162             {
163                 vals[o+0] =  vals[off+i+0];
164                 vals[o+1] =  vals[off+i+1];
165                 vals[o+2] = -vals[off+i+2];
166             }
167         }
168     }
169 }
170
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)
173 {
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 */
176     int u,v,i,j,o;
177
178     /* generate vertices and coordinates for the patch */
179     for (u=0,o=0; u<nSubDivs; u++)
180     {
181         for (v=0; v<nSubDivs; v++, o+=3)
182         {
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;
185             for (i=0; i<=3; i++)
186             {
187                 float vert_0[3]={0}, vert_1[3]={0};
188                 for (j=0; j<=3; j++)
189                 {
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];
193
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];
197                 }
198
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];
202
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];
209             }
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]);
215             norms[o+0] /= len;
216             norms[o+1] /= len;
217             norms[o+2] /= len;
218         }
219     }
220
221     /* Fix normal vector if needed */
222     if (normalFix)
223     {
224         for (o=0; o<nSubDivs*3; o+=3) /* whole first row (first nSubDivs normals) is broken: replace normals for the whole row */
225         {
226             norms[o+0] = 0.f;
227             norms[o+1] = normalFix==1? 1.f:-1.f;
228             norms[o+2] = 0.f;
229         }
230     }
231
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);
235
236     return nVertVals*flag;
237 }
238
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)
241 {
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 */
244     int u,v,i,j,o;
245
246     /* generate vertices and coordinates for the patch */
247     for (u=0,o=0; u<nSubDivs; u++)
248     {
249         for (v=0; v<nSubDivs; v++, o+=3)
250         {
251             for (i=0; i<=3; i++)
252             {
253                 float vert_0[3]={0};
254                 for (j=0; j<=3; j++)
255                 {
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];
259                 }
260
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];
264             }
265         }
266     }
267
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);
270
271     return nVertVals*flag;
272 }
273
274 static void fghTeapot( GLfloat scale, GLboolean useWireMode )
275 {
276     /* for internal use */
277     int p,o;
278     GLfloat cp[4][4][3];
279     /* to hold pointers to static vars/arrays */
280     GLfloat (*bern_0)[4], (*bern_1)[4];
281     GLfloat *verts, *norms, *lastScale;
282     GLushort *vertIdxs;
283     GLboolean * inited;
284     int nSubDivs, nVerts;
285
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;
296
297     /* check if need to generate vertices */
298     if (!*inited || scale != *lastScale)
299     {
300         printf("regen\n");
301         /* set vertex array to all 0 (not necessary for normals and vertex indices) */
302         memset(verts,0,nVerts*3*sizeof(GLfloat));
303
304         /* pregen Berstein polynomials and their first derivatives (for normals) */
305         if (!*inited)
306             pregenBernstein(nSubDivs,bern_0,bern_1);
307
308         /* generate vertices and normals */
309         for (p=0, o=0; p<GLUT_TEAPOT_N_INPUT_PATCHES; p++)
310         {
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 */
314
315             /* collect control points */
316             int i;
317             for (i=0; i<16; i++)
318             {
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.
321                  * Original:
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 );
325                  */
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;
329             }
330
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);
336         }
337         *lastScale = scale;
338
339         if (!*inited)
340         {
341             int r,c;
342             /* generate texture coordinates if solid teapot */
343             if (!useWireMode)
344             {
345                 /* generate for first patch */
346                 for (r=0,o=0; r<nSubDivs; r++)
347                 {
348                     GLfloat u = r/(nSubDivs-1.f);
349                     for (c=0; c<nSubDivs; c++, o+=2)
350                     {
351                         GLfloat v = c/(nSubDivs-1.f);
352                         texcsSolid[o+0] = u;
353                         texcsSolid[o+1] = v;
354                     }
355                 }
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));
359             }
360
361             /* build vertex index array */
362             if (useWireMode)
363             {
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++)
367                 {
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;
372                 }
373
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! */
376                 {
377                     int idx = nSubDivs*nSubDivs*p;
378                     for (r=0; r<nSubDivs; r++)
379                     {
380                         int loc = r*nSubDivs;
381                         for (c=0; c<nSubDivs; c++, o++)
382                             vertIdxs[o] = idx+loc+c;
383                     }
384                 }
385             }
386             else
387             {
388                 /* build vertex indices to draw teapot as triangles */
389                 for (p=0,o=0; p<GLUT_TEAPOT_N_PATCHES; p++)
390                 {
391                     int idx = nSubDivs*nSubDivs*p;
392                     for (r=0; r<nSubDivs-1; r++)
393                     {
394                         int loc = r*nSubDivs;
395                         for (c=0; c<nSubDivs-1; c++, o+=6)
396                         {
397                             /* ABC ACD, where B and C are one row lower */
398                             int row1 = idx+loc+c;
399                             int row2 = row1+nSubDivs;
400
401                             vertIdxs[o+0] = row1+0;
402                             vertIdxs[o+1] = row2+0;
403                             vertIdxs[o+2] = row2+1;
404
405                             vertIdxs[o+3] = row1+0;
406                             vertIdxs[o+4] = row2+1;
407                             vertIdxs[o+5] = row1+1;
408                         }
409                     }
410                 }
411             }
412
413             *inited = GL_TRUE;
414         }
415     }
416
417     /* draw */
418     // TODO: texture coordinates
419     if (useWireMode)
420         fghDrawGeometryWire(verts, norms, nVerts, vertIdxs, GLUT_TEAPOT_N_PATCHES*nSubDivs*2, nSubDivs, GL_LINE_STRIP, NULL,0,0);
421     else
422         fghDrawGeometrySolid(verts,norms,texcsSolid,nVerts,vertIdxs,1,GLUT_SOLID_TEAPOT_N_TRI*3);
423 }
424
425
426 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
427
428 /*
429  * Renders a wired teapot...
430  */
431 void FGAPIENTRY glutWireTeapot( double size )
432 {
433     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeapot" );
434     /* We will use the general teapot rendering code */
435     fghTeapot( (GLfloat)size, GL_TRUE );
436 }
437
438 /*
439  * Renders a filled teapot...
440  */
441 void FGAPIENTRY glutSolidTeapot( double size )
442 {
443     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeapot" );
444     /* We will use the general teapot rendering code */
445     fghTeapot( (GLfloat)size, GL_FALSE );
446 }
447
448 /*** END OF FILE ***/
449
450
451
452
453