/* * fg_teapot.c * * Teapot(tm) rendering code. * * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. * Written by Pawel W. Olszta, * Creation date: Fri Dec 24 1999 * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* notes: * the (very little) required math is found here: http://www.gamasutra.com/view/feature/131848/tessellation_of_4x4_bezier_patches_.php?print=1 * 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 */ #include #include "fg_internal.h" #include "fg_teapot_data.h" /* -- STATIC VARS: CACHES ---------------------------------------------------- */ /* General defs */ #define GLUT_SOLID_N_SUBDIV 8 #define GLUT_WIRE_N_SUBDIV 10 /* Bernstein coefficients only have to be precomputed once (number of patch subdivisions is fixed) * Can thus define arrays for them here, they will be filled upon first use. * 3rd order Bezier surfaces have 4 Bernstein coeffs. * Have separate caches for solid and wire as they use a different number of subdivisions * _0 is for Bernstein polynomials, _1 for their first derivative (which we need for normals) */ static GLfloat bernWire_0 [GLUT_WIRE_N_SUBDIV] [4]; static GLfloat bernWire_1 [GLUT_WIRE_N_SUBDIV] [4]; static GLfloat bernSolid_0[GLUT_SOLID_N_SUBDIV][4]; static GLfloat bernSolid_1[GLUT_SOLID_N_SUBDIV][4]; /* Teapot defs */ #define GLUT_TEAPOT_N_PATCHES (6*4 + 4*2) /* 6 patches are reproduced (rotated) 4 times, 4 patches (flipped) 2 times */ #define GLUT_SOLID_TEAPOT_N_VERT GLUT_SOLID_N_SUBDIV*GLUT_SOLID_N_SUBDIV * GLUT_TEAPOT_N_PATCHES /* N_SUBDIV^2 vertices per patch */ #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 */ #define GLUT_WIRE_TEAPOT_N_VERT GLUT_WIRE_N_SUBDIV*GLUT_WIRE_N_SUBDIV * GLUT_TEAPOT_N_PATCHES /* N_SUBDIV^2 vertices per patch */ /* Bit of caching: * vertex indices and normals only need to be generated once for * a given number of subdivisions as they don't change with scale. * Vertices can be cached and reused if scale didn't change. */ static GLushort vertIdxsTeapotS[GLUT_SOLID_TEAPOT_N_TRI*3]; static GLfloat normsTeapotS [GLUT_SOLID_TEAPOT_N_VERT*3]; static GLfloat vertsTeapotS [GLUT_SOLID_TEAPOT_N_VERT*3]; static GLfloat texcsTeapotS [GLUT_SOLID_TEAPOT_N_VERT*2]; static GLfloat lastScaleTeapotS = 0.f; static GLboolean initedTeapotS = GL_FALSE; static GLushort vertIdxsTeapotW[GLUT_WIRE_TEAPOT_N_VERT*2]; static GLfloat normsTeapotW [GLUT_WIRE_TEAPOT_N_VERT*3]; static GLfloat vertsTeapotW [GLUT_WIRE_TEAPOT_N_VERT*3]; static GLfloat lastScaleTeapotW = 0.f; static GLboolean initedTeapotW = GL_FALSE; /* Teacup defs */ #define GLUT_TEACUP_N_PATCHES (6*4 + 1*2) /* 6 patches are reproduced (rotated) 4 times, 1 patch (flipped) 2 times */ #define GLUT_SOLID_TEACUP_N_VERT GLUT_SOLID_N_SUBDIV*GLUT_SOLID_N_SUBDIV * GLUT_TEACUP_N_PATCHES /* N_SUBDIV^2 vertices per patch */ #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 */ #define GLUT_WIRE_TEACUP_N_VERT GLUT_WIRE_N_SUBDIV*GLUT_WIRE_N_SUBDIV * GLUT_TEACUP_N_PATCHES /* N_SUBDIV^2 vertices per patch */ /* Bit of caching: * vertex indices and normals only need to be generated once for * a given number of subdivisions as they don't change with scale. * Vertices can be cached and reused if scale didn't change. */ static GLushort vertIdxsTeacupS[GLUT_SOLID_TEACUP_N_TRI*3]; static GLfloat normsTeacupS [GLUT_SOLID_TEACUP_N_VERT*3]; static GLfloat vertsTeacupS [GLUT_SOLID_TEACUP_N_VERT*3]; static GLfloat texcsTeacupS [GLUT_SOLID_TEACUP_N_VERT*2]; static GLfloat lastScaleTeacupS = 0.f; static GLboolean initedTeacupS = GL_FALSE; static GLushort vertIdxsTeacupW[GLUT_WIRE_TEACUP_N_VERT*2]; static GLfloat normsTeacupW [GLUT_WIRE_TEACUP_N_VERT*3]; static GLfloat vertsTeacupW [GLUT_WIRE_TEACUP_N_VERT*3]; static GLfloat lastScaleTeacupW = 0.f; static GLboolean initedTeacupW = GL_FALSE; /* Teaspoon defs */ #define GLUT_TEASPOON_N_PATCHES GLUT_TEASPOON_N_INPUT_PATCHES #define GLUT_SOLID_TEASPOON_N_VERT GLUT_SOLID_N_SUBDIV*GLUT_SOLID_N_SUBDIV * GLUT_TEASPOON_N_PATCHES /* N_SUBDIV^2 vertices per patch */ #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 */ #define GLUT_WIRE_TEASPOON_N_VERT GLUT_WIRE_N_SUBDIV*GLUT_WIRE_N_SUBDIV * GLUT_TEASPOON_N_PATCHES /* N_SUBDIV^2 vertices per patch */ /* Bit of caching: * vertex indices and normals only need to be generated once for * a given number of subdivisions as they don't change with scale. * Vertices can be cached and reused if scale didn't change. */ static GLushort vertIdxsTeaspoonS[GLUT_SOLID_TEASPOON_N_TRI*3]; static GLfloat normsTeaspoonS [GLUT_SOLID_TEASPOON_N_VERT*3]; static GLfloat vertsTeaspoonS [GLUT_SOLID_TEASPOON_N_VERT*3]; static GLfloat texcsTeaspoonS [GLUT_SOLID_TEASPOON_N_VERT*2]; static GLfloat lastScaleTeaspoonS = 0.f; static GLboolean initedTeaspoonS = GL_FALSE; static GLushort vertIdxsTeaspoonW[GLUT_WIRE_TEASPOON_N_VERT*2]; static GLfloat normsTeaspoonW [GLUT_WIRE_TEASPOON_N_VERT*3]; static GLfloat vertsTeaspoonW [GLUT_WIRE_TEASPOON_N_VERT*3]; static GLfloat lastScaleTeaspoonW = 0.f; static GLboolean initedTeaspoonW = GL_FALSE; /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */ extern void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart); extern void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numVertices, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode, GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2); /* evaluate 3rd order Bernstein polynomial and its 1st deriv */ static void bernstein3(int i, GLfloat x, GLfloat *r0, GLfloat *r1) { float invx = 1.f - x; /* r0: zero order coeff, r1: first deriv coeff */ switch (i) { GLfloat temp; case 0: temp = invx*invx; *r0 = invx * temp; /* invx * invx * invx */ *r1 = -3 * temp; /* -3 * invx * invx */ break; case 1: temp = invx*invx; *r0 = 3 * x * temp; /* 3 * x * invx * invx */ *r1 = 3 * temp - 6 * x * invx; /* 3 * invx * invx - 6 * x * invx */ break; case 2: temp = x*x; *r0 = 3 * temp * invx; /* 3 * x * x * invx */ *r1 = 6 * x * invx - 3 * temp; /* 6 * x * invx - 3 * x * x */ break; case 3: temp = x*x; *r0 = x * temp; /* x * x * x */ *r1 = 3 * temp; /* 3 * x * x */ break; default: *r0 = *r1 = 0; } } static void pregenBernstein(int nSubDivs, GLfloat (*bern_0)[4], GLfloat (*bern_1)[4]) { int s,i; for (s=0; s was 1.5 for teapot, but should be 1.575 to center it on the Z axis. Teacup and teaspoon have different offsets */ cp[i/4][i%4][0] = cpdata[patchdata[p][i]][0] *scale/2.f; cp[i/4][i%4][1] = (cpdata[patchdata[p][i]][2]-zOffset)*scale/2.f; cp[i/4][i%4][2] = -cpdata[patchdata[p][i]][1] *scale/2.f; } /* eval bezier patch */ if (!*inited) /* first time, generate normals as well */ o += evalBezierWithNorm(cp,nSubDivs,bern_0,bern_1, flag, normalFix, verts+o,norms+o); else /* only need to regen vertices */ o += evalBezier(cp,nSubDivs,bern_0, flag, verts+o); } *lastScale = scale; if (!*inited) { int r,c; /* generate texture coordinates if solid teapot/teacup/teaspoon */ if (!useWireMode) { /* generate for first patch */ for (r=0,o=0; r