diff mupdf-source/thirdparty/freeglut/src/fg_teapot.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/freeglut/src/fg_teapot.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,570 @@
+/*
+ * fg_teapot.c
+ *
+ * Teapot(tm) rendering code.
+ *
+ * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
+ * Written by Pawel W. Olszta, <olszta@sourceforge.net>
+ * 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 <GL/freeglut.h>
+#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<nSubDivs; s++)
+    {
+        GLfloat x = s/(nSubDivs-1.f);
+        for (i=0; i<4; i++) /* 3rd order polynomial */
+            bernstein3(i,x,bern_0[s]+i,bern_1[s]+i);
+    }
+}
+
+/* based on flag either rotate patches around y axis to other 3 quadrants (flag=4) or reflect patch across x-y plane (flag=2) */
+static void rotOrReflect(int flag, int nVals, int nSubDivs, GLfloat *vals)
+{
+    int u,i,o;
+
+    if (flag==4)
+    {
+        int i1=nVals, i2=nVals*2, i3=nVals*3;
+        for (o=0; o<nVals; o+=3)
+        {
+            /* 90° rotation */
+            vals[i1+o+0] =  vals[o+2];
+            vals[i1+o+1] =  vals[o+1];
+            vals[i1+o+2] = -vals[o+0];
+            /* 180° rotation */
+            vals[i2+o+0] = -vals[o+0];
+            vals[i2+o+1] =  vals[o+1];
+            vals[i2+o+2] = -vals[o+2];
+            /* 270° rotation */
+            vals[i3+o+0] = -vals[o+2];
+            vals[i3+o+1] =  vals[o+1];
+            vals[i3+o+2] =  vals[o+0];
+        }
+    }
+    else if (flag==2)
+    {
+        /* copy over values, reversing row order to keep winding correct, and negating z to perform the flip */
+        for (u=0; u<nSubDivs; u++)  /* per row */
+        {
+            int off =   (nSubDivs-u-1)*nSubDivs*3;  /* read last row first from the already existing rows */
+            o       = nVals + u   *nSubDivs*3;      /* write last row as first row to output */
+            for (i=0; i<nSubDivs*3; i+=3, o+=3)     /* each row has nSubDivs points consisting of three values */
+            {
+                vals[o+0] =  vals[off+i+0];
+                vals[o+1] =  vals[off+i+1];
+                vals[o+2] = -vals[off+i+2];
+            }
+        }
+    }
+}
+
+/* verts array should be initialized to 0! */
+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)
+{
+    int nVerts    = nSubDivs*nSubDivs;
+    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 */
+    int u,v,i,j,o;
+
+    /* generate vertices and coordinates for the patch */
+    for (u=0,o=0; u<nSubDivs; u++)
+    {
+        for (v=0; v<nSubDivs; v++, o+=3)
+        {
+            /* for normals, get two tangents at the vertex using partial derivatives of 2D Bezier grid */
+            float tan1[3]={0}, tan2[3]={0}, len;
+            for (i=0; i<=3; i++)
+            {
+                float vert_0[3]={0}, vert_1[3]={0};
+                for (j=0; j<=3; j++)
+                {
+                    vert_0[0] += bern_0[v][j] * cp[i][j][0];
+                    vert_0[1] += bern_0[v][j] * cp[i][j][1];
+                    vert_0[2] += bern_0[v][j] * cp[i][j][2];
+
+                    vert_1[0] += bern_1[v][j] * cp[i][j][0];
+                    vert_1[1] += bern_1[v][j] * cp[i][j][1];
+                    vert_1[2] += bern_1[v][j] * cp[i][j][2];
+                }
+
+                verts[o+0] += bern_0[u][i]*vert_0[0];
+                verts[o+1] += bern_0[u][i]*vert_0[1];
+                verts[o+2] += bern_0[u][i]*vert_0[2];
+
+                tan1[0] += bern_0[u][i]*vert_1[0];
+                tan1[1] += bern_0[u][i]*vert_1[1];
+                tan1[2] += bern_0[u][i]*vert_1[2];
+                tan2[0] += bern_1[u][i]*vert_0[0];
+                tan2[1] += bern_1[u][i]*vert_0[1];
+                tan2[2] += bern_1[u][i]*vert_0[2];
+            }
+            /* get normal through cross product of the two tangents of the vertex */
+            norms[o+0] = tan1[1] * tan2[2] - tan1[2] * tan2[1];
+            norms[o+1] = tan1[2] * tan2[0] - tan1[0] * tan2[2];
+            norms[o+2] = tan1[0] * tan2[1] - tan1[1] * tan2[0];
+            len = (GLfloat)sqrt(norms[o+0] * norms[o+0] + norms[o+1] * norms[o+1] + norms[o+2] * norms[o+2]);
+            norms[o+0] /= len;
+            norms[o+1] /= len;
+            norms[o+2] /= len;
+        }
+    }
+
+    /* Fix normal vector if needed */
+    if (normalFix)
+    {
+        for (o=0; o<nSubDivs*3; o+=3) /* whole first row (first nSubDivs normals) is broken: replace normals for the whole row */
+        {
+            norms[o+0] = 0.f;
+            norms[o+1] = normalFix==1? 1.f:-1.f;
+            norms[o+2] = 0.f;
+        }
+    }
+
+    /* 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) */
+    rotOrReflect(flag, nVertVals, nSubDivs, verts);
+    rotOrReflect(flag, nVertVals, nSubDivs, norms);
+
+    return nVertVals*flag;
+}
+
+/* verts array should be initialized to 0! */
+static int evalBezier(GLfloat cp[4][4][3], int nSubDivs, float (*bern_0)[4], int flag, GLfloat *verts)
+{
+    int nVerts    = nSubDivs*nSubDivs;
+    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 */
+    int u,v,i,j,o;
+
+    /* generate vertices and coordinates for the patch */
+    for (u=0,o=0; u<nSubDivs; u++)
+    {
+        for (v=0; v<nSubDivs; v++, o+=3)
+        {
+            for (i=0; i<=3; i++)
+            {
+                float vert_0[3]={0};
+                for (j=0; j<=3; j++)
+                {
+                    vert_0[0] += bern_0[v][j] * cp[i][j][0];
+                    vert_0[1] += bern_0[v][j] * cp[i][j][1];
+                    vert_0[2] += bern_0[v][j] * cp[i][j][2];
+                }
+
+                verts[o+0] += bern_0[u][i]*vert_0[0];
+                verts[o+1] += bern_0[u][i]*vert_0[1];
+                verts[o+2] += bern_0[u][i]*vert_0[2];
+            }
+        }
+    }
+
+    /* 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) */
+    rotOrReflect(flag, nVertVals, nSubDivs, verts);
+
+    return nVertVals*flag;
+}
+
+static void fghTeaset( GLfloat scale, GLboolean useWireMode,
+                       GLfloat (*cpdata)[3], int (*patchdata)[16],
+                       GLushort *vertIdxs,
+                       GLfloat *verts, GLfloat *norms, GLfloat *texcs,
+                       GLfloat *lastScale, GLboolean *inited,
+                       GLboolean needNormalFix, GLboolean rotFlip, GLfloat zOffset,
+                       int nVerts, int nInputPatches, int nPatches, int nTriangles )
+{
+    /* for internal use */
+    int p,o;
+    GLfloat cp[4][4][3];
+    /* to hold pointers to static vars/arrays */
+    GLfloat (*bern_0)[4], (*bern_1)[4];
+    int nSubDivs;
+
+    /* Get relevant static arrays and variables */
+    bern_0      = useWireMode ? bernWire_0                : bernSolid_0;
+    bern_1      = useWireMode ? bernWire_1                : bernSolid_1;
+    nSubDivs    = useWireMode ? GLUT_WIRE_N_SUBDIV        : GLUT_SOLID_N_SUBDIV;
+
+    /* check if need to generate vertices */
+    if (!*inited || scale != *lastScale)
+    {
+        /* set vertex array to all 0 (not necessary for normals and vertex indices) */
+        memset(verts,0,nVerts*3*sizeof(GLfloat));
+
+        /* pregen Berstein polynomials and their first derivatives (for normals) */
+        if (!*inited)
+            pregenBernstein(nSubDivs,bern_0,bern_1);
+
+        /* generate vertices and normals */
+        for (p=0, o=0; p<nInputPatches; p++)
+        {
+            /* set flags for evalBezier function */
+            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 */
+            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 */
+
+            /* collect control points */
+            int i;
+            for (i=0; i<16; i++)
+            {
+                /* Original code draws with a 270° rot around X axis, a scaling and a translation along the Z-axis.
+                 * Incorporating these in the control points is much cheaper than transforming all the vertices.
+                 * Original:
+                 * glRotated( 270.0, 1.0, 0.0, 0.0 );
+                 * glScaled( 0.5 * scale, 0.5 * scale, 0.5 * scale );
+                 * 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
+                 */
+                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<nSubDivs; r++)
+                {
+                    GLfloat u = r/(nSubDivs-1.f);
+                    for (c=0; c<nSubDivs; c++, o+=2)
+                    {
+                        GLfloat v = c/(nSubDivs-1.f);
+                        texcs[o+0] = u;
+                        texcs[o+1] = v;
+                    }
+                }
+                /* copy it over for all the other patches */
+                for (p=1; p<nPatches; p++)
+                    memcpy(texcs+p*nSubDivs*nSubDivs*2,texcs,nSubDivs*nSubDivs*2*sizeof(GLfloat));
+            }
+
+            /* build vertex index array */
+            if (useWireMode)
+            {
+                /* build vertex indices to draw teapot/teacup/teaspoon as line strips */
+                /* first strips along increasing u, constant v */
+                for (p=0, o=0; p<nPatches; p++)
+                {
+                    int idx = nSubDivs*nSubDivs*p;
+                    for (c=0; c<nSubDivs; c++)
+                        for (r=0; r<nSubDivs; r++, o++)
+                            vertIdxs[o] = idx+r*nSubDivs+c;
+                }
+
+                /* then strips along increasing v, constant u */
+                for (p=0; p<nPatches; p++) /* don't reset o, we continue appending! */
+                {
+                    int idx = nSubDivs*nSubDivs*p;
+                    for (r=0; r<nSubDivs; r++)
+                    {
+                        int loc = r*nSubDivs;
+                        for (c=0; c<nSubDivs; c++, o++)
+                            vertIdxs[o] = idx+loc+c;
+                    }
+                }
+            }
+            else
+            {
+                /* build vertex indices to draw teapot/teacup/teaspoon as triangles */
+                for (p=0,o=0; p<nPatches; p++)
+                {
+                    int idx = nSubDivs*nSubDivs*p;
+                    for (r=0; r<nSubDivs-1; r++)
+                    {
+                        int loc = r*nSubDivs;
+                        for (c=0; c<nSubDivs-1; c++, o+=6)
+                        {
+                            /* ABC ACD, where B and C are one row lower */
+                            int row1 = idx+loc+c;
+                            int row2 = row1+nSubDivs;
+
+                            vertIdxs[o+0] = row1+0;
+                            vertIdxs[o+1] = row2+0;
+                            vertIdxs[o+2] = row2+1;
+
+                            vertIdxs[o+3] = row1+0;
+                            vertIdxs[o+4] = row2+1;
+                            vertIdxs[o+5] = row1+1;
+                        }
+                    }
+                }
+            }
+
+            *inited = GL_TRUE;
+        }
+    }
+
+    /* draw */
+    if (useWireMode)
+        fghDrawGeometryWire (verts, norms,        nVerts, vertIdxs, nPatches*nSubDivs*2, nSubDivs, GL_LINE_STRIP, NULL,0,0);
+    else
+        fghDrawGeometrySolid(verts, norms, texcs, nVerts, vertIdxs,1,nTriangles*3);
+}
+
+
+/* -- INTERFACE FUNCTIONS -------------------------------------------------- */
+
+/*
+ * Renders a wired teapot...
+ */
+void FGAPIENTRY glutWireTeapot( double size )
+{
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeapot" );
+    fghTeaset( (GLfloat)size, GL_TRUE,
+               cpdata_teapot, patchdata_teapot,
+               vertIdxsTeapotW,
+               vertsTeapotW, normsTeapotW, NULL,
+               &lastScaleTeapotW, &initedTeapotW,
+               GL_TRUE, GL_TRUE, 1.575f,
+               GLUT_WIRE_TEAPOT_N_VERT, GLUT_TEAPOT_N_INPUT_PATCHES, GLUT_TEAPOT_N_PATCHES, 0);
+}
+
+/*
+ * Renders a filled teapot...
+ */
+void FGAPIENTRY glutSolidTeapot( double size )
+{
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeapot" );
+    fghTeaset( (GLfloat)size, GL_FALSE,
+               cpdata_teapot, patchdata_teapot,
+               vertIdxsTeapotS,
+               vertsTeapotS, normsTeapotS, texcsTeapotS,
+               &lastScaleTeapotS, &initedTeapotS,
+               GL_TRUE, GL_TRUE, 1.575f,
+               GLUT_SOLID_TEAPOT_N_VERT, GLUT_TEAPOT_N_INPUT_PATCHES, GLUT_TEAPOT_N_PATCHES, GLUT_SOLID_TEAPOT_N_TRI);
+}
+
+
+/*
+ * Renders a wired teacup...
+ */
+void FGAPIENTRY glutWireTeacup( double size )
+{
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeacup" );
+    fghTeaset( (GLfloat)size/2.5f, GL_TRUE,
+               cpdata_teacup, patchdata_teacup,
+               vertIdxsTeacupW,
+               vertsTeacupW, normsTeacupW, NULL,
+               &lastScaleTeacupW, &initedTeacupW,
+               GL_FALSE, GL_TRUE, 1.5121f,
+               GLUT_WIRE_TEACUP_N_VERT, GLUT_TEACUP_N_INPUT_PATCHES, GLUT_TEACUP_N_PATCHES, 0);
+}
+
+/*
+ * Renders a filled teacup...
+ */
+void FGAPIENTRY glutSolidTeacup( double size )
+{
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeacup" );
+    fghTeaset( (GLfloat)size/2.5f, GL_FALSE,
+               cpdata_teacup, patchdata_teacup,
+               vertIdxsTeacupS,
+               vertsTeacupS, normsTeacupS, texcsTeacupS,
+               &lastScaleTeacupS, &initedTeacupS,
+               GL_FALSE, GL_TRUE, 1.5121f,
+               GLUT_SOLID_TEACUP_N_VERT, GLUT_TEACUP_N_INPUT_PATCHES, GLUT_TEACUP_N_PATCHES, GLUT_SOLID_TEACUP_N_TRI);
+}
+
+
+/*
+ * Renders a wired teaspoon...
+ */
+void FGAPIENTRY glutWireTeaspoon( double size )
+{
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTeaspoon" );
+    fghTeaset( (GLfloat)size/2.5f, GL_TRUE,
+               cpdata_teaspoon, patchdata_teaspoon,
+               vertIdxsTeaspoonW,
+               vertsTeaspoonW, normsTeaspoonW, NULL,
+               &lastScaleTeaspoonW, &initedTeaspoonW,
+               GL_FALSE, GL_FALSE, -0.0315f,
+               GLUT_WIRE_TEASPOON_N_VERT, GLUT_TEASPOON_N_INPUT_PATCHES, GLUT_TEASPOON_N_PATCHES, 0);
+}
+
+/*
+ * Renders a filled teaspoon...
+ */
+void FGAPIENTRY glutSolidTeaspoon( double size )
+{
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTeaspoon" );
+    fghTeaset( (GLfloat)size/2.5f, GL_FALSE,
+               cpdata_teaspoon, patchdata_teaspoon,
+               vertIdxsTeaspoonS,
+               vertsTeaspoonS, normsTeaspoonS, texcsTeaspoonS,
+               &lastScaleTeaspoonS, &initedTeaspoonS,
+               GL_FALSE, GL_FALSE, -0.0315f,
+               GLUT_SOLID_TEASPOON_N_VERT, GLUT_TEASPOON_N_INPUT_PATCHES, GLUT_TEASPOON_N_PATCHES, GLUT_SOLID_TEASPOON_N_TRI);
+}
+
+/*** END OF FILE ***/