/* ** License Applicability. Except to the extent portions of this file are ** made subject to an alternative license as permitted in the SGI Free ** Software License B, Version 1.1 (the "License"), the contents of this ** file are subject only to the provisions of the License. You may not use ** this file except in compliance with the License. You may obtain a copy ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: ** ** http://oss.sgi.com/projects/FreeB ** ** Note that, as provided in the License, the Software is distributed on an ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND ** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A ** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. ** ** Original Code. The Original Code is: OpenGL Sample Implementation, ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. ** Copyright in any portions created by third parties is as indicated ** elsewhere herein. All Rights Reserved. ** ** Additional Notice Provisions: The application programming interfaces ** established by SGI in conjunction with the Original Code are The ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released ** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version ** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X ** Window System(R) (Version 1.3), released October 19, 1998. This software ** was created using the OpenGL(R) version 1.2.1 Sample Implementation ** published by SGI, but has not been independently verified as being ** compliant with the OpenGL(R) version 1.2.1 Specification. ** ** $Date$ $Revision$ ** $Header$ */ /* * Copyright (c) 2002-2004 LWJGL Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'LWJGL' nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN * MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. */ package net.java.games.jogl.impl; import net.java.games.jogl.*; /** * GLUquadricImpl.java * * * Created 22-dec-2003 (originally Quadric.java) * @author Erik Duijs * @author Kenneth Russell */ class GLUquadricImpl implements GLUquadric { private int drawStyle; private int orientation; private boolean textureFlag; private int normals; GLUquadricImpl() { drawStyle = GLU.GLU_FILL; orientation = GLU.GLU_OUTSIDE; textureFlag = false; normals = GLU.GLU_SMOOTH; } /** * specifies the draw style for quadrics. * * The legal values are as follows: * * GLU.FILL: Quadrics are rendered with polygon primitives. The polygons * are drawn in a counterclockwise fashion with respect to * their normals (as defined with glu.quadricOrientation). * * GLU.LINE: Quadrics are rendered as a set of lines. * * GLU.SILHOUETTE: Quadrics are rendered as a set of lines, except that edges * separating coplanar faces will not be drawn. * * GLU.POINT: Quadrics are rendered as a set of points. * * @param drawStyle The drawStyle to set */ void setDrawStyle(int drawStyle) { this.drawStyle = drawStyle; } /** * specifies what kind of normals are desired for quadrics. * The legal values are as follows: * * GLU.NONE: No normals are generated. * * GLU.FLAT: One normal is generated for every facet of a quadric. * * GLU.SMOOTH: One normal is generated for every vertex of a quadric. This * is the default. * * @param normals The normals to set */ void setNormals(int normals) { this.normals = normals; } /** * specifies what kind of orientation is desired for. * The orientation values are as follows: * * GLU.OUTSIDE: Quadrics are drawn with normals pointing outward. * * GLU.INSIDE: Normals point inward. The default is GLU.OUTSIDE. * * Note that the interpretation of outward and inward depends on the quadric * being drawn. * * @param orientation The orientation to set */ void setOrientation(int orientation) { this.orientation = orientation; } /** * specifies if texture coordinates should be generated for * quadrics rendered with qobj. If the value of textureCoords is true, * then texture coordinates are generated, and if textureCoords is false, * they are not.. The default is false. * * The manner in which texture coordinates are generated depends upon the * specific quadric rendered. * * @param textureFlag The textureFlag to set */ void setTextureFlag(boolean textureFlag) { this.textureFlag = textureFlag; } /** * Returns the drawStyle. * @return int */ int getDrawStyle() { return drawStyle; } /** * Returns the normals. * @return int */ int getNormals() { return normals; } /** * Returns the orientation. * @return int */ int getOrientation() { return orientation; } /** * Returns the textureFlag. * @return boolean */ boolean getTextureFlag() { return textureFlag; } /** * draws a cylinder oriented along the z axis. The base of the * cylinder is placed at z = 0, and the top at z=height. Like a sphere, a * cylinder is subdivided around the z axis into slices, and along the z axis * into stacks. * * Note that if topRadius is set to zero, then this routine will generate a * cone. * * If the orientation is set to GLU.OUTSIDE (with glu.quadricOrientation), then * any generated normals point away from the z axis. Otherwise, they point * toward the z axis. * * If texturing is turned on (with glu.quadricTexture), then texture * coordinates are generated so that t ranges linearly from 0.0 at z = 0 to * 1.0 at z = height, and s ranges from 0.0 at the +y axis, to 0.25 at the +x * axis, to 0.5 at the -y axis, to 0.75 at the -x axis, and back to 1.0 at the * +y axis. * * @param baseRadius Specifies the radius of the cylinder at z = 0. * @param topRadius Specifies the radius of the cylinder at z = height. * @param height Specifies the height of the cylinder. * @param slices Specifies the number of subdivisions around the z axis. * @param stacks Specifies the number of subdivisions along the z axis. */ void drawCylinder(GL gl, float baseRadius, float topRadius, float height, int slices, int stacks) { float da, r, dr, dz; float x, y, z, nz, nsign; int i, j; if (orientation == GLU.GLU_INSIDE) { nsign = -1.0f; } else { nsign = 1.0f; } da = 2.0f * PI / slices; dr = (topRadius - baseRadius) / stacks; dz = height / stacks; nz = (baseRadius - topRadius) / height; // Z component of normal vectors if (drawStyle == GLU.GLU_POINT) { gl.glBegin(GL.GL_POINTS); for (i = 0; i < slices; i++) { x = cos((i * da)); y = sin((i * da)); normal3f(gl, x * nsign, y * nsign, nz * nsign); z = 0.0f; r = baseRadius; for (j = 0; j <= stacks; j++) { gl.glVertex3f((x * r), (y * r), z); z += dz; r += dr; } } gl.glEnd(); } else if (drawStyle == GLU.GLU_LINE || drawStyle == GLU.GLU_SILHOUETTE) { // Draw rings if (drawStyle == GLU.GLU_LINE) { z = 0.0f; r = baseRadius; for (j = 0; j <= stacks; j++) { gl.glBegin(GL.GL_LINE_LOOP); for (i = 0; i < slices; i++) { x = cos((i * da)); y = sin((i * da)); normal3f(gl, x * nsign, y * nsign, nz * nsign); gl.glVertex3f((x * r), (y * r), z); } gl.glEnd(); z += dz; r += dr; } } else { // draw one ring at each end if (baseRadius != 0.0) { gl.glBegin(GL.GL_LINE_LOOP); for (i = 0; i < slices; i++) { x = cos((i * da)); y = sin((i * da)); normal3f(gl, x * nsign, y * nsign, nz * nsign); gl.glVertex3f((x * baseRadius), (y * baseRadius), 0.0f); } gl.glEnd(); gl.glBegin(GL.GL_LINE_LOOP); for (i = 0; i < slices; i++) { x = cos((i * da)); y = sin((i * da)); normal3f(gl, x * nsign, y * nsign, nz * nsign); gl.glVertex3f((x * topRadius), (y * topRadius), height); } gl.glEnd(); } } // draw length lines gl.glBegin(GL.GL_LINES); for (i = 0; i < slices; i++) { x = cos((i * da)); y = sin((i * da)); normal3f(gl, x * nsign, y * nsign, nz * nsign); gl.glVertex3f((x * baseRadius), (y * baseRadius), 0.0f); gl.glVertex3f((x * topRadius), (y * topRadius), (height)); } gl.glEnd(); } else if (drawStyle == GLU.GLU_FILL) { float ds = 1.0f / slices; float dt = 1.0f / stacks; float t = 0.0f; z = 0.0f; r = baseRadius; for (j = 0; j < stacks; j++) { float s = 0.0f; gl.glBegin(GL.GL_QUAD_STRIP); for (i = 0; i <= slices; i++) { if (i == slices) { x = sin(0.0f); y = cos(0.0f); } else { x = sin((i * da)); y = cos((i * da)); } if (nsign == 1.0f) { normal3f(gl, (x * nsign), (y * nsign), (nz * nsign)); TXTR_COORD(gl, s, t); gl.glVertex3f((x * r), (y * r), z); normal3f(gl, (x * nsign), (y * nsign), (nz * nsign)); TXTR_COORD(gl, s, t + dt); gl.glVertex3f((x * (r + dr)), (y * (r + dr)), (z + dz)); } else { normal3f(gl, x * nsign, y * nsign, nz * nsign); TXTR_COORD(gl, s, t); gl.glVertex3f((x * r), (y * r), z); normal3f(gl, x * nsign, y * nsign, nz * nsign); TXTR_COORD(gl, s, t + dt); gl.glVertex3f((x * (r + dr)), (y * (r + dr)), (z + dz)); } s += ds; } // for slices gl.glEnd(); r += dr; t += dt; z += dz; } // for stacks } } /** * renders a disk on the z = 0 plane. The disk has a radius of * outerRadius, and contains a concentric circular hole with a radius of * innerRadius. If innerRadius is 0, then no hole is generated. The disk is * subdivided around the z axis into slices (like pizza slices), and also * about the z axis into rings (as specified by slices and loops, * respectively). * * With respect to orientation, the +z side of the disk is considered to be * "outside" (see glu.quadricOrientation). This means that if the orientation * is set to GLU.OUTSIDE, then any normals generated point along the +z axis. * Otherwise, they point along the -z axis. * * If texturing is turned on (with glu.quadricTexture), texture coordinates are * generated linearly such that where r=outerRadius, the value at (r, 0, 0) is * (1, 0.5), at (0, r, 0) it is (0.5, 1), at (-r, 0, 0) it is (0, 0.5), and at * (0, -r, 0) it is (0.5, 0). */ public void drawDisk(GL gl, float innerRadius, float outerRadius, int slices, int loops) { float da, dr; /* Normal vectors */ if (normals != GLU.GLU_NONE) { if (orientation == GLU.GLU_OUTSIDE) { gl.glNormal3f(0.0f, 0.0f, +1.0f); } else { gl.glNormal3f(0.0f, 0.0f, -1.0f); } } da = 2.0f * PI / slices; dr = (outerRadius - innerRadius) / loops; switch (drawStyle) { case GLU.GLU_FILL: { /* texture of a gluDisk is a cut out of the texture unit square * x, y in [-outerRadius, +outerRadius]; s, t in [0, 1] * (linear mapping) */ float dtc = 2.0f * outerRadius; float sa, ca; float r1 = innerRadius; int l; for (l = 0; l < loops; l++) { float r2 = r1 + dr; if (orientation == GLU.GLU_OUTSIDE) { int s; gl.glBegin(gl.GL_QUAD_STRIP); for (s = 0; s <= slices; s++) { float a; if (s == slices) a = 0.0f; else a = s * da; sa = sin(a); ca = cos(a); TXTR_COORD(gl, 0.5f + sa * r2 / dtc, 0.5f + ca * r2 / dtc); gl.glVertex2f(r2 * sa, r2 * ca); TXTR_COORD(gl, 0.5f + sa * r1 / dtc, 0.5f + ca * r1 / dtc); gl.glVertex2f(r1 * sa, r1 * ca); } gl.glEnd(); } else { int s; gl.glBegin(GL.GL_QUAD_STRIP); for (s = slices; s >= 0; s--) { float a; if (s == slices) a = 0.0f; else a = s * da; sa = sin(a); ca = cos(a); TXTR_COORD(gl, 0.5f - sa * r2 / dtc, 0.5f + ca * r2 / dtc); gl.glVertex2f(r2 * sa, r2 * ca); TXTR_COORD(gl, 0.5f - sa * r1 / dtc, 0.5f + ca * r1 / dtc); gl.glVertex2f(r1 * sa, r1 * ca); } gl.glEnd(); } r1 = r2; } break; } case GLU.GLU_LINE: { int l, s; /* draw loops */ for (l = 0; l <= loops; l++) { float r = innerRadius + l * dr; gl.glBegin(GL.GL_LINE_LOOP); for (s = 0; s < slices; s++) { float a = s * da; gl.glVertex2f(r * sin(a), r * cos(a)); } gl.glEnd(); } /* draw spokes */ for (s = 0; s < slices; s++) { float a = s * da; float x = sin(a); float y = cos(a); gl.glBegin(GL.GL_LINE_STRIP); for (l = 0; l <= loops; l++) { float r = innerRadius + l * dr; gl.glVertex2f(r * x, r * y); } gl.glEnd(); } break; } case GLU.GLU_POINT: { int s; gl.glBegin(GL.GL_POINTS); for (s = 0; s < slices; s++) { float a = s * da; float x = sin(a); float y = cos(a); int l; for (l = 0; l <= loops; l++) { float r = innerRadius * l * dr; gl.glVertex2f(r * x, r * y); } } gl.glEnd(); break; } case GLU.GLU_SILHOUETTE: { if (innerRadius != 0.0) { float a; gl.glBegin(GL.GL_LINE_LOOP); for (a = 0.0f; a < 2.0 * PI; a += da) { float x = innerRadius * sin(a); float y = innerRadius * cos(a); gl.glVertex2f(x, y); } gl.glEnd(); } { float a; gl.glBegin(GL.GL_LINE_LOOP); for (a = 0; a < 2.0f * PI; a += da) { float x = outerRadius * sin(a); float y = outerRadius * cos(a); gl.glVertex2f(x, y); } gl.glEnd(); } break; } default: return; } } /** * renders a partial disk on the z=0 plane. A partial disk is similar to a * full disk, except that only the subset of the disk from startAngle * through startAngle + sweepAngle is included (where 0 degrees is along * the +y axis, 90 degrees along the +x axis, 180 along the -y axis, and * 270 along the -x axis). * * The partial disk has a radius of outerRadius, and contains a concentric * circular hole with a radius of innerRadius. If innerRadius is zero, then * no hole is generated. The partial disk is subdivided around the z axis * into slices (like pizza slices), and also about the z axis into rings * (as specified by slices and loops, respectively). * * With respect to orientation, the +z side of the partial disk is * considered to be outside (see gluQuadricOrientation). This means that if * the orientation is set to GLU.GLU_OUTSIDE, then any normals generated point * along the +z axis. Otherwise, they point along the -z axis. * * If texturing is turned on (with gluQuadricTexture), texture coordinates * are generated linearly such that where r=outerRadius, the value at (r, 0, 0) * is (1, 0.5), at (0, r, 0) it is (0.5, 1), at (-r, 0, 0) it is (0, 0.5), * and at (0, -r, 0) it is (0.5, 0). */ public void drawPartialDisk(GL gl, float innerRadius, float outerRadius, int slices, int loops, float startAngle, float sweepAngle) { int i, j, max; float[] sinCache = new float[CACHE_SIZE]; float[] cosCache = new float[CACHE_SIZE]; float angle; float x, y; float sintemp, costemp; float deltaRadius; float radiusLow, radiusHigh; float texLow = 0, texHigh = 0; float angleOffset; int slices2; int finish; if (slices >= CACHE_SIZE) slices = CACHE_SIZE - 1; if (slices < 2 || loops < 1 || outerRadius <= 0.0f || innerRadius < 0.0f || innerRadius > outerRadius) { //gluQuadricError(qobj, GLU.GLU_INVALID_VALUE); System.err.println("PartialDisk: GLU_INVALID_VALUE"); return; } if (sweepAngle < -360.0f) sweepAngle = 360.0f; if (sweepAngle > 360.0f) sweepAngle = 360.0f; if (sweepAngle < 0) { startAngle += sweepAngle; sweepAngle = -sweepAngle; } if (sweepAngle == 360.0f) { slices2 = slices; } else { slices2 = slices + 1; } /* Compute length (needed for normal calculations) */ deltaRadius = outerRadius - innerRadius; /* Cache is the vertex locations cache */ angleOffset = startAngle / 180.0f * PI; for (i = 0; i <= slices; i++) { angle = angleOffset + ((PI * sweepAngle) / 180.0f) * i / slices; sinCache[i] = sin(angle); cosCache[i] = cos(angle); } if (sweepAngle == 360.0f) { sinCache[slices] = sinCache[0]; cosCache[slices] = cosCache[0]; } switch (normals) { case GLU.GLU_FLAT : case GLU.GLU_SMOOTH : if (orientation == GLU.GLU_OUTSIDE) { gl.glNormal3f(0.0f, 0.0f, 1.0f); } else { gl.glNormal3f(0.0f, 0.0f, -1.0f); } break; default : case GLU.GLU_NONE : break; } switch (drawStyle) { case GLU.GLU_FILL : if (innerRadius == .0f) { finish = loops - 1; /* Triangle strip for inner polygons */ gl.glBegin(GL.GL_TRIANGLE_FAN); if (textureFlag) { gl.glTexCoord2f(0.5f, 0.5f); } gl.glVertex3f(0.0f, 0.0f, 0.0f); radiusLow = outerRadius - deltaRadius * ((float) (loops - 1) / loops); if (textureFlag) { texLow = radiusLow / outerRadius / 2; } if (orientation == GLU.GLU_OUTSIDE) { for (i = slices; i >= 0; i--) { if (textureFlag) { gl.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f); } gl.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); } } else { for (i = 0; i <= slices; i++) { if (textureFlag) { gl.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f); } gl.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); } } gl.glEnd(); } else { finish = loops; } for (j = 0; j < finish; j++) { radiusLow = outerRadius - deltaRadius * ((float) j / loops); radiusHigh = outerRadius - deltaRadius * ((float) (j + 1) / loops); if (textureFlag) { texLow = radiusLow / outerRadius / 2; texHigh = radiusHigh / outerRadius / 2; } gl.glBegin(GL.GL_QUAD_STRIP); for (i = 0; i <= slices; i++) { if (orientation == GLU.GLU_OUTSIDE) { if (textureFlag) { gl.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f); } gl.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); if (textureFlag) { gl.glTexCoord2f(texHigh * sinCache[i] + 0.5f, texHigh * cosCache[i] + 0.5f); } gl.glVertex3f(radiusHigh * sinCache[i], radiusHigh * cosCache[i], 0.0f); } else { if (textureFlag) { gl.glTexCoord2f(texHigh * sinCache[i] + 0.5f, texHigh * cosCache[i] + 0.5f); } gl.glVertex3f(radiusHigh * sinCache[i], radiusHigh * cosCache[i], 0.0f); if (textureFlag) { gl.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f); } gl.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); } } gl.glEnd(); } break; case GLU.GLU_POINT : gl.glBegin(GL.GL_POINTS); for (i = 0; i < slices2; i++) { sintemp = sinCache[i]; costemp = cosCache[i]; for (j = 0; j <= loops; j++) { radiusLow = outerRadius - deltaRadius * ((float) j / loops); if (textureFlag) { texLow = radiusLow / outerRadius / 2; gl.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f); } gl.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f); } } gl.glEnd(); break; case GLU.GLU_LINE : if (innerRadius == outerRadius) { gl.glBegin(GL.GL_LINE_STRIP); for (i = 0; i <= slices; i++) { if (textureFlag) { gl.glTexCoord2f(sinCache[i] / 2 + 0.5f, cosCache[i] / 2 + 0.5f); } gl.glVertex3f(innerRadius * sinCache[i], innerRadius * cosCache[i], 0.0f); } gl.glEnd(); break; } for (j = 0; j <= loops; j++) { radiusLow = outerRadius - deltaRadius * ((float) j / loops); if (textureFlag) { texLow = radiusLow / outerRadius / 2; } gl.glBegin(GL.GL_LINE_STRIP); for (i = 0; i <= slices; i++) { if (textureFlag) { gl.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f); } gl.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); } gl.glEnd(); } for (i = 0; i < slices2; i++) { sintemp = sinCache[i]; costemp = cosCache[i]; gl.glBegin(GL.GL_LINE_STRIP); for (j = 0; j <= loops; j++) { radiusLow = outerRadius - deltaRadius * ((float) j / loops); if (textureFlag) { texLow = radiusLow / outerRadius / 2; } if (textureFlag) { gl.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f); } gl.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f); } gl.glEnd(); } break; case GLU.GLU_SILHOUETTE : if (sweepAngle < 360.0f) { for (i = 0; i <= slices; i += slices) { sintemp = sinCache[i]; costemp = cosCache[i]; gl.glBegin(GL.GL_LINE_STRIP); for (j = 0; j <= loops; j++) { radiusLow = outerRadius - deltaRadius * ((float) j / loops); if (textureFlag) { texLow = radiusLow / outerRadius / 2; gl.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f); } gl.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f); } gl.glEnd(); } } for (j = 0; j <= loops; j += loops) { radiusLow = outerRadius - deltaRadius * ((float) j / loops); if (textureFlag) { texLow = radiusLow / outerRadius / 2; } gl.glBegin(GL.GL_LINE_STRIP); for (i = 0; i <= slices; i++) { if (textureFlag) { gl.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f); } gl.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); } gl.glEnd(); if (innerRadius == outerRadius) break; } break; default : break; } } /** * draws a sphere of the given radius centered around the origin. * The sphere is subdivided around the z axis into slices and along the z axis * into stacks (similar to lines of longitude and latitude). * * If the orientation is set to GLU.OUTSIDE (with glu.quadricOrientation), then * any normals generated point away from the center of the sphere. Otherwise, * they point toward the center of the sphere. * If texturing is turned on (with glu.quadricTexture), then texture * coordinates are generated so that t ranges from 0.0 at z=-radius to 1.0 at * z=radius (t increases linearly along longitudinal lines), and s ranges from * 0.0 at the +y axis, to 0.25 at the +x axis, to 0.5 at the -y axis, to 0.75 * at the -x axis, and back to 1.0 at the +y axis. */ public void drawSphere(GL gl, float radius, int slices, int stacks) { // TODO float rho, drho, theta, dtheta; float x, y, z; float s, t, ds, dt; int i, j, imin, imax; boolean normals; float nsign; normals = (this.normals != GLU.GLU_NONE); if (orientation == GLU.GLU_INSIDE) { nsign = -1.0f; } else { nsign = 1.0f; } drho = PI / stacks; dtheta = 2.0f * PI / slices; if (drawStyle == GLU.GLU_FILL) { if (textureFlag) { // draw +Z end as a triangle fan gl.glBegin(GL.GL_TRIANGLE_FAN); gl.glNormal3f(0.0f, 0.0f, 1.0f); gl.glVertex3f(0.0f, 0.0f, nsign * radius); for (j = 0; j <= slices; j++) { theta = (j == slices) ? 0.0f : j * dtheta; x = -sin(theta) * sin(drho); y = cos(theta) * sin(drho); z = nsign * cos(drho); if (normals) { gl.glNormal3f(x * nsign, y * nsign, z * nsign); } gl.glVertex3f(x * radius, y * radius, z * radius); } gl.glEnd(); } ds = 1.0f / slices; dt = 1.0f / stacks; t = 1.0f; // because loop now runs from 0 if (textureFlag) { imin = 0; imax = stacks; } else { imin = 1; imax = stacks - 1; } // draw intermediate stacks as quad strips for (i = imin; i < imax; i++) { rho = i * drho; gl.glBegin(GL.GL_QUAD_STRIP); s = 0.0f; for (j = 0; j <= slices; j++) { theta = (j == slices) ? 0.0f : j * dtheta; x = -sin(theta) * sin(rho); y = cos(theta) * sin(rho); z = nsign * cos(rho); if (normals) { gl.glNormal3f(x * nsign, y * nsign, z * nsign); } TXTR_COORD(gl, s, t); gl.glVertex3f(x * radius, y * radius, z * radius); x = -sin(theta) * sin(rho + drho); y = cos(theta) * sin(rho + drho); z = nsign * cos(rho + drho); if (normals) { gl.glNormal3f(x * nsign, y * nsign, z * nsign); } TXTR_COORD(gl, s, t - dt); s += ds; gl.glVertex3f(x * radius, y * radius, z * radius); } gl.glEnd(); t -= dt; } if (!textureFlag) { // draw -Z end as a triangle fan gl.glBegin(GL.GL_TRIANGLE_FAN); gl.glNormal3f(0.0f, 0.0f, -1.0f); gl.glVertex3f(0.0f, 0.0f, -radius * nsign); rho = PI - drho; s = 1.0f; for (j = slices; j >= 0; j--) { theta = (j == slices) ? 0.0f : j * dtheta; x = -sin(theta) * sin(rho); y = cos(theta) * sin(rho); z = nsign * cos(rho); if (normals) gl.glNormal3f(x * nsign, y * nsign, z * nsign); s -= ds; gl.glVertex3f(x * radius, y * radius, z * radius); } gl.glEnd(); } } else if ( drawStyle == GLU.GLU_LINE || drawStyle == GLU.GLU_SILHOUETTE) { // draw stack lines for (i = 1; i < stacks; i++) { // stack line at i==stacks-1 was missing here rho = i * drho; gl.glBegin(GL.GL_LINE_LOOP); for (j = 0; j < slices; j++) { theta = j * dtheta; x = cos(theta) * sin(rho); y = sin(theta) * sin(rho); z = cos(rho); if (normals) gl.glNormal3f(x * nsign, y * nsign, z * nsign); gl.glVertex3f(x * radius, y * radius, z * radius); } gl.glEnd(); } // draw slice lines for (j = 0; j < slices; j++) { theta = j * dtheta; gl.glBegin(GL.GL_LINE_STRIP); for (i = 0; i <= stacks; i++) { rho = i * drho; x = cos(theta) * sin(rho); y = sin(theta) * sin(rho); z = cos(rho); if (normals) gl.glNormal3f(x * nsign, y * nsign, z * nsign); gl.glVertex3f(x * radius, y * radius, z * radius); } gl.glEnd(); } } else if (drawStyle == GLU.GLU_POINT) { // top and bottom-most points gl.glBegin(GL.GL_POINTS); if (normals) gl.glNormal3f(0.0f, 0.0f, nsign); gl.glVertex3f(0.0f, 0.0f, radius); if (normals) gl.glNormal3f(0.0f, 0.0f, -nsign); gl.glVertex3f(0.0f, 0.0f, -radius); // loop over stacks for (i = 1; i < stacks - 1; i++) { rho = i * drho; for (j = 0; j < slices; j++) { theta = j * dtheta; x = cos(theta) * sin(rho); y = sin(theta) * sin(rho); z = cos(rho); if (normals) gl.glNormal3f(x * nsign, y * nsign, z * nsign); gl.glVertex3f(x * radius, y * radius, z * radius); } } gl.glEnd(); } } //---------------------------------------------------------------------- // Internals only below this point // private static final float PI = (float)Math.PI; private static final int CACHE_SIZE = 240; /** * Call glNormal3f after scaling normal to unit length. * * @param x * @param y * @param z */ private void normal3f(GL gl, float x, float y, float z) { float mag; mag = (float)Math.sqrt(x * x + y * y + z * z); if (mag > 0.00001F) { x /= mag; y /= mag; z /= mag; } gl.glNormal3f(x, y, z); } private void TXTR_COORD(GL gl, float x, float y) { if (textureFlag) gl.glTexCoord2f(x,y); } private float sin(float r) { return (float)Math.sin(r); } private float cos(float r) { return (float)Math.cos(r); } }