---- 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