001: /*
002: * $RCSfile: Cone.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.6 $
041: * $Date: 2007/04/24 18:50:59 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.geometry;
046:
047: import com.sun.j3d.utils.geometry.*;
048: import java.io.*;
049: import java.util.*;
050: import javax.media.j3d.*;
051: import javax.vecmath.*;
052:
053: /**
054: * Cone is a geometry primitive defined with a radius and a height.
055: * It is a capped cone centered at the origin with its central axis
056: * aligned along the Y-axis. The center of the cone is defined to be
057: * the center of its bounding box (rather than its centroid).
058: * <p>
059: * If the GENERATE_TEXTURE_COORDS flag is set, the texture coordinates
060: * are generated such that the texture gets mapped onto the cone similarly
061: * to how it gets mapped onto a cylinder, the difference being the top
062: * cap is nonexistent.
063: * <p>
064: * By default all primitives with the same parameters share their
065: * geometry (e.g., you can have 50 shperes in your scene, but the
066: * geometry is stored only once). A change to one primitive will
067: * effect all shared nodes. Another implication of this
068: * implementation is that the capabilities of the geometry are shared,
069: * and once one of the shared nodes is live, the capabilities cannot
070: * be set. Use the GEOMETRY_NOT_SHARED flag if you do not wish to
071: * share geometry among primitives with the same parameters.
072: */
073: public class Cone extends Primitive {
074: float radius, height;
075: int xdivisions, ydivisions;
076: static final int MID_REZ_DIV_X = 15;
077: static final int MID_REZ_DIV_Y = 1;
078:
079: /**
080: * Designates the body of the cone. Used by <code>getShape</code>.
081: *
082: * @see Cone#getShape
083: */
084: public static final int BODY = 0;
085:
086: /**
087: * Designates the end-cap of the cone. Used by <code>getShape</code>.
088: *
089: * @see Cone#getShape
090: */
091: public static final int CAP = 1;
092:
093: /**
094: * Constructs a default Cone of radius of 1.0 and height
095: * of 2.0. Resolution defaults to 15 divisions along X and axis
096: * and 1 along the Y axis. Normals are generated, texture
097: * coordinates are not.
098: */
099: public Cone() {
100: this (1.0f, 2.0f, GENERATE_NORMALS, MID_REZ_DIV_X,
101: MID_REZ_DIV_Y, null);
102: }
103:
104: /**
105: *
106: * Constructs a default Cone of a given radius and height. Normals
107: * are generated, texture coordinates are not.
108: * @param radius Radius
109: * @param height Height
110: */
111: public Cone(float radius, float height) {
112: this (radius, height, GENERATE_NORMALS, MID_REZ_DIV_X,
113: MID_REZ_DIV_Y, null);
114: }
115:
116: /**
117: *
118: * Constructs a default cone of a given radius, height,
119: * and appearance. Normals are generated, texture coordinates are not.
120: * @param radius Radius
121: * @param height Height
122: * @param ap Appearance
123: *
124: * @since Java 3D 1.2.1
125: */
126: public Cone(float radius, float height, Appearance ap) {
127: this (radius, height, GENERATE_NORMALS, MID_REZ_DIV_X,
128: MID_REZ_DIV_Y, ap);
129: }
130:
131: /**
132: *
133: * Constructs a default cone of a given radius, height,
134: * primitive flags, and appearance.
135: * @param radius Radius
136: * @param height Height
137: * @param primflags Primitive flags
138: * @param ap Appearance
139: */
140: public Cone(float radius, float height, int primflags, Appearance ap) {
141: this (radius, height, primflags, MID_REZ_DIV_X, MID_REZ_DIV_Y,
142: ap);
143: }
144:
145: /**
146: * Obtains the Shape3D node associated with one of the parts of the
147: * cone (the body or the cap). This allows users to modify the appearance
148: * or geometry of individual parts.
149: * @param partId The part to return (BODY or CAP).
150: * @return The Shape3D object associated with the partId. If an
151: * invalid partId is passed in, null is returned.
152: */
153: public Shape3D getShape(int partId) {
154: if (partId > CAP || partId < BODY)
155: return null;
156: return (Shape3D) getChild(partId);
157: }
158:
159: /**
160: * Sets appearance of the cone. This will set each part of the
161: * cone (cap & body) to the same appearance. To set each
162: * part's appearance separately, use getShape(partId) to get the
163: * individual shape and call shape.setAppearance(ap).
164: */
165: public void setAppearance(Appearance ap) {
166: ((Shape3D) getChild(BODY)).setAppearance(ap);
167: ((Shape3D) getChild(CAP)).setAppearance(ap);
168: }
169:
170: /**
171: * Gets the appearance of the specified part of the cone.
172: *
173: * @param partId identifier for a given subpart of the cone
174: *
175: * @return The appearance object associated with the partID. If an
176: * invalid partId is passed in, null is returned.
177: *
178: * @since Java 3D 1.2.1
179: */
180: public Appearance getAppearance(int partId) {
181: if (partId > CAP || partId < BODY)
182: return null;
183: return getShape(partId).getAppearance();
184: }
185:
186: /**
187: * Constructs a customized Cone of a given radius, height, flags,
188: * resolution (X and Y dimensions), and appearance. The
189: * resolution is defined in terms of number of subdivisions
190: * along the object's X axis (width) and Y axis (height). More divisions
191: * lead to finer tesselated objects.
192: * <p>
193: * If appearance is null, the default white appearance will be used.
194: * @param radius Radius
195: * @param height Height
196: * @param xdivision Number of divisions along X direction.
197: * @param ydivision Number of divisions along the height of cone.
198: * @param primflags flags
199: * @param ap Appearance
200: */
201:
202: public Cone(float radius, float height, int primflags,
203: int xdivision, int ydivision, Appearance ap) {
204: super ();
205:
206: Shape3D shape[] = new Shape3D[2];
207: this .radius = radius;
208: this .height = height;
209: xdivisions = xdivision;
210: ydivisions = ydivision;
211: flags = primflags;
212: boolean outside = (flags & GENERATE_NORMALS_INWARD) == 0;
213: boolean texCoordYUp = (flags & GENERATE_TEXTURE_COORDS_Y_UP) != 0;
214: Quadrics q = new Quadrics();
215: GeomBuffer gbuf = null;
216:
217: GeomBuffer cache = getCachedGeometry(Primitive.CONE, radius,
218: 0.0f, height, xdivision, ydivision, primflags);
219: if (cache != null) {
220: // System.out.println("using cached geometry");
221: shape[BODY] = new Shape3D(cache.getComputedGeometry());
222: numVerts += cache.getNumVerts();
223: numTris += cache.getNumTris();
224: } else {
225: // the body of the cone consists of the top of the cone and if
226: // ydivisions is greater than 1, the body of the cone.
227: gbuf = q.coneTop((double) (height / 2.0 - height
228: / ydivisions), (double) (radius / ydivisions),
229: height / ydivisions, xdivisions,
230: 1.0 - 1.0 / (double) ydivisions, outside,
231: texCoordYUp);
232: shape[BODY] = new Shape3D(gbuf.getGeom(flags));
233: numVerts += gbuf.getNumVerts();
234: numTris += gbuf.getNumTris();
235: if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
236: cacheGeometry(Primitive.CONE, radius, 0.0f, height,
237: xdivision, ydivision, primflags, gbuf);
238: }
239: }
240:
241: // only need to add a body if the ydivisions is greater than 1
242: if (ydivisions > 1) {
243: cache = getCachedGeometry(Primitive.CONE_DIVISIONS, radius,
244: 0.0f, height, xdivision, ydivision, primflags);
245: if (cache != null) {
246: // System.out.println("using cached divisions");
247: shape[BODY].addGeometry(cache.getComputedGeometry());
248: numVerts += cache.getNumVerts();
249: numTris += cache.getNumTris();
250: } else {
251: gbuf = q.coneBody(-(double) (height / 2.0),
252: (double) (height / 2.0 - height / ydivisions),
253: (double) radius,
254: (double) (radius / ydivisions), xdivisions,
255: ydivisions - 1, 1.0 / (double) ydivisions,
256: outside, texCoordYUp);
257: shape[BODY].addGeometry(gbuf.getGeom(flags));
258: numVerts += gbuf.getNumVerts();
259: numTris += gbuf.getNumTris();
260: if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
261: cacheGeometry(Primitive.CONE_DIVISIONS, radius,
262: 0.0f, height, xdivision, ydivision,
263: primflags, gbuf);
264: }
265: }
266: }
267:
268: if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
269: (shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
270: (shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
271: }
272:
273: if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
274: (shape[BODY]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
275: }
276:
277: this .addChild(shape[BODY]);
278:
279: // Create bottom cap.
280: cache = getCachedGeometry(Primitive.BOTTOM_DISK, radius,
281: radius, -height / 2.0f, xdivision, xdivision, primflags);
282: if (cache != null) {
283: // System.out.println("using cached bottom");
284: shape[CAP] = new Shape3D(cache.getComputedGeometry());
285: numVerts += cache.getNumVerts();
286: numTris += cache.getNumTris();
287: } else {
288: gbuf = q.disk((double) radius, xdivision,
289: -(double) height / 2.0, !outside, texCoordYUp);
290: shape[CAP] = new Shape3D(gbuf.getGeom(flags));
291: numVerts += gbuf.getNumVerts();
292: numTris += gbuf.getNumTris();
293: if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
294: cacheGeometry(Primitive.BOTTOM_DISK, radius, radius,
295: -height / 2.0f, xdivision, xdivision,
296: primflags, gbuf);
297: }
298: }
299:
300: if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
301: (shape[CAP]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
302: (shape[CAP]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
303: }
304:
305: if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
306: (shape[CAP]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
307: }
308:
309: // Transform3D t2 = new Transform3D();
310:
311: // Flip it to match up the texture coords.
312: /* This causes the bottom not to match up for odd xdivision values
313: objectMat = new Matrix4d();
314: objectMat.setIdentity();
315: rotMat = new Matrix4d();
316: rotMat.setIdentity();
317: rotMat.rotZ(Math.PI);
318: objectMat.mul(objectMat, rotMat);
319: t2.set(objectMat);
320: */
321:
322: this .addChild(shape[CAP]);
323:
324: if (ap == null) {
325: setAppearance();
326: } else
327: setAppearance(ap);
328: }
329:
330: /**
331: * Used to create a new instance of the node. This routine is called
332: * by <code>cloneTree</code> to duplicate the current node.
333: * <code>cloneNode</code> should be overridden by any user subclassed
334: * objects. All subclasses must have their <code>cloneNode</code>
335: * method consist of the following lines:
336: * <P><blockquote><pre>
337: * public Node cloneNode(boolean forceDuplicate) {
338: * UserSubClass usc = new UserSubClass();
339: * usc.duplicateNode(this, forceDuplicate);
340: * return usc;
341: * }
342: * </pre></blockquote>
343: * @param forceDuplicate when set to <code>true</code>, causes the
344: * <code>duplicateOnCloneTree</code> flag to be ignored. When
345: * <code>false</code>, the value of each node's
346: * <code>duplicateOnCloneTree</code> variable determines whether
347: * NodeComponent data is duplicated or copied.
348: *
349: * @see Node#cloneTree
350: * @see Node#duplicateNode
351: * @see NodeComponent#setDuplicateOnCloneTree
352: */
353: public Node cloneNode(boolean forceDuplicate) {
354: Cone c = new Cone(radius, height, flags, xdivisions,
355: ydivisions, getAppearance());
356: c.duplicateNode(this , forceDuplicate);
357: return c;
358: }
359:
360: /**
361: * Copies all node information from <code>originalNode</code> into
362: * the current node. This method is called from the
363: * <code>cloneNode</code> method which is, in turn, called by the
364: * <code>cloneTree</code> method.
365: * <P>
366: * For any <i>NodeComponent</i> objects
367: * contained by the object being duplicated, each <i>NodeComponent</i>
368: * object's <code>duplicateOnCloneTree</code> value is used to determine
369: * whether the <i>NodeComponent</i> should be duplicated in the new node
370: * or if just a reference to the current node should be placed in the
371: * new node. This flag can be overridden by setting the
372: * <code>forceDuplicate</code> parameter in the <code>cloneTree</code>
373: * method to <code>true</code>.
374: *
375: * @param originalNode the original node to duplicate.
376: * @param forceDuplicate when set to <code>true</code>, causes the
377: * <code>duplicateOnCloneTree</code> flag to be ignored. When
378: * <code>false</code>, the value of each node's
379: * <code>duplicateOnCloneTree</code> variable determines whether
380: * NodeComponent data is duplicated or copied.
381: *
382: * @see Node#cloneTree
383: * @see Node#cloneNode
384: * @see NodeComponent#setDuplicateOnCloneTree
385: */
386: public void duplicateNode(Node originalNode, boolean forceDuplicate) {
387: super .duplicateNode(originalNode, forceDuplicate);
388: }
389:
390: /**
391: * Returns the radius of the cone
392: *
393: * @since Java 3D 1.2.1
394: */
395: public float getRadius() {
396: return radius;
397: }
398:
399: /**
400: * Returns the height of the cone
401: *
402: * @since Java 3D 1.2.1
403: */
404: public float getHeight() {
405: return height;
406: }
407:
408: /**
409: * Returns the number divisions along the X direction
410: *
411: * @since Java 3D 1.2.1
412: */
413: public int getXdivisions() {
414: return xdivisions;
415: }
416:
417: /**
418: * Returns the number of divisions along the height of the cone
419: *
420: * @since Java 3D 1.2.1
421: */
422: public int getYdivisions() {
423: return ydivisions;
424: }
425:
426: }
|