Bug 146 - GLUT teapot
Summary: GLUT teapot
Status: VERIFIED FIXED
Alias: None
Product: Jogl
Classification: JogAmp
Component: core (show other bugs)
Version: 1
Hardware: All all
: P5 normal
Assignee: Sven Gothel
URL:
Depends on:
Blocks:
 
Reported: 2005-03-05 02:38 CET by Sven Gothel
Modified: 2010-03-24 07:47 CET (History)
0 users

See Also:
Type: FEATURE
SCM Refs:
Workaround: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Sven Gothel 2010-03-24 07:47:36 CET


---- Reported by tedmunds 2005-03-05 14:38:29 ----

The GLUT teapot is a useful non-trivial "primitive" for test applications, but
is currently unimplemented in the jogl port.  When porting applications from C
or C++ it would be useful to be able to simply copy calls to glutSolitTeapot(GL,
double) and glutWireTeapot(GL, double).  That said, there are some legacy
bugs/features in the C implementation of the teapot, and now could be a good
time to fix them. I propose adding the following public methods to GLUT.java:

public void glutSolidTeapot(GL gl, double scale, boolean cStyle);
public void glutWireTeapot(GL gl, double scale, boolean cStyle);
public void glutSolidTeapot(GL gl, double scale);
public void glutWireTeapot(GL gl, double scale);
(Where the final two methods simply invoke the first two methods with cStyle=true).

The cStyle argument would indicate whether the teapot should be created in
precisely the same way as in the C implementation of GLUT.  If cStyle is false,
the following changes would be made to the teapot:

1. Dating back to the original aux toolkit's teapot rendering routines, the
teapot has been drawn inside out; this shall be (somewhat) remedied by calling
glFrontFace(GL.GL_CW) to reverse the orientation sense of the surface.

2. The C implementation has the teapot sitting on the y=-1.5 plane (with the lid
in the positive y direction) - this placement is accomplished by a translation
and rotation on the model-view matrix.  Instead the teapot shall sit on the z=0
plane (with the lid in the positive z direction) - i.e. no translation or
rotation shall be applied to the model-view matrix.

3. The components of the teapot are built using evaluators which yield bad
normals at locations where edges of the control mesh collapse to single points
(the top of the lid and the middle of the base).  Parts with such singularities
shall be repaired by creating a small hole at the singular location, and filling
it with a triangle fan.

