001: /*
002: * $RCSfile: GeometryDecompressorRetained.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:22 $
029: * $State: Exp $
030: */
031:
032: package javax.media.j3d;
033:
034: import javax.vecmath.*;
035: import java.util.*;
036:
037: /**
038: * This class implements a retained geometry backend for the abstract
039: * GeometryDecompressor.
040: */
041: class GeometryDecompressorRetained extends GeometryDecompressor {
042: private static final boolean debug = false;
043: private static final boolean benchmark = false;
044: private static final boolean statistics = false;
045: private static final boolean printInfo = debug || benchmark
046: || statistics;
047:
048: // Type of connections in the compressed data:
049: // TYPE_POINT (1), TYPE_LINE (2), or TYPE_TRIANGLE (4).
050: private int bufferDataType;
051:
052: // Data bundled with each vertex: bitwise combination of
053: // NORMAL_IN_BUFFER (1), COLOR_IN_BUFFER (2), ALPHA_IN_BUFFER (4).
054: private int dataPresent;
055:
056: // Size of the compressed geometry in bytes.
057: private int size;
058:
059: // Decompressor output state variables.
060: private Color4f curColor;
061: private Vector3f curNormal;
062:
063: // List for accumulating the output of the decompressor and converting to
064: // GeometryArray representations.
065: private GeneralizedVertexList vlist;
066:
067: // Geometric bounds.
068: private Point3d lbounds = new Point3d();
069: private Point3d ubounds = new Point3d();
070:
071: // Decompression output constraints. The decompressor will still process
072: // all data contained in the compressed buffer, but will only retain data
073: // for output subject to these booleans.
074: private boolean boundsOnly = false;
075: private boolean positionsOnly = false;
076:
077: // A very rough gauge used to initialize the size of vlist, based on
078: // normal-per-vertex data collected from the HelloUniverse.cg file
079: // (seagull, '57 Chevy, dinosaur).
080: //
081: // XXXX: get fudge values for other vertex combinations
082: private static final float bytesPerVertexFudge = 5.3f;
083:
084: // Used for benchmarking if so configured.
085: private long startTime;
086: private long endTime;
087:
088: // Private convenience copies of various constants.
089: private static final int TYPE_POINT = CompressedGeometryRetained.TYPE_POINT;
090: private static final int TYPE_LINE = CompressedGeometryRetained.TYPE_LINE;
091: private static final int TYPE_TRIANGLE = CompressedGeometryRetained.TYPE_TRIANGLE;
092: private static final int FRONTFACE_CCW = GeneralizedStripFlags.FRONTFACE_CCW;
093:
094: /**
095: * If the given argument is true, sets the decompressor to output only the
096: * bounding box of the decompressed geometry.
097: * @param boundsOnly set to true if the decompressor should output only the
098: * geometric bounding box.
099: */
100: void setDecompressBoundsOnly(boolean boundsOnly) {
101: this .boundsOnly = boundsOnly;
102: if (boundsOnly)
103: this .positionsOnly = false;
104: }
105:
106: /**
107: * If the given argument is true, sets the decompressor to output only the
108: * decompressed positions, their connections, and the bounding box.
109: * @param positionsOnly set to true if the decompressor should output only
110: * position, connection, and bounding box data.
111: */
112: void setDecompressPositionsOnly(boolean positionsOnly) {
113: this .positionsOnly = positionsOnly;
114: if (positionsOnly)
115: this .boundsOnly = false;
116: }
117:
118: /**
119: * Decompress the geometry data in a CompressedGeometryRetained. The
120: * GeometryArray output is intended to be cached as retained data for
121: * efficient rendering. Global color and normal changes output by the
122: * decompressor are stored as redundant per-vertex data so that a single
123: * GeometryArray can be used.
124: *
125: * Since only one GeometryArray is created, if the bundling attributes
126: * change while building the vertex list then the decompression is
127: * aborted. There should be only one SetState bundling attribute command
128: * per CompressedGeometry.
129: *
130: * @param cgr CompressedGeometryRetained containing compressed geometry
131: * @return GeometryArrayRetained containing the results of the
132: * decompression, or null if only the bounds are computed.
133: */
134: GeometryRetained decompress(CompressedGeometryRetained cgr) {
135:
136: if (!checkVersion(cgr.majorVersionNumber,
137: cgr.minorVersionNumber)) {
138: return null;
139: }
140:
141: vlist = null;
142: curColor = null;
143: curNormal = null;
144: lbounds.set(1.0, 1.0, 1.0);
145: ubounds.set(-1.0, -1.0, -1.0);
146:
147: // Get the descriptors for the compressed data.
148: bufferDataType = cgr.bufferType;
149: dataPresent = cgr.bufferContents;
150: if (printInfo)
151: beginPrint();
152:
153: // Call the superclass decompress() method which calls the output
154: // methods of this subclass. The results are stored in vlist.
155: size = cgr.size;
156: super .decompress(cgr.offset, size, cgr.compressedGeometry);
157:
158: if (boundsOnly) {
159: if (printInfo)
160: endPrint();
161: return null;
162: }
163:
164: // Convert the output to a GeometryRetained.
165: GeometryArray ga;
166: switch (bufferDataType) {
167: case TYPE_TRIANGLE:
168: ga = vlist.toTriangleStripArray();
169: break;
170: case TYPE_LINE:
171: ga = vlist.toLineStripArray();
172: break;
173: case TYPE_POINT:
174: ga = vlist.toPointArray();
175: break;
176: default:
177: throw new IllegalArgumentException(J3dI18N
178: .getString("GeometryDecompressorRetained0"));
179: }
180:
181: // Release the reference to the non-retained data.
182: ga.retained.setSource(null);
183:
184: if (printInfo)
185: endPrint();
186: return (GeometryRetained) ga.retained;
187: }
188:
189: /**
190: * Get the bounds of the decompressed geometry.
191: * @param bb BoundingBox to receive bounds
192: */
193: void getBoundingBox(BoundingBox bb) {
194: bb.setLower(lbounds);
195: bb.setUpper(ubounds);
196: }
197:
198: /**
199: * Initialize the vertex output list based on the vertex format provided
200: * by the SetState decompression command.
201: */
202: void outputVertexFormat(boolean bundlingNorm,
203: boolean bundlingColor, boolean doingAlpha) {
204:
205: if (boundsOnly)
206: return;
207:
208: if (vlist != null)
209: throw new IllegalStateException(J3dI18N
210: .getString("GeometryDecompressorRetained1"));
211:
212: int vertexFormat = GeometryArray.COORDINATES;
213:
214: if (!positionsOnly) {
215: if (bundlingNorm)
216: vertexFormat |= GeometryArray.NORMALS;
217: if (bundlingColor)
218: vertexFormat |= GeometryArray.COLOR;
219: if (doingAlpha)
220: vertexFormat |= GeometryArray.WITH_ALPHA;
221: }
222:
223: vlist = new GeneralizedVertexList(vertexFormat, FRONTFACE_CCW,
224: (int) (size / bytesPerVertexFudge));
225: }
226:
227: /**
228: * Process a decompressed vertex.
229: */
230: void outputVertex(Point3f position, Vector3f normal, Color4f color,
231: int vertexReplaceCode) {
232:
233: if (position.x < lbounds.x)
234: lbounds.x = position.x;
235: if (position.y < lbounds.y)
236: lbounds.y = position.y;
237: if (position.z < lbounds.z)
238: lbounds.z = position.z;
239:
240: if (position.x > ubounds.x)
241: ubounds.x = position.x;
242: if (position.y > ubounds.y)
243: ubounds.y = position.y;
244: if (position.z > ubounds.z)
245: ubounds.z = position.z;
246:
247: if (boundsOnly)
248: return;
249: if (curColor != null)
250: color = curColor;
251: if (curNormal != null)
252: normal = curNormal;
253:
254: vlist.addVertex(position, normal, color, vertexReplaceCode);
255:
256: if (debug) {
257: System.err.println("outputVertex: flag "
258: + vertexReplaceCode);
259: System.err.println(" position " + position.toString());
260: if (normal != null)
261: System.err.println(" normal " + normal.toString());
262: if (color != null)
263: System.err.println(" color " + color.toString());
264: }
265: }
266:
267: /**
268: * Any global colors output by the decompressor are stored as per-vertex
269: * color in the retained data used internally by the renderer. This is
270: * done for performance and simplicity reasons, at the expense of
271: * replicating colors.
272: *
273: * The next method sets the current color that will be copied to each
274: * succeeding vertex. The outputColor() method is never called if
275: * colors are bundled with each vertex in the compressed buffer.
276: */
277: void outputColor(Color4f color) {
278: if (boundsOnly || positionsOnly)
279: return;
280: if (debug)
281: System.err.println("outputColor: " + color.toString());
282:
283: if ((vlist.vertexFormat & GeometryArray.COLOR) == 0) {
284: if (vlist.size() > 0)
285: throw new IllegalStateException(J3dI18N
286: .getString("GeometryDecompressorRetained2"));
287:
288: vlist.setVertexFormat(vlist.vertexFormat
289: | GeometryArray.COLOR);
290: }
291:
292: if (curColor == null)
293: curColor = new Color4f();
294: curColor.set(color);
295: }
296:
297: /**
298: * Set the current normal that will be copied to each succeeding vertex
299: * output by the decompressor. The per-vertex copy is always needed since
300: * in Java 3D a normal is always associated with a vertex. This is never
301: * called if normals are bundled with each vertex in the compressed
302: * buffer.
303: */
304: void outputNormal(Vector3f normal) {
305: if (boundsOnly || positionsOnly)
306: return;
307: if (debug)
308: System.err.println("outputNormal: " + normal.toString());
309:
310: if ((vlist.vertexFormat & GeometryArray.NORMALS) == 0) {
311: if (vlist.size() > 0)
312: throw new IllegalStateException(J3dI18N
313: .getString("GeometryDecompressorRetained3"));
314:
315: vlist.setVertexFormat(vlist.vertexFormat
316: | GeometryArray.NORMALS);
317: }
318:
319: if (curNormal == null)
320: curNormal = new Vector3f();
321: curNormal.set(normal);
322: }
323:
324: private void beginPrint() {
325: System.err.println("\nGeometryDecompressorRetained");
326:
327: switch (bufferDataType) {
328: case TYPE_TRIANGLE:
329: System.err.println(" buffer TYPE_TRIANGLE");
330: break;
331: case TYPE_LINE:
332: System.err.println(" buffer TYPE_LINE");
333: break;
334: case TYPE_POINT:
335: System.err.println(" buffer TYPE_POINT");
336: break;
337: default:
338: throw new IllegalArgumentException(J3dI18N
339: .getString("GeometryDecompressorRetained4"));
340: }
341:
342: System.err.print(" buffer data present: coords");
343:
344: if ((dataPresent & CompressedGeometryHeader.NORMAL_IN_BUFFER) != 0)
345: System.err.print(" normals");
346: if ((dataPresent & CompressedGeometryHeader.COLOR_IN_BUFFER) != 0)
347: System.err.print(" colors");
348: if ((dataPresent & CompressedGeometryHeader.ALPHA_IN_BUFFER) != 0)
349: System.err.print(" alpha");
350:
351: System.err.println();
352: if (boundsOnly)
353: System.err.println(" computing bounds only");
354: if (positionsOnly)
355: System.err.println(" computing positions only");
356:
357: startTime = J3dClock.currentTimeMillis();
358: }
359:
360: private void endPrint() {
361: endTime = J3dClock.currentTimeMillis();
362:
363: if (benchmark || statistics)
364: printBench();
365:
366: if (statistics)
367: printStats();
368: }
369:
370: private void printBench() {
371: float t = (endTime - startTime) / 1000.0f;
372:
373: if (boundsOnly) {
374: System.err.println(" decompression took " + t + " sec.\n");
375: return;
376: }
377:
378: System.err.println(" decompression + strip conversion took "
379: + t + " sec.");
380:
381: switch (bufferDataType) {
382: case TYPE_POINT:
383: System.err.println(" decompressed " + (vlist.size())
384: + " points at " + (vlist.size() / t)
385: + " points/sec.\n");
386: break;
387: case TYPE_LINE:
388: System.err.println(" decompressed "
389: + (vlist.vertexCount - vlist.stripCount)
390: + " lines at "
391: + ((vlist.vertexCount - vlist.stripCount) / t)
392: + " lines/sec.\n");
393: break;
394: case TYPE_TRIANGLE:
395: System.err.println(" decompressed "
396: + (vlist.vertexCount - 2 * vlist.stripCount)
397: + " triangles at "
398: + ((vlist.vertexCount - 2 * vlist.stripCount) / t)
399: + " triangles/sec.\n");
400: break;
401: }
402: }
403:
404: private void printStats() {
405: System.err.println(" bounding box:\n lower "
406: + lbounds.toString() + "\n upper "
407: + ubounds.toString());
408:
409: if (boundsOnly)
410: return;
411:
412: System.err
413: .print(" number of vertices in GeometryArray output: "
414: + vlist.vertexCount + "\n"
415: + " GeometryArray vertex data present: coords");
416:
417: if ((vlist.vertexFormat & GeometryArray.NORMALS) != 0)
418: System.err.print(" normals");
419:
420: if ((vlist.vertexFormat & GeometryArray.COLOR) != 0)
421: System.err.print(" colors");
422:
423: if ((vlist.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
424: System.err.print(" alpha");
425:
426: System.err.println("\n number of strips: " + vlist.stripCount);
427: if (vlist.stripCount > 0)
428: System.err
429: .println(" vertices/strip: "
430: + ((float) vlist.vertexCount / (float) vlist.stripCount));
431: }
432: }
|