001: /*
002: * $RCSfile: CompressedGeometryRetained.java,v $
003: *
004: * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
006: *
007: * This code is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License version 2 only, as
009: * published by the Free Software Foundation. Sun designates this
010: * particular file as subject to the "Classpath" exception as provided
011: * by Sun in the LICENSE file that accompanied this code.
012: *
013: * This code is distributed in the hope that it will be useful, but WITHOUT
014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: * version 2 for more details (a copy is included in the LICENSE file that
017: * accompanied this code).
018: *
019: * You should have received a copy of the GNU General Public License version
020: * 2 along with this work; if not, write to the Free Software Foundation,
021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
022: *
023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
024: * CA 95054 USA or visit www.sun.com if you need additional information or
025: * have any questions.
026: *
027: * $Revision: 1.8 $
028: * $Date: 2008/02/28 20:17:20 $
029: * $State: Exp $
030: */
031:
032: package javax.media.j3d;
033:
034: import javax.vecmath.*;
035:
036: /**
037: * The compressed geometry object is used to store geometry in a
038: * compressed format. Using compressed geometry reduces the amount
039: * of memory needed by a Java 3D application and increases the speed
040: * objects can be sent over the network. Once geometry decompression
041: * hardware support becomes available, increased rendering performance
042: * will also result from the use of compressed geometry.
043: */
044: class CompressedGeometryRetained extends GeometryRetained {
045:
046: // If not in by-reference mode, a 48-byte header as defined by the
047: // GL_SUNX_geometry_compression OpenGL extension is always concatenated to
048: // the beginning of the compressed geometry data and copied along with the
049: // it into a contiguous array. This allows hardware decompression using
050: // the obsolete experimental GL_SUNX_geometry_compression extension if
051: // that is all that is available.
052: //
053: // This is completely distinct and not to be confused with the cgHeader
054: // field on the non-retained side, although much of the data is
055: // essentially the same.
056: private static final int HEADER_LENGTH = 48;
057:
058: // These are the header locations examined.
059: private static final int HEADER_MAJOR_VERSION_OFFSET = 0;
060: private static final int HEADER_MINOR_VERSION_OFFSET = 1;
061: private static final int HEADER_MINOR_MINOR_VERSION_OFFSET = 2;
062: private static final int HEADER_BUFFER_TYPE_OFFSET = 3;
063: private static final int HEADER_BUFFER_DATA_OFFSET = 4;
064:
065: // The OpenGL compressed geometry extensions use bits instead of
066: // enumerations to represent the type of compressed geometry.
067: static final byte TYPE_POINT = 1;
068: static final byte TYPE_LINE = 2;
069: static final byte TYPE_TRIANGLE = 4;
070:
071: // Version number of this compressed geometry object.
072: int majorVersionNumber;
073: int minorVersionNumber;
074: int minorMinorVersionNumber;
075:
076: // These fields are used by the native execute() method.
077: int packedVersion;
078: int bufferType;
079: int bufferContents;
080: int renderFlags;
081: int offset;
082: int size;
083: byte[] compressedGeometry;
084:
085: // True if by-reference data access mode is in effect.
086: private boolean byReference = false;
087:
088: // A reference to the original byte array with which this object was
089: // created. If hardware decompression is available but it doesn't support
090: // by-reference semantics, then an internal copy of the original byte array
091: // is made even when by-reference semantics have been requested.
092: private byte[] originalCompressedGeometry = null;
093:
094: // True if the platform supports hardware decompression.
095: private static boolean hardwareDecompression = false;
096:
097: // This field retains a reference to the GeometryRetained object used for
098: // geometry-based picking. It is normally the same reference as the
099: // mirror geometry used for rendering unless hardware decompression is
100: // supported.
101: private GeometryRetained pickGeometry = null;
102:
103: /**
104: * Formerly native method that returns availability of a native by-reference
105: * rendering API for compressed geometry.
106: */
107: private boolean decompressByRef(Context ctx) {
108: return false;
109: }
110:
111: /**
112: * Formerly native method that returns availability of hardware
113: * rendering (and acceleration) for compressed geometry of the
114: * given version.
115: */
116: private boolean decompressHW(Context ctx, int majorVersion,
117: int minorVersion) {
118: return false;
119: }
120:
121: /**
122: * Formerly native method that does HW compressed geometry rendering
123: */
124: private void execute(Context ctx, int version, int bufferType,
125: int bufferContents, int renderFlags, int offset, int size,
126: byte[] geometry) {
127:
128: assert false : "This method should never be called!";
129: }
130:
131: /**
132: * Method for calling native execute() method on behalf of the J3D renderer.
133: */
134: void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
135: boolean updateAlpha, float alpha, int screen,
136: boolean ignoreVertexColors) {
137:
138: // XXXX: alpha udpate
139: execute(cv.ctx, packedVersion, bufferType, bufferContents,
140: renderFlags, offset, size, compressedGeometry);
141: }
142:
143: /**
144: * The package-scoped constructor.
145: */
146: CompressedGeometryRetained() {
147: this .geoType = GEO_TYPE_COMPRESSED;
148:
149: // Compressed geometry is always bounded by [-1..1] on each axis, so
150: // set that as the initial bounding box.
151: geoBounds.setUpper(1.0, 1.0, 1.0);
152: geoBounds.setLower(-1.0, -1.0, -1.0);
153: }
154:
155: /**
156: * Compressed geometry is immutable so this method does nothing.
157: */
158: void computeBoundingBox() {
159: }
160:
161: /**
162: * Update this object. Compressed geometry is immutable so there's
163: * nothing to do.
164: */
165: void update() {
166: isDirty = 0;
167: }
168:
169: /**
170: * Return true if the data access mode is by-reference.
171: */
172: boolean isByReference() {
173: return this .byReference;
174: }
175:
176: private void createByCopy(byte[] geometry) {
177: // Always copy a header along with the compressed geometry into a
178: // contiguous array in order to support hardware acceleration with the
179: // GL_SUNX_geometry_compression extension. The header is unnecessary
180: // if only the newer GL_SUN_geometry_compression API needs support.
181: compressedGeometry = new byte[HEADER_LENGTH + this .size];
182:
183: compressedGeometry[HEADER_MAJOR_VERSION_OFFSET] = (byte) this .majorVersionNumber;
184:
185: compressedGeometry[HEADER_MINOR_VERSION_OFFSET] = (byte) this .minorVersionNumber;
186:
187: compressedGeometry[HEADER_MINOR_MINOR_VERSION_OFFSET] = (byte) this .minorMinorVersionNumber;
188:
189: compressedGeometry[HEADER_BUFFER_TYPE_OFFSET] = (byte) this .bufferType;
190:
191: compressedGeometry[HEADER_BUFFER_DATA_OFFSET] = (byte) this .bufferContents;
192:
193: System.arraycopy(geometry, this .offset, compressedGeometry,
194: HEADER_LENGTH, this .size);
195:
196: this .offset = HEADER_LENGTH;
197: }
198:
199: /**
200: * Creates the retained compressed geometry data. Data from the header is
201: * always copied; the compressed geometry is copied as well if the data
202: * access mode is not by-reference.
203: *
204: * @param hdr the compressed geometry header
205: * @param geometry the compressed geometry
206: * @param byReference if true then by-reference semantics requested
207: */
208: void createCompressedGeometry(CompressedGeometryHeader hdr,
209: byte[] geometry, boolean byReference) {
210:
211: this .byReference = byReference;
212:
213: if (hdr.lowerBound != null)
214: this .geoBounds.setLower(hdr.lowerBound);
215:
216: if (hdr.upperBound != null)
217: this .geoBounds.setUpper(hdr.upperBound);
218:
219: this .centroid.set(geoBounds.getCenter());
220: recompCentroid = false;
221: this .majorVersionNumber = hdr.majorVersionNumber;
222: this .minorVersionNumber = hdr.minorVersionNumber;
223: this .minorMinorVersionNumber = hdr.minorMinorVersionNumber;
224:
225: this .packedVersion = (hdr.majorVersionNumber << 24)
226: | (hdr.minorVersionNumber << 16)
227: | (hdr.minorMinorVersionNumber << 8);
228:
229: switch (hdr.bufferType) {
230: case CompressedGeometryHeader.POINT_BUFFER:
231: this .bufferType = TYPE_POINT;
232: break;
233: case CompressedGeometryHeader.LINE_BUFFER:
234: this .bufferType = TYPE_LINE;
235: break;
236: case CompressedGeometryHeader.TRIANGLE_BUFFER:
237: this .bufferType = TYPE_TRIANGLE;
238: break;
239: }
240:
241: this .bufferContents = hdr.bufferDataPresent;
242: this .renderFlags = 0;
243:
244: this .size = hdr.size;
245: this .offset = hdr.start;
246:
247: if (byReference) {
248: // Assume we can use the given reference, but maintain a second
249: // reference in case a copy is later needed.
250: this .compressedGeometry = geometry;
251: this .originalCompressedGeometry = geometry;
252: } else {
253: // Copy the original data into a format that can be used by both
254: // the software and native hardware decompressors.
255: createByCopy(geometry);
256: this .originalCompressedGeometry = null;
257: }
258: }
259:
260: /**
261: * Decompress this object into a GeometryArrayRetained if hardware
262: * decompression is not available. Once decompressed the resulting
263: * geometry replaces the geometry reference in the associated RenderAtom
264: * as well as the mirror geometry reference in this object.
265: */
266: GeometryRetained getGeometry(boolean forceDecompression, Canvas3D cv) {
267:
268: if (forceDecompression) {
269: // forceDecompression is set to true if lighting is disabled and
270: // ignoreVertexColors is true, since there is no way for openGL to
271: // ignore vertexColors in this case.
272: GeometryDecompressorRetained gdr = new GeometryDecompressorRetained();
273:
274: mirrorGeometry = gdr.decompress(this );
275: gdr.getBoundingBox(geoBounds);
276: pickGeometry = mirrorGeometry;
277: } else {
278: // Return this object if hardware decompression is available.
279: if (hardwareDecompression)
280: return (GeometryRetained) this ;
281:
282: // Check to see if hardware decompression is available.
283: if (decompressHW(cv.ctx, majorVersionNumber,
284: minorVersionNumber)) {
285: hardwareDecompression = true;
286:
287: // If hardware can't handle by-reference, punt to by-copy.
288: if (isByReference() && !decompressByRef(cv.ctx)) {
289: createByCopy(compressedGeometry);
290: }
291:
292: return (GeometryRetained) this ;
293: }
294:
295: // Decompress the data into a GeometryArrayRetained representation
296: // for the mirror geometry reference.
297: GeometryDecompressorRetained gdr = new GeometryDecompressorRetained();
298:
299: mirrorGeometry = gdr.decompress(this );
300: gdr.getBoundingBox(geoBounds);
301:
302: // The mirror geometry contains a superset of the pick geometry
303: // data. Since hardware decompression isn't available, there's no
304: // need to retain separate pick geometry.
305: pickGeometry = mirrorGeometry;
306: }
307:
308: return mirrorGeometry;
309: }
310:
311: /**
312: * This method always decompresses the geometry and retains the result in
313: * order to support geometry-based picking and collision detection. The
314: * returned GeometryRetained object will contain only positions and
315: * connections.
316: */
317: GeometryRetained getPickGeometry() {
318: // Return the pick geometry if available.
319: if (pickGeometry != null)
320: return pickGeometry;
321:
322: // Decompress the data into a GeometryArrayRetained representation for
323: // the pick geometry reference. Retain it and its bounding box.
324: GeometryDecompressorRetained gdr = new GeometryDecompressorRetained();
325: gdr.setDecompressPositionsOnly(true);
326:
327: pickGeometry = gdr.decompress(this );
328: gdr.getBoundingBox(geoBounds);
329: return pickGeometry;
330: }
331:
332: //
333: // The following intersect() methods are used to implement geometry-based
334: // picking and collision.
335: //
336: boolean intersect(PickShape pickShape, PickInfo pickInfo,
337: int flags, Point3d iPnt, GeometryRetained geom,
338: int geomIndex) {
339: GeometryRetained geomR = getPickGeometry();
340: return (geomR != null ? geomR.intersect(pickShape, pickInfo,
341: flags, iPnt, geom, geomIndex) : false);
342: }
343:
344: boolean intersect(Bounds targetBound) {
345: GeometryRetained geom = getPickGeometry();
346: return (geom != null ? geom.intersect(targetBound) : false);
347: }
348:
349: boolean intersect(Transform3D this ToOtherVworld, GeometryRetained g) {
350: GeometryRetained geom = getPickGeometry();
351: return (geom != null ? geom.intersect(this ToOtherVworld, g)
352: : false);
353: }
354:
355: boolean intersect(Point3d[] pnts) {
356: GeometryRetained geom = getPickGeometry();
357: return (geom != null ? geom.intersect(pnts) : false);
358: }
359:
360: /**
361: * Return a vertex format mask that's compatible with GeometryArray
362: * objects.
363: */
364: int getVertexFormat() {
365: int vertexFormat = GeometryArray.COORDINATES;
366:
367: if ((this .bufferContents & CompressedGeometryHeader.NORMAL_IN_BUFFER) != 0)
368: vertexFormat |= GeometryArray.NORMALS;
369:
370: if ((this .bufferContents & CompressedGeometryHeader.COLOR_IN_BUFFER) != 0)
371: vertexFormat |= GeometryArray.COLOR;
372:
373: if ((this .bufferContents & CompressedGeometryHeader.ALPHA_IN_BUFFER) != 0)
374: vertexFormat |= GeometryArray.WITH_ALPHA;
375:
376: return vertexFormat;
377: }
378:
379: /**
380: * Return a buffer type that's compatible with CompressedGeometryHeader.
381: */
382: int getBufferType() {
383: switch (this .bufferType) {
384: case TYPE_POINT:
385: return CompressedGeometryHeader.POINT_BUFFER;
386: case TYPE_LINE:
387: return CompressedGeometryHeader.LINE_BUFFER;
388: default:
389: case TYPE_TRIANGLE:
390: return CompressedGeometryHeader.TRIANGLE_BUFFER;
391: }
392: }
393:
394: /**
395: * Copies compressed geometry data into the given array of bytes.
396: * The internal header information is not copied.
397: *
398: * @param buff array of bytes into which to copy compressed geometry
399: */
400: void copy(byte[] buff) {
401: System.arraycopy(compressedGeometry, offset, buff, 0, size);
402: }
403:
404: /**
405: * Returns a reference to the original compressed geometry byte array,
406: * which may have been copied even if by-reference semantics have been
407: * requested. It will be null if byCopy is in effect.
408: *
409: * @return reference to array of bytes containing the compressed geometry.
410: */
411: byte[] getReference() {
412: return originalCompressedGeometry;
413: }
414:
415: /**
416: * Copies all retained data for cloneNodeComponent() on the non-retained
417: * side. This is unlike GeometryArray subclasses which just call the
418: * public API constructors and then duplicateNodeComponent() to invoke the
419: * GeometryArray implementation of duplicateAttributes(), since the
420: * CompressedGeometry class directly subclasses Geometry and calling the
421: * public constructors would cause a lot of redundant data copying.
422: */
423: void duplicate(CompressedGeometryRetained cgr) {
424: cgr.majorVersionNumber = this .majorVersionNumber;
425: cgr.minorVersionNumber = this .minorVersionNumber;
426: cgr.minorMinorVersionNumber = this .minorMinorVersionNumber;
427:
428: cgr.packedVersion = this .packedVersion;
429: cgr.bufferType = this .bufferType;
430: cgr.bufferContents = this .bufferContents;
431: cgr.renderFlags = this .renderFlags;
432:
433: cgr.offset = this .offset;
434: cgr.size = this .size;
435:
436: cgr.geoBounds.setLower(this .geoBounds.lower);
437: cgr.geoBounds.setUpper(this .geoBounds.upper);
438: cgr.pickGeometry = this .pickGeometry;
439: cgr.byReference = this .byReference;
440:
441: if (this .byReference) {
442: // Copy references only.
443: cgr.compressedGeometry = this .compressedGeometry;
444: cgr.originalCompressedGeometry = this .originalCompressedGeometry;
445: } else {
446: // Copy entire byte array including 48-byte native OpenGL header.
447: cgr.compressedGeometry = new byte[this .compressedGeometry.length];
448: System.arraycopy(this .compressedGeometry, 0,
449: cgr.compressedGeometry, 0,
450: this .compressedGeometry.length);
451: cgr.originalCompressedGeometry = null;
452: }
453: }
454:
455: int getClassType() {
456: return COMPRESS_TYPE;
457: }
458: }
|