Below is a listing of the additions to GLUT.java that would implement the above
recommendations:

  /**
   * Renders the teapot as a solid shape of the specified size. The teapot is
   * created in a way that replicates the C GLUT implementation.
   * 
   * @param gl
   *        the OpenGL context in which to render the teapot
   * @param scale
   *        the factor by which to scale the teapot
   */
  public void glutSolidTeapot(GL gl, double scale) {
    glutSolidTeapot(gl, scale, true);
  }

  /**
   * Renders the teapot as a solid shape of the specified size. The teapot can
   * either be created in a way that is backward-compatible with the standard
   * C glut library (i.e. broken), or in a more pleasing way (i.e. with
   * surfaces whose front-faces point outwards and standing on the z=0 plane,
   * instead of the y=-1 plane). Both surface normals and texture coordinates
   * for the teapot are generated. The teapot is generated with OpenGL
   * evaluators.
   * 
   * @param gl
   *        the OpenGL context in which to render the teapot
   * @param scale
   *        the factor by which to scale the teapot
   * @param cStyle
   *        whether to create the teapot in exactly the same way as in the C
   *        implementation of GLUT
   */
  public void glutSolidTeapot(GL gl, double scale, boolean cStyle) {
    teapot(gl, 14, scale, GL.GL_FILL, cStyle);
  }

  /**
   * Renders the teapot as a wireframe shape of the specified size. The teapot
   * is created in a way that replicates the C GLUT implementation.
   * 
   * @param gl
   *        the OpenGL context in which to render the teapot
   * @param scale
   *        the factor by which to scale the teapot
   */
  public void glutWireTeapot(GL gl, double scale) {
    glutWireTeapot(gl, scale, true);
  }
  
  /**
   * Renders the teapot as a wireframe shape of the specified size. The teapot
   * can either be created in a way that is backward-compatible with the
   * standard C glut library (i.e. broken), or in a more pleasing way (i.e.
   * with surfaces whose front-faces point outwards and standing on the z=0
   * plane, instead of the y=-1 plane). Both surface normals and texture
   * coordinates for the teapot are generated. The teapot is generated with
   * OpenGL evaluators.
   * 
   * @param gl
   *        the OpenGL context in which to render the teapot
   * @param scale
   *        the factor by which to scale the teapot
   * @param cStyle
   *        whether to create the teapot in exactly the same way as in the C
   *        implementation of GLUT
   */
  public void glutWireTeapot(GL gl, double scale, boolean cStyle) {
    teapot(gl, 10, scale, GL.GL_LINE, cStyle);
  }

  // Teapot implementation (a modified port of glut_teapot.c)
  //
  // Rim, body, lid, and bottom data must be reflected in x and
  // y; handle and spout data across the y axis only.
  private static final int[][] teapotPatchData = {
    /* rim */
    {102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
    /* body */
    {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27},
    {24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
    /* lid */
    {96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,},
    {0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117},
    /* bottom */
    {118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37},
    /* handle */
    {41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56},
    {53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67},
    /* spout */
    {68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83},
    {80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}
  };
  private static final float[][] teapotCPData = {
    {0.2f, 0f, 2.7f},
    {0.2f, -0.112f, 2.7f},
    {0.112f, -0.2f, 2.7f},
    {0f, -0.2f, 2.7f},
    {1.3375f, 0f, 2.53125f},
    {1.3375f, -0.749f, 2.53125f},
    {0.749f, -1.3375f, 2.53125f},
    {0f, -1.3375f, 2.53125f},
    {1.4375f, 0f, 2.53125f},
    {1.4375f, -0.805f, 2.53125f},
    {0.805f, -1.4375f, 2.53125f},
    {0f, -1.4375f, 2.53125f},
    {1.5f, 0f, 2.4f},
    {1.5f, -0.84f, 2.4f},
    {0.84f, -1.5f, 2.4f},
    {0f, -1.5f, 2.4f},
    {1.75f, 0f, 1.875f},
    {1.75f, -0.98f, 1.875f},
    {0.98f, -1.75f, 1.875f},
    {0f, -1.75f, 1.875f},
    {2f, 0f, 1.35f},
    {2f, -1.12f, 1.35f},
    {1.12f, -2f, 1.35f},
    {0f, -2f, 1.35f},
    {2f, 0f, 0.9f},
    {2f, -1.12f, 0.9f},
    {1.12f, -2f, 0.9f},
    {0f, -2f, 0.9f},
    {-2f, 0f, 0.9f},
    {2f, 0f, 0.45f},
    {2f, -1.12f, 0.45f},
    {1.12f, -2f, 0.45f},
    {0f, -2f, 0.45f},
    {1.5f, 0f, 0.225f},
    {1.5f, -0.84f, 0.225f},
    {0.84f, -1.5f, 0.225f},
    {0f, -1.5f, 0.225f},
    {1.5f, 0f, 0.15f},
    {1.5f, -0.84f, 0.15f},
    {0.84f, -1.5f, 0.15f},
    {0f, -1.5f, 0.15f},
    {-1.6f, 0f, 2.025f},
    {-1.6f, -0.3f, 2.025f},
    {-1.5f, -0.3f, 2.25f},
    {-1.5f, 0f, 2.25f},
    {-2.3f, 0f, 2.025f},
    {-2.3f, -0.3f, 2.025f},
    {-2.5f, -0.3f, 2.25f},
    {-2.5f, 0f, 2.25f},
    {-2.7f, 0f, 2.025f},
    {-2.7f, -0.3f, 2.025f},
    {-3f, -0.3f, 2.25f},
    {-3f, 0f, 2.25f},
    {-2.7f, 0f, 1.8f},
    {-2.7f, -0.3f, 1.8f},
    {-3f, -0.3f, 1.8f},
    {-3f, 0f, 1.8f},
    {-2.7f, 0f, 1.575f},
    {-2.7f, -0.3f, 1.575f},
    {-3f, -0.3f, 1.35f},
    {-3f, 0f, 1.35f},
    {-2.5f, 0f, 1.125f},
    {-2.5f, -0.3f, 1.125f},
    {-2.65f, -0.3f, 0.9375f},
    {-2.65f, 0f, 0.9375f},
    {-2f, -0.3f, 0.9f},
    {-1.9f, -0.3f, 0.6f},
    {-1.9f, 0f, 0.6f},
    {1.7f, 0f, 1.425f},
    {1.7f, -0.66f, 1.425f},
    {1.7f, -0.66f, 0.6f},
    {1.7f, 0f, 0.6f},
    {2.6f, 0f, 1.425f},
    {2.6f, -0.66f, 1.425f},
    {3.1f, -0.66f, 0.825f},
    {3.1f, 0f, 0.825f},
    {2.3f, 0f, 2.1f},
    {2.3f, -0.25f, 2.1f},
    {2.4f, -0.25f, 2.025f},
    {2.4f, 0f, 2.025f},
    {2.7f, 0f, 2.4f},
    {2.7f, -0.25f, 2.4f},
    {3.3f, -0.25f, 2.4f},
    {3.3f, 0f, 2.4f},
    {2.8f, 0f, 2.475f},
    {2.8f, -0.25f, 2.475f},
    {3.525f, -0.25f, 2.49375f},
    {3.525f, 0f, 2.49375f},
    {2.9f, 0f, 2.475f},
    {2.9f, -0.15f, 2.475f},
    {3.45f, -0.15f, 2.5125f},
    {3.45f, 0f, 2.5125f},
    {2.8f, 0f, 2.4f},
    {2.8f, -0.15f, 2.4f},
    {3.2f, -0.15f, 2.4f},
    {3.2f, 0f, 2.4f},
    {0f, 0f, 3.15f},
    {0.8f, 0f, 3.15f},
    {0.8f, -0.45f, 3.15f},
    {0.45f, -0.8f, 3.15f},
    {0f, -0.8f, 3.15f},
    {0f, 0f, 2.85f},
    {1.4f, 0f, 2.4f},
    {1.4f, -0.784f, 2.4f},
    {0.784f, -1.4f, 2.4f},
    {0f, -1.4f, 2.4f},
    {0.4f, 0f, 2.55f},
    {0.4f, -0.224f, 2.55f},
    {0.224f, -0.4f, 2.55f},
    {0f, -0.4f, 2.55f},
    {1.3f, 0f, 2.55f},
    {1.3f, -0.728f, 2.55f},
    {0.728f, -1.3f, 2.55f},
    {0f, -1.3f, 2.55f},
    {1.3f, 0f, 2.4f},
    {1.3f, -0.728f, 2.4f},
    {0.728f, -1.3f, 2.4f},
    {0f, -1.3f, 2.4f},
    {0f, 0f, 0f},
    {1.425f, -0.798f, 0f},
    {1.5f, 0f, 0.075f},
    {1.425f, 0f, 0f},
    {0.798f, -1.425f, 0f},
    {0f, -1.5f, 0.075f},
    {0f, -1.425f, 0f},
    {1.5f, -0.84f, 0.075f},
    {0.84f, -1.5f, 0.075f}
  };
  // Since GL.glMap2f expects a packed array of floats, we must convert
  // from a 3-dimensional array to a 1-dimensional array
  private static final float[] teapotTex = {
    0, 0, 1, 0, 0, 1, 1, 1
  };

  private static void teapot(GL gl,
                             int grid,
                             double scale,
                             int type,
                             boolean backCompatible)
  {
    // As mentioned above, GL.glMap2f expects a packed array of floats
    float[] p = new float[4*4*3];
    float[] q = new float[4*4*3];
    float[] r = new float[4*4*3];
    float[] s = new float[4*4*3];
    int i, j, k, l;
  
    gl.glPushAttrib(GL.GL_ENABLE_BIT | GL.GL_EVAL_BIT | GL.GL_POLYGON_BIT);
    gl.glEnable(GL.GL_AUTO_NORMAL);
    gl.glEnable(GL.GL_NORMALIZE);
    gl.glEnable(GL.GL_MAP2_VERTEX_3);
    gl.glEnable(GL.GL_MAP2_TEXTURE_COORD_2);
    if (!backCompatible) {
      // The time has come to have the teapot no longer be inside out
      gl.glFrontFace(GL.GL_CW);
      gl.glScaled(0.5*scale, 0.5*scale, 0.5*scale);
    } else {
      // We want the teapot in it's backward compatible position and
      // orientation
      gl.glPushMatrix();
      gl.glRotatef(270.0f, 1, 0, 0);
      gl.glScalef((float)(0.5 * scale),
                  (float)(0.5 * scale),
                  (float)(0.5 * scale));
      gl.glTranslatef(0.0f, 0.0f, -1.5f);
    }
    for (i = 0; i < 10; i++) {
      for (j = 0; j < 4; j++) {
        for (k = 0; k < 4; k++) {
          for (l = 0; l < 3; l++) {
            p[(j*4+k)*3+l] = teapotCPData[teapotPatchData[i][j * 4 + k]][l];
            q[(j*4+k)*3+l] =
              teapotCPData[teapotPatchData[i][j * 4 + (3 - k)]][l];
            if (l == 1)
              q[(j*4+k)*3+l] *= -1.0;
            if (i < 6) {
              r[(j*4+k)*3+l] =
                teapotCPData[teapotPatchData[i][j * 4 + (3 - k)]][l];
              if (l == 0)
                r[(j*4+k)*3+l] *= -1.0;
              s[(j*4+k)*3+l] = teapotCPData[teapotPatchData[i][j * 4 + k]][l];
              if (l == 0)
                s[(j*4+k)*3+l] *= -1.0;
              if (l == 1)
                s[(j*4+k)*3+l] *= -1.0;
            }
          }
        }
      }
      gl.glMap2f(GL.GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2, teapotTex);
      gl.glMap2f(GL.GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, p);
      gl.glMapGrid2f(grid, 0.0f, 1.0f, grid, 0.0f, 1.0f);
      evaluateTeapotMesh(gl, grid, type, i, !backCompatible);
      gl.glMap2f(GL.GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, q);
      evaluateTeapotMesh(gl, grid, type, i, !backCompatible);
      if (i < 6) {
        gl.glMap2f(GL.GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, r);
        evaluateTeapotMesh(gl, grid, type, i, !backCompatible);
        gl.glMap2f(GL.GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, s);
        evaluateTeapotMesh(gl, grid, type, i, !backCompatible);
      }
    }
    if (backCompatible) {
      gl.glPopMatrix();
    }
    gl.glPopAttrib();
  }
  
  private static void evaluateTeapotMesh(GL gl,
                                         int grid,
                                         int type,
                                         int partNum,
                                         boolean repairSingularities)
  {
    if (repairSingularities && (partNum == 5 || partNum == 3)) {
      // Instead of using evaluators that give bad results at singularities,
      // evaluate by hand
      gl.glPolygonMode(GL.GL_FRONT_AND_BACK, type);
      for (int nv = 0; nv < grid; nv++) {
        if (nv == 0) {
          // Draw a small triangle-fan to fill the hole
          gl.glDisable(GL.GL_AUTO_NORMAL);
          gl.glNormal3f(0, 0, partNum == 3 ? 1 : -1);
          gl.glBegin(GL.GL_TRIANGLE_FAN);
          {
            gl.glEvalCoord2f(0, 0);
            // Note that we draw in clock-wise order to match the evaluator
            // method
            for (int nu = 0; nu <= grid; nu++)
            {
              gl.glEvalCoord2f(nu / (float)grid, (1f / grid) / (float)grid);
            }
          }
          gl.glEnd();
          gl.glEnable(GL.GL_AUTO_NORMAL);
        }
        // Draw the rest of the piece as an evaluated quad-strip
        gl.glBegin(GL.GL_QUAD_STRIP);
        {
          // Note that we draw in clock-wise order to match the evaluator method
          for (int nu = grid; nu >= 0; nu--) {
            gl.glEvalCoord2f(nu / (float)grid, (nv + 1) / (float)grid);
            gl.glEvalCoord2f(nu / (float)grid, Math.max(nv, 1f / grid)
                                                         / (float)grid);
          }
        }
        gl.glEnd();
      }
    } else {
      gl.glEvalMesh2(type, 0, grid, 0, grid);
    }
  }



---- Additional Comments From kbr 2005-04-09 17:07:13 ----

Thanks for the excellent patch and sorry for the delay integrating it. It looks
perfect and it has been applied verbatim. It will be present in the next JOGL
beta (1.1 b11).




--- Bug imported by sgothel@jausoft.com 2010-03-24 07:47 EDT  ---

This bug was previously known as _bug_ 146 at https://jogl.dev.java.net/bugs/show_bug.cgi?id=146