0001: /*
0002: * $RCSfile: CompressionStream.java,v $
0003: *
0004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * - Redistribution of source code must retain the above copyright
0011: * notice, this list of conditions and the following disclaimer.
0012: *
0013: * - Redistribution in binary form must reproduce the above copyright
0014: * notice, this list of conditions and the following disclaimer in
0015: * the documentation and/or other materials provided with the
0016: * distribution.
0017: *
0018: * Neither the name of Sun Microsystems, Inc. or the names of
0019: * contributors may be used to endorse or promote products derived
0020: * from this software without specific prior written permission.
0021: *
0022: * This software is provided "AS IS," without a warranty of any
0023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0034: * POSSIBILITY OF SUCH DAMAGES.
0035: *
0036: * You acknowledge that this software is not designed, licensed or
0037: * intended for use in the design, construction, operation or
0038: * maintenance of any nuclear facility.
0039: *
0040: * $Revision: 1.5 $
0041: * $Date: 2007/02/09 17:20:16 $
0042: * $State: Exp $
0043: */
0044:
0045: package com.sun.j3d.utils.compression;
0046:
0047: import com.sun.j3d.internal.BufferWrapper;
0048: import com.sun.j3d.internal.ByteBufferWrapper;
0049: import com.sun.j3d.internal.DoubleBufferWrapper;
0050: import com.sun.j3d.internal.FloatBufferWrapper;
0051: import com.sun.j3d.utils.geometry.GeometryInfo;
0052: import java.util.Collection;
0053: import java.util.Iterator;
0054: import java.util.LinkedList;
0055: import javax.media.j3d.Appearance;
0056: import javax.media.j3d.CompressedGeometryHeader;
0057: import javax.media.j3d.Geometry;
0058: import javax.media.j3d.GeometryArray;
0059: import javax.media.j3d.GeometryStripArray;
0060: import javax.media.j3d.IndexedGeometryArray;
0061: import javax.media.j3d.IndexedGeometryStripArray;
0062: import javax.media.j3d.IndexedLineArray;
0063: import javax.media.j3d.IndexedLineStripArray;
0064: import javax.media.j3d.IndexedQuadArray;
0065: import javax.media.j3d.IndexedTriangleArray;
0066: import javax.media.j3d.IndexedTriangleFanArray;
0067: import javax.media.j3d.IndexedTriangleStripArray;
0068: import javax.media.j3d.J3DBuffer;
0069: import javax.media.j3d.LineArray;
0070: import javax.media.j3d.LineStripArray;
0071: import javax.media.j3d.Material;
0072: import javax.media.j3d.QuadArray;
0073: import javax.media.j3d.Shape3D;
0074: import javax.media.j3d.TriangleArray;
0075: import javax.media.j3d.TriangleFanArray;
0076: import javax.media.j3d.TriangleStripArray;
0077: import javax.vecmath.Color3f;
0078: import javax.vecmath.Color4f;
0079: import javax.vecmath.Point3d;
0080: import javax.vecmath.Point3f;
0081: import javax.vecmath.Point3i;
0082: import javax.vecmath.Vector3f;
0083:
0084: /**
0085: * This class is used as input to a geometry compressor. It collects elements
0086: * such as vertices, normals, colors, mesh references, and quantization
0087: * parameters in an ordered stream. This stream is then traversed during
0088: * the compression process and used to build the compressed output buffer.
0089: *
0090: * @see GeometryCompressor
0091: *
0092: * @deprecated As of Java 3D 1.5, replaced by
0093: * com.sun.j3d.utils.geometry.compression.{@link com.sun.j3d.utils.geometry.compression.CompressionStream}.
0094: */
0095: public class CompressionStream {
0096: //
0097: // NOTE: For now, copies are made of all GeometryArray vertex components
0098: // even when by-reference access is available.
0099: //
0100: // TODO: Retrofit all CompressionStreamElements and MeshBuffer to handle
0101: // offsets to vertex data array references so that vertex components don't
0102: // have to be copied. New CompressionStreamElements could be defined to
0103: // set the current array reference during the quantization pass, or the
0104: // reference could be included in every CompressionStreamElement along
0105: // with the data offsets.
0106: //
0107: // TODO: Quantize on-the-fly when adding GeometryArray vertex data so that
0108: // CompressionStreamElements don't need references to the original float,
0109: // double, or byte data. Quantization is currently a separate pass since
0110: // the 1st pass adds vertex data and gets the total object bounds, but
0111: // this can be computed by merging the bounds of each GeometryArray
0112: // compressed into a single object. The 2nd pass quantization is still
0113: // needed for vertex data which isn't retrieved from a GeometryArray; for
0114: // example, apps that might use the addVertex() methods directly instead
0115: // of addGeometryArray().
0116: //
0117: // TODO: To further optimize memory, create new subclasses of
0118: // CompressionStream{Color, Normal} for bundled attributes and add them as
0119: // explicit stream elements. Then CompressionStreamVertex won't need to
0120: // carry references to them. This memory savings might be negated by the
0121: // extra overhead of adding more elements to the stream, however.
0122: //
0123: // TODO: Keep the absolute quantized values in the mesh buffer mirror so
0124: // that unmeshed CompressionStreamElements don't need to carry them.
0125: //
0126: // TODO: Support texture coordinate compression even though Level II is
0127: // not supported by any hardware decompressor on any graphics card.
0128: // Software decompression is still useful for applications interested in
0129: // minimizing file space, transmission time, and object loading time.
0130: //
0131: private static final boolean debug = false;
0132: private static final boolean benchmark = false;
0133:
0134: // Mesh buffer normal substitution is unavailable in Level I.
0135: private static final boolean noMeshNormalSubstitution = true;
0136:
0137: /**
0138: * This flag indicates that a vertex starts a new triangle or line strip.
0139: */
0140: static final int RESTART = 1;
0141:
0142: /**
0143: * This flag indicates that the next triangle in the strip is defined by
0144: * replacing the middle vertex of the previous triangle in the strip.
0145: * Equivalent to REPLACE_OLDEST for line strips.
0146: */
0147: static final int REPLACE_MIDDLE = 2;
0148:
0149: /**
0150: * This flag indicates that the next triangle in the strip is defined by
0151: * replacing the oldest vertex of the previous triangle in the strip.
0152: * Equivalent to REPLACE_MIDDLE for line strips.
0153: */
0154: static final int REPLACE_OLDEST = 3;
0155:
0156: /**
0157: * This flag indicates that a vertex is to be pushed into the mesh buffer.
0158: */
0159: static final int MESH_PUSH = 1;
0160:
0161: /**
0162: * This flag indicates that a vertex does not use the mesh buffer.
0163: */
0164: static final int NO_MESH_PUSH = 0;
0165:
0166: /**
0167: * Byte to float scale factor for scaling byte color components.
0168: */
0169: static final float ByteToFloatScale = 1.0f / 255.0f;
0170:
0171: /**
0172: * Type of this stream, either CompressedGeometryHeader.POINT_BUFFER,
0173: * CompressedGeometryHeader.LINE_BUFFER, or
0174: * CompressedGeometryHeader.TRIANGLE_BUFFER
0175: */
0176: int streamType;
0177:
0178: /**
0179: * A mask indicating which components are present in each vertex, as
0180: * defined by GeometryArray.
0181: */
0182: int vertexComponents;
0183:
0184: /**
0185: * Boolean indicating colors are bundled with the vertices.
0186: */
0187: boolean vertexColors;
0188:
0189: /**
0190: * Boolean indicating RGB colors are bundled with the vertices.
0191: */
0192: boolean vertexColor3;
0193:
0194: /**
0195: * Boolean indicating RGBA colors are bundled with the vertices.
0196: */
0197: boolean vertexColor4;
0198:
0199: /**
0200: * Boolean indicating normals are bundled with the vertices.
0201: */
0202: boolean vertexNormals;
0203:
0204: /**
0205: * Boolean indicating texture coordinates are present.
0206: */
0207: boolean vertexTextures;
0208:
0209: /**
0210: * Boolean indicating that 2D texture coordinates are used.
0211: * Currently only used to skip over textures in interleaved data.
0212: */
0213: boolean vertexTexture2;
0214:
0215: /**
0216: * Boolean indicating that 3D texture coordinates are used.
0217: * Currently only used to skip over textures in interleaved data.
0218: */
0219: boolean vertexTexture3;
0220:
0221: /**
0222: * Boolean indicating that 4D texture coordinates are used.
0223: * Currently only used to skip over textures in interleaved data.
0224: */
0225: boolean vertexTexture4;
0226:
0227: /**
0228: * Axes-aligned box enclosing all vertices in model coordinates.
0229: */
0230: Point3d mcBounds[] = new Point3d[2];
0231:
0232: /**
0233: * Axes-aligned box enclosing all vertices in normalized coordinates.
0234: */
0235: Point3d ncBounds[] = new Point3d[2];
0236:
0237: /**
0238: * Axes-aligned box enclosing all vertices in quantized coordinates.
0239: */
0240: Point3i qcBounds[] = new Point3i[2];
0241:
0242: /**
0243: * Center for normalizing positions to the unit cube.
0244: */
0245: double center[] = new double[3];
0246:
0247: /**
0248: * Maximum position range along the 3 axes.
0249: */
0250: double positionRangeMaximum;
0251:
0252: /**
0253: * Scale for normalizing positions to the unit cube.
0254: */
0255: double scale;
0256:
0257: /**
0258: * Current position component (X, Y, and Z) quantization value. This can
0259: * range from 1 to 16 bits and has a default of 16.<p>
0260: *
0261: * At 1 bit of quantization it is not possible to express positive
0262: * absolute or delta positions.
0263: */
0264: int positionQuant;
0265:
0266: /**
0267: * Current color component (R, G, B, A) quantization value. This can
0268: * range from 2 to 16 bits and has a default of 9.<p>
0269: *
0270: * A color component is represented with a signed fixed-point value in
0271: * order to be able express negative deltas; the default of 9 bits
0272: * corresponds to the 8-bit color component range of the graphics hardware
0273: * commonly available. Colors must be non-negative, so the lower limit of
0274: * quantization is 2 bits.
0275: */
0276: int colorQuant;
0277:
0278: /**
0279: * Current normal component (U and V) quantization value. This can range
0280: * from 0 to 6 bits and has a default of 6.<p>
0281: *
0282: * At 0 bits of quantization normals are represented only as 6 bit
0283: * sextant/octant pairs and 14 specially encoded normals (the 6 axis
0284: * normals and the 8 octant midpoint normals); since U and V can only be 0
0285: * at the minimum quantization, the totally number of unique normals is
0286: * 12 + 14 = 26.
0287: */
0288: int normalQuant;
0289:
0290: /**
0291: * Flag indicating position quantization change.
0292: */
0293: boolean positionQuantChanged;
0294:
0295: /**
0296: * Flag indicating color quantization change.
0297: */
0298: boolean colorQuantChanged;
0299:
0300: /**
0301: * Flag indicating normal quantization change.
0302: */
0303: boolean normalQuantChanged;
0304:
0305: /**
0306: * Last quantized position.
0307: */
0308: int lastPosition[] = new int[3];
0309:
0310: /**
0311: * Last quantized color.
0312: */
0313: int lastColor[] = new int[4];
0314:
0315: /**
0316: * Last quantized normal's sextant.
0317: */
0318: int lastSextant;
0319:
0320: /**
0321: * Last quantized normal's octant.
0322: */
0323: int lastOctant;
0324:
0325: /**
0326: * Last quantized normal's U encoding parameter.
0327: */
0328: int lastU;
0329:
0330: /**
0331: * Last quantized normal's V encoding parameter.
0332: */
0333: int lastV;
0334:
0335: /**
0336: * Flag indicating last normal used a special encoding.
0337: */
0338: boolean lastSpecialNormal;
0339:
0340: /**
0341: * Flag indicating the first position in this stream.
0342: */
0343: boolean firstPosition;
0344:
0345: /**
0346: * Flag indicating the first color in this stream.
0347: */
0348: boolean firstColor;
0349:
0350: /**
0351: * Flag indicating the first normal in this stream.
0352: */
0353: boolean firstNormal;
0354:
0355: /**
0356: * The total number of bytes used to create the uncompressed geometric
0357: * elements in this stream, useful for performance analysis. This
0358: * excludes mesh buffer references.
0359: */
0360: int byteCount;
0361:
0362: /**
0363: * The number of vertices created for this stream, excluding mesh buffer
0364: * references.
0365: */
0366: int vertexCount;
0367:
0368: /**
0369: * The number of mesh buffer references created for this stream.
0370: */
0371: int meshReferenceCount;
0372:
0373: /**
0374: * Mesh buffer mirror used for computing deltas during quantization pass
0375: * and a limited meshing algorithm for unstripped data.
0376: */
0377: MeshBuffer meshBuffer = new MeshBuffer();
0378:
0379: // Collection which holds the elements of this stream.
0380: private Collection stream;
0381:
0382: // True if preceding stream elements were colors or normals. Used to flag
0383: // color and normal mesh buffer substitution when computing deltas during
0384: // quantization pass.
0385: private boolean lastElementColor = false;
0386: private boolean lastLastElementColor = false;
0387: private boolean lastElementNormal = false;
0388: private boolean lastLastElementNormal = false;
0389:
0390: // Some convenient temporary holding variables.
0391: private Point3f p3f = new Point3f();
0392: private Color3f c3f = new Color3f();
0393: private Color4f c4f = new Color4f();
0394: private Vector3f n3f = new Vector3f();
0395:
0396: // Private constructor for common initializations.
0397: private CompressionStream() {
0398: this .stream = new LinkedList();
0399:
0400: byteCount = 0;
0401: vertexCount = 0;
0402: meshReferenceCount = 0;
0403:
0404: mcBounds[0] = new Point3d(Double.POSITIVE_INFINITY,
0405: Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
0406: mcBounds[1] = new Point3d(Double.NEGATIVE_INFINITY,
0407: Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
0408:
0409: qcBounds[0] = new Point3i(Integer.MAX_VALUE, Integer.MAX_VALUE,
0410: Integer.MAX_VALUE);
0411: qcBounds[1] = new Point3i(Integer.MIN_VALUE, Integer.MIN_VALUE,
0412: Integer.MIN_VALUE);
0413:
0414: /* normalized bounds computed from quantized bounds */
0415: ncBounds[0] = new Point3d();
0416: ncBounds[1] = new Point3d();
0417: }
0418:
0419: /**
0420: * Creates a new CompressionStream for the specified geometry type and
0421: * vertex format.<p>
0422: *
0423: * @param streamType type of data in this stream, either
0424: * CompressedGeometryHeader.POINT_BUFFER,
0425: * CompressedGeometryHeader.LINE_BUFFER, or
0426: * CompressedGeometryHeader.TRIANGLE_BUFFER
0427: *
0428: * @param vertexComponents a mask indicating which components are present
0429: * in each vertex, as defined by GeometryArray: COORDINATES, NORMALS, and
0430: * COLOR_3 or COLOR_4.
0431: *
0432: * @see GeometryCompressor
0433: * @see GeometryArray
0434: */
0435: CompressionStream(int streamType, int vertexComponents) {
0436: this ();
0437: this .streamType = streamType;
0438: this .vertexComponents = getVertexComponents(vertexComponents);
0439: }
0440:
0441: // See what vertex geometry components are present. The byReference,
0442: // interleaved, useNIOBuffer, and useCoordIndexOnly flags are not
0443: // examined.
0444: private int getVertexComponents(int vertexFormat) {
0445: int components = 0;
0446:
0447: vertexColors = vertexColor3 = vertexColor4 = vertexNormals = vertexTextures = vertexTexture2 = vertexTexture3 = vertexTexture4 = false;
0448:
0449: if ((vertexFormat & GeometryArray.NORMALS) != 0) {
0450: vertexNormals = true;
0451: components &= GeometryArray.NORMALS;
0452: if (debug)
0453: System.out.println("vertexNormals");
0454: }
0455:
0456: if ((vertexFormat & GeometryArray.COLOR_3) != 0) {
0457: vertexColors = true;
0458:
0459: if ((vertexFormat & GeometryArray.COLOR_4) != 0) {
0460: vertexColor4 = true;
0461: components &= GeometryArray.COLOR_4;
0462: if (debug)
0463: System.out.println("vertexColor4");
0464: } else {
0465: vertexColor3 = true;
0466: components &= GeometryArray.COLOR_3;
0467: if (debug)
0468: System.out.println("vertexColor3");
0469: }
0470: }
0471:
0472: if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
0473: vertexTextures = true;
0474: vertexTexture2 = true;
0475: components &= GeometryArray.TEXTURE_COORDINATE_2;
0476: if (debug)
0477: System.out.println("vertexTexture2");
0478: } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
0479: vertexTextures = true;
0480: vertexTexture3 = true;
0481: components &= GeometryArray.TEXTURE_COORDINATE_3;
0482: if (debug)
0483: System.out.println("vertexTexture3");
0484: } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
0485: vertexTextures = true;
0486: vertexTexture4 = true;
0487: components &= GeometryArray.TEXTURE_COORDINATE_4;
0488: if (debug)
0489: System.out.println("vertexTexture4");
0490: }
0491:
0492: if (vertexTextures)
0493: // Throw exception for now until texture is supported.
0494: throw new UnsupportedOperationException(
0495: "\ncompression of texture coordinates is not supported");
0496:
0497: return components;
0498: }
0499:
0500: // Get the streamType associated with a GeometryArray instance.
0501: private int getStreamType(GeometryArray ga) {
0502: if (ga instanceof TriangleStripArray
0503: || ga instanceof IndexedTriangleStripArray
0504: || ga instanceof TriangleFanArray
0505: || ga instanceof IndexedTriangleFanArray
0506: || ga instanceof TriangleArray
0507: || ga instanceof IndexedTriangleArray
0508: || ga instanceof QuadArray
0509: || ga instanceof IndexedQuadArray)
0510:
0511: return CompressedGeometryHeader.TRIANGLE_BUFFER;
0512:
0513: else if (ga instanceof LineArray
0514: || ga instanceof IndexedLineArray
0515: || ga instanceof LineStripArray
0516: || ga instanceof IndexedLineStripArray)
0517:
0518: return CompressedGeometryHeader.LINE_BUFFER;
0519:
0520: else
0521: return CompressedGeometryHeader.POINT_BUFFER;
0522: }
0523:
0524: /**
0525: * Iterates across all compression stream elements and applies
0526: * quantization parameters, encoding consecutive vertices as delta values
0527: * whenever possible. Each geometric element is mapped to a HuffmanNode
0528: * object containing its resulting bit length, right shift (trailing 0
0529: * count), and absolute or relative status.<p>
0530: *
0531: * Positions are normalized to span a unit cube via an offset and a
0532: * uniform scale factor that maps the midpoint of the object extents along
0533: * each dimension to the origin, and the longest dimension of the object to
0534: * the open interval (-1.0 .. +1.0). The geometric endpoints along that
0535: * dimension are both one quantum away from unity; for example, at a
0536: * position quantization of 6 bits, an object would be normalized so that
0537: * its most negative dimension is at (-1 + 1/64) and the most positive is
0538: * at (1 - 1/64).<p>
0539: *
0540: * Normals are assumed to be of unit length. Color components are clamped
0541: * to the [0..1) range, where the right endpoint is one quantum less
0542: * than 1.0.<p>
0543: *
0544: * @param huffmanTable Table which will map geometric compression stream
0545: * elements to HuffmanNode objects describing each element's data
0546: * representation. This table can then be processed with Huffman's
0547: * algorithm to optimize the bit length of descriptor tags according to
0548: * the number of geometric elements mapped to each tag.
0549: */
0550: void quantize(HuffmanTable huffmanTable) {
0551: // Set up default initial quantization parameters. The position and
0552: // color parameters specify the number of bits for each X, Y, Z, R, G,
0553: // B, or A component. The normal quantization parameter specifies the
0554: // number of bits for each U and V component.
0555: positionQuant = 16;
0556: colorQuant = 9;
0557: normalQuant = 6;
0558:
0559: // Compute position center and scaling for normalization to the unit
0560: // cube. This is a volume bounded by the open intervals (-1..1) on
0561: // each axis.
0562: center[0] = (mcBounds[1].x + mcBounds[0].x) / 2.0;
0563: center[1] = (mcBounds[1].y + mcBounds[0].y) / 2.0;
0564: center[2] = (mcBounds[1].z + mcBounds[0].z) / 2.0;
0565:
0566: double xRange = mcBounds[1].x - mcBounds[0].x;
0567: double yRange = mcBounds[1].y - mcBounds[0].y;
0568: double zRange = mcBounds[1].z - mcBounds[0].z;
0569:
0570: if (xRange > yRange)
0571: positionRangeMaximum = xRange;
0572: else
0573: positionRangeMaximum = yRange;
0574:
0575: if (zRange > positionRangeMaximum)
0576: positionRangeMaximum = zRange;
0577:
0578: // Adjust the range of the unit cube to match the default
0579: // quantization.
0580: //
0581: // This scale factor along with the center values computed above will
0582: // produce 16-bit integer representations of the floating point
0583: // position coordinates ranging symmetrically about 0 from -32767 to
0584: // +32767. -32768 is not used and the normalized floating point
0585: // position coordinates of -1.0 as well as +1.0 will not be
0586: // represented.
0587: //
0588: // Applications which wish to seamlessly stitch together compressed
0589: // objects will need to be aware that the range of normalized
0590: // positions will be one quantum away from the [-1..1] endpoints of
0591: // the unit cube and should adjust scale factors accordingly.
0592: scale = (2.0 / positionRangeMaximum) * (32767.0 / 32768.0);
0593:
0594: // Flag quantization change.
0595: positionQuantChanged = colorQuantChanged = normalQuantChanged = true;
0596:
0597: // Flag first position, color, and normal.
0598: firstPosition = firstColor = firstNormal = true;
0599:
0600: // Apply quantization.
0601: Iterator i = stream.iterator();
0602: while (i.hasNext()) {
0603: Object o = i.next();
0604:
0605: if (o instanceof CompressionStreamElement) {
0606: ((CompressionStreamElement) o).quantize(this ,
0607: huffmanTable);
0608:
0609: // Keep track of whether last two elements were colors or
0610: // normals for mesh buffer component substitution semantics.
0611: lastLastElementColor = lastElementColor;
0612: lastLastElementNormal = lastElementNormal;
0613: lastElementColor = lastElementNormal = false;
0614:
0615: if (o instanceof CompressionStreamColor)
0616: lastElementColor = true;
0617: else if (o instanceof CompressionStreamNormal)
0618: lastElementNormal = true;
0619: }
0620: }
0621:
0622: // Compute the bounds in normalized coordinates.
0623: ncBounds[0].x = (double) qcBounds[0].x / 32768.0;
0624: ncBounds[0].y = (double) qcBounds[0].y / 32768.0;
0625: ncBounds[0].z = (double) qcBounds[0].z / 32768.0;
0626:
0627: ncBounds[1].x = (double) qcBounds[1].x / 32768.0;
0628: ncBounds[1].y = (double) qcBounds[1].y / 32768.0;
0629: ncBounds[1].z = (double) qcBounds[1].z / 32768.0;
0630: }
0631:
0632: /**
0633: * Iterates across all compression stream elements and builds the
0634: * compressed geometry command stream output.<p>
0635: *
0636: * @param huffmanTable Table which maps geometric elements in this stream
0637: * to tags describing the encoding parameters (length, shift, and
0638: * absolute/relative status) to be used for their representations in the
0639: * compressed output. All tags must be 6 bits or less in length, and the
0640: * sum of the number of bits in the tag plus the number of bits in the
0641: * data it describes must be at least 6 bits in length.
0642: *
0643: * @param outputBuffer CommandStream to use for collecting the compressed
0644: * bits.
0645: */
0646: void outputCommands(HuffmanTable huffmanTable,
0647: CommandStream outputBuffer) {
0648: //
0649: // The first command output is setState to indicate what data is
0650: // bundled with each vertex. Although the semantics of geometry
0651: // decompression allow setState to appear anywhere in the stream, this
0652: // cannot be handled by the current Java 3D software decompressor,
0653: // which internally decompresses an entire compressed buffer into a
0654: // single retained object sharing a single consistent vertex format.
0655: // This limitation may be removed in subsequent releases of Java 3D.
0656: //
0657: int bnv = (vertexNormals ? 1 : 0);
0658: int bcv = ((vertexColor3 || vertexColor4) ? 1 : 0);
0659: int cap = (vertexColor4 ? 1 : 0);
0660:
0661: int command = CommandStream.SET_STATE | bnv;
0662: long data = (bcv << 2) | (cap << 1);
0663:
0664: // Output the setState command.
0665: outputBuffer.addCommand(command, 8, data, 3);
0666:
0667: // Output the Huffman table commands.
0668: huffmanTable.outputCommands(outputBuffer);
0669:
0670: // Output each compression stream element's data.
0671: Iterator i = stream.iterator();
0672: while (i.hasNext()) {
0673: Object o = i.next();
0674: if (o instanceof CompressionStreamElement)
0675: ((CompressionStreamElement) o).outputCommand(
0676: huffmanTable, outputBuffer);
0677: }
0678:
0679: // Finish the header-forwarding interleave and long-word align.
0680: outputBuffer.end();
0681: }
0682:
0683: /**
0684: * Retrieve the total size of the uncompressed geometric data in bytes,
0685: * excluding mesh buffer references.
0686: * @return uncompressed byte count
0687: */
0688: int getByteCount() {
0689: return byteCount;
0690: }
0691:
0692: /**
0693: * Retrieve the the number of vertices created for this stream, excluding
0694: * mesh buffer references.
0695: * @return vertex count
0696: */
0697: int getVertexCount() {
0698: return vertexCount;
0699: }
0700:
0701: /**
0702: * Retrieve the number of mesh buffer references created for this stream.
0703: * @return mesh buffer reference count
0704: */
0705: int getMeshReferenceCount() {
0706: return meshReferenceCount;
0707: }
0708:
0709: /**
0710: * Stream element that sets position quantization during quantize pass.
0711: */
0712: private class PositionQuant extends CompressionStreamElement {
0713: int value;
0714:
0715: PositionQuant(int value) {
0716: this .value = value;
0717: }
0718:
0719: void quantize(CompressionStream s, HuffmanTable t) {
0720: positionQuant = value;
0721: positionQuantChanged = true;
0722:
0723: // Adjust range of unit cube scaling to match quantization.
0724: scale = (2.0 / positionRangeMaximum)
0725: * (((double) ((1 << (value - 1)) - 1)) / ((double) (1 << (value - 1))));
0726: }
0727:
0728: public String toString() {
0729: return "positionQuant: " + value;
0730: }
0731: }
0732:
0733: /**
0734: * Stream element that sets normal quantization during quantize pass.
0735: */
0736: private class NormalQuant extends CompressionStreamElement {
0737: int value;
0738:
0739: NormalQuant(int value) {
0740: this .value = value;
0741: }
0742:
0743: void quantize(CompressionStream s, HuffmanTable t) {
0744: normalQuant = value;
0745: normalQuantChanged = true;
0746: }
0747:
0748: public String toString() {
0749: return "normalQuant: " + value;
0750: }
0751: }
0752:
0753: /**
0754: * Stream element that sets color quantization during quantize pass.
0755: */
0756: private class ColorQuant extends CompressionStreamElement {
0757: int value;
0758:
0759: ColorQuant(int value) {
0760: this .value = value;
0761: }
0762:
0763: void quantize(CompressionStream s, HuffmanTable t) {
0764: colorQuant = value;
0765: colorQuantChanged = true;
0766: }
0767:
0768: public String toString() {
0769: return "colorQuant: " + value;
0770: }
0771: }
0772:
0773: /**
0774: * Stream element that references the mesh buffer.
0775: */
0776: private class MeshReference extends CompressionStreamElement {
0777: int stripFlag, meshIndex;
0778:
0779: MeshReference(int stripFlag, int meshIndex) {
0780: this .stripFlag = stripFlag;
0781: this .meshIndex = meshIndex;
0782: meshReferenceCount++;
0783: }
0784:
0785: void quantize(CompressionStream s, HuffmanTable t) {
0786: // Retrieve the vertex from the mesh buffer mirror and set up the
0787: // data needed for the next stream element to compute its deltas.
0788: CompressionStreamVertex v = meshBuffer.getVertex(meshIndex);
0789: lastPosition[0] = v.xAbsolute;
0790: lastPosition[1] = v.yAbsolute;
0791: lastPosition[2] = v.zAbsolute;
0792:
0793: // Set up last color data if it exists and previous elements
0794: // don't override it.
0795: if (v.color != null && !lastElementColor
0796: && !(lastElementNormal && lastLastElementColor)) {
0797: lastColor[0] = v.color.rAbsolute;
0798: lastColor[1] = v.color.gAbsolute;
0799: lastColor[2] = v.color.bAbsolute;
0800: lastColor[3] = v.color.aAbsolute;
0801: }
0802:
0803: // Set up last normal data if it exists and previous element
0804: // doesn't override it.
0805: if (v.normal != null && !lastElementNormal
0806: && !(lastElementColor && lastLastElementNormal)) {
0807: lastSextant = v.normal.sextant;
0808: lastOctant = v.normal.octant;
0809: lastU = v.normal.uAbsolute;
0810: lastV = v.normal.vAbsolute;
0811: lastSpecialNormal = v.normal.specialNormal;
0812: }
0813: }
0814:
0815: void outputCommand(HuffmanTable t, CommandStream outputBuffer) {
0816: int command = CommandStream.MESH_B_R;
0817: long data = stripFlag & 0x1;
0818:
0819: command |= (((meshIndex & 0xf) << 1) | (stripFlag >> 1));
0820: outputBuffer.addCommand(command, 8, data, 1);
0821: }
0822:
0823: public String toString() {
0824: return "meshReference: stripFlag " + stripFlag
0825: + " meshIndex " + meshIndex;
0826: }
0827: }
0828:
0829: /**
0830: * Copy vertex data and add it to the end of this stream.
0831: * @param pos position data
0832: * @param stripFlag vertex replacement flag, either RESTART,
0833: * REPLACE_OLDEST, or REPLACE_MIDDLE
0834: */
0835: void addVertex(Point3f pos, int stripFlag) {
0836: stream.add(new CompressionStreamVertex(this , pos,
0837: (Vector3f) null, (Color3f) null, stripFlag,
0838: NO_MESH_PUSH));
0839: }
0840:
0841: /**
0842: * Copy vertex data and add it to the end of this stream.
0843: * @param pos position data
0844: * @param norm normal data
0845: * @param stripFlag vertex replacement flag, either RESTART,
0846: * REPLACE_OLDEST, or REPLACE_MIDDLE
0847: */
0848: void addVertex(Point3f pos, Vector3f norm, int stripFlag) {
0849: stream.add(new CompressionStreamVertex(this , pos, norm,
0850: (Color3f) null, stripFlag, NO_MESH_PUSH));
0851: }
0852:
0853: /**
0854: * Copy vertex data and add it to the end of this stream.
0855: * @param pos position data
0856: * @param color color data
0857: * @param stripFlag vertex replacement flag, either RESTART,
0858: * REPLACE_OLDEST, or REPLACE_MIDDLE
0859: */
0860: void addVertex(Point3f pos, Color3f color, int stripFlag) {
0861: stream.add(new CompressionStreamVertex(this , pos,
0862: (Vector3f) null, color, stripFlag, NO_MESH_PUSH));
0863: }
0864:
0865: /**
0866: * Copy vertex data and add it to the end of this stream.
0867: * @param pos position data
0868: * @param color color data
0869: * @param stripFlag vertex replacement flag, either RESTART,
0870: * REPLACE_OLDEST, or REPLACE_MIDDLE
0871: */
0872: void addVertex(Point3f pos, Color4f color, int stripFlag) {
0873: stream.add(new CompressionStreamVertex(this , pos,
0874: (Vector3f) null, color, stripFlag, NO_MESH_PUSH));
0875: }
0876:
0877: /**
0878: * Copy vertex data and add it to the end of this stream.
0879: * @param pos position data
0880: * @param norm normal data
0881: * @param color color data
0882: * @param stripFlag vertex replacement flag, either RESTART,
0883: * REPLACE_OLDEST, or REPLACE_MIDDLE
0884: */
0885: void addVertex(Point3f pos, Vector3f norm, Color3f color,
0886: int stripFlag) {
0887: stream.add(new CompressionStreamVertex(this , pos, norm, color,
0888: stripFlag, NO_MESH_PUSH));
0889: }
0890:
0891: /**
0892: * Copy vertex data and add it to the end of this stream.
0893: * @param pos position data
0894: * @param norm normal data
0895: * @param color color data
0896: * @param stripFlag vertex replacement flag, either RESTART,
0897: * REPLACE_OLDEST, or REPLACE_MIDDLE
0898: */
0899: void addVertex(Point3f pos, Vector3f norm, Color4f color,
0900: int stripFlag) {
0901: stream.add(new CompressionStreamVertex(this , pos, norm, color,
0902: stripFlag, NO_MESH_PUSH));
0903: }
0904:
0905: /**
0906: * Copy vertex data and add it to the end of this stream.
0907: * @param pos position data
0908: * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0909: * or REPLACE_MIDDLE
0910: * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0911: */
0912: void addVertex(Point3f pos, int stripFlag, int meshFlag) {
0913: stream.add(new CompressionStreamVertex(this , pos,
0914: (Vector3f) null, (Color3f) null, stripFlag, meshFlag));
0915: }
0916:
0917: /**
0918: * Copy vertex data and add it to the end of this stream.
0919: * @param pos position data
0920: * @param norm normal data
0921: * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0922: * or REPLACE_MIDDLE
0923: * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0924: */
0925: void addVertex(Point3f pos, Vector3f norm, int stripFlag,
0926: int meshFlag) {
0927: stream.add(new CompressionStreamVertex(this , pos, norm,
0928: (Color3f) null, stripFlag, meshFlag));
0929: }
0930:
0931: /**
0932: * Copy vertex data and add it to the end of this stream.
0933: * @param pos position data
0934: * @param color color data
0935: * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0936: * or REPLACE_MIDDLE
0937: * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0938: */
0939: void addVertex(Point3f pos, Color3f color, int stripFlag,
0940: int meshFlag) {
0941: stream.add(new CompressionStreamVertex(this , pos,
0942: (Vector3f) null, color, stripFlag, meshFlag));
0943: }
0944:
0945: /**
0946: * Copy vertex data and add it to the end of this stream.
0947: * @param pos position data
0948: * @param color color data
0949: * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0950: * or REPLACE_MIDDLE
0951: * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0952: */
0953: void addVertex(Point3f pos, Color4f color, int stripFlag,
0954: int meshFlag) {
0955: stream.add(new CompressionStreamVertex(this , pos,
0956: (Vector3f) null, color, stripFlag, meshFlag));
0957: }
0958:
0959: /**
0960: * Copy vertex data and add it to the end of this stream.
0961: * @param pos position data
0962: * @param norm normal data
0963: * @param color color data
0964: * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0965: * or REPLACE_MIDDLE
0966: * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0967: */
0968: void addVertex(Point3f pos, Vector3f norm, Color3f color,
0969: int stripFlag, int meshFlag) {
0970: stream.add(new CompressionStreamVertex(this , pos, norm, color,
0971: stripFlag, meshFlag));
0972: }
0973:
0974: /**
0975: * Copy vertex data and add it to the end of this stream.
0976: * @param pos position data
0977: * @param norm normal data
0978: * @param color color data
0979: * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0980: * or REPLACE_MIDDLE
0981: * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0982: */
0983: void addVertex(Point3f pos, Vector3f norm, Color4f color,
0984: int stripFlag, int meshFlag) {
0985: stream.add(new CompressionStreamVertex(this , pos, norm, color,
0986: stripFlag, meshFlag));
0987: }
0988:
0989: /**
0990: * Copy vertex data and add it to the end of this stream.
0991: * @param pos position data
0992: * @param norm normal data
0993: * @param color color data, either Color3f or Color4f, determined by
0994: * current vertex format
0995: * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0996: * or REPLACE_MIDDLE
0997: * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0998: */
0999: void addVertex(Point3f pos, Vector3f norm, Object color,
1000: int stripFlag, int meshFlag) {
1001:
1002: if (vertexColor3)
1003: stream.add(new CompressionStreamVertex(this , pos, norm,
1004: (Color3f) color, stripFlag, meshFlag));
1005: else
1006: stream.add(new CompressionStreamVertex(this , pos, norm,
1007: (Color4f) color, stripFlag, meshFlag));
1008: }
1009:
1010: /**
1011: * Add a mesh buffer reference to this stream.
1012: * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
1013: * or REPLACE_MIDDLE
1014: * @param meshIndex index of vertex to retrieve from the mesh buffer
1015: */
1016: void addMeshReference(int stripFlag, int meshIndex) {
1017: stream.add(new MeshReference(stripFlag, meshIndex));
1018: }
1019:
1020: /**
1021: * Copy the given color to the end of this stream and use it as a global
1022: * state change that applies to all subsequent vertices.
1023: */
1024: void addColor(Color3f c3f) {
1025: stream.add(new CompressionStreamColor(this , c3f));
1026: }
1027:
1028: /**
1029: * Copy the given color to the end of this stream and use it as a global
1030: * state change that applies to all subsequent vertices.
1031: */
1032: void addColor(Color4f c4f) {
1033: stream.add(new CompressionStreamColor(this , c4f));
1034: }
1035:
1036: /**
1037: * Copy the given normal to the end of this stream and use it as a global
1038: * state change that applies to all subsequent vertices.
1039: */
1040: void addNormal(Vector3f n) {
1041: stream.add(new CompressionStreamNormal(this , n));
1042: }
1043:
1044: /**
1045: * Add a new position quantization value to the end of this stream that
1046: * will apply to all subsequent vertex positions.
1047: *
1048: * @param value number of bits to quantize each position's X, Y,
1049: * and Z components, ranging from 1 to 16 with a default of 16
1050: */
1051: void addPositionQuantization(int value) {
1052: stream.add(new PositionQuant(value));
1053: }
1054:
1055: /**
1056: * Add a new color quantization value to the end of this stream that will
1057: * apply to all subsequent colors.
1058: *
1059: * @param value number of bits to quantize each color's R, G, B, and
1060: * alpha components, ranging from 2 to 16 with a default of 9
1061: */
1062: void addColorQuantization(int value) {
1063: stream.add(new ColorQuant(value));
1064: }
1065:
1066: /**
1067: * Add a new normal quantization value to the end of this stream that will
1068: * apply to all subsequent normals. This value specifies the number of
1069: * bits for each normal's U and V components.
1070: *
1071: * @param value number of bits for quantizing U and V, ranging from 0 to
1072: * 6 with a default of 6
1073: */
1074: void addNormalQuantization(int value) {
1075: stream.add(new NormalQuant(value));
1076: }
1077:
1078: /**
1079: * Interface to access GeometryArray vertex components and add them to the
1080: * compression stream.
1081: *
1082: * A processVertex() implementation retrieves vertex components using the
1083: * appropriate access semantics of a particular GeometryArray, and adds
1084: * them to the compression stream.
1085: *
1086: * The implementation always pushes vertices into the mesh buffer unless
1087: * they match ones already there; if they do, it generates mesh buffer
1088: * references instead. This reduces the number of vertices when
1089: * non-stripped abutting facets are added to the stream.
1090: *
1091: * Note: Level II geometry compression semantics allow the mesh buffer
1092: * normals to be substituted with the value of an immediately
1093: * preceding SetNormal command, but this is unavailable in Level I.
1094: *
1095: * @param index vertex offset from the beginning of its data array
1096: * @param stripFlag RESTART, REPLACE_MIDDLE, or REPLACE_OLDEST
1097: */
1098: private interface GeometryAccessor {
1099: void processVertex(int index, int stripFlag);
1100: }
1101:
1102: /**
1103: * This class implements the GeometryAccessor interface for geometry
1104: * arrays accessed with by-copy semantics.
1105: */
1106: private class ByCopyGeometry implements GeometryAccessor {
1107: Point3f[] positions = null;
1108: Vector3f[] normals = null;
1109: Color3f[] colors3 = null;
1110: Color4f[] colors4 = null;
1111:
1112: ByCopyGeometry(GeometryArray ga) {
1113: this (ga, ga.getInitialVertexIndex(), ga
1114: .getValidVertexCount());
1115: }
1116:
1117: ByCopyGeometry(GeometryArray ga, int firstVertex,
1118: int validVertexCount) {
1119: int i;
1120: positions = new Point3f[validVertexCount];
1121: for (i = 0; i < validVertexCount; i++)
1122: positions[i] = new Point3f();
1123:
1124: ga.getCoordinates(firstVertex, positions);
1125:
1126: if (vertexNormals) {
1127: normals = new Vector3f[validVertexCount];
1128: for (i = 0; i < validVertexCount; i++)
1129: normals[i] = new Vector3f();
1130:
1131: ga.getNormals(firstVertex, normals);
1132: }
1133:
1134: if (vertexColor3) {
1135: colors3 = new Color3f[validVertexCount];
1136: for (i = 0; i < validVertexCount; i++)
1137: colors3[i] = new Color3f();
1138:
1139: ga.getColors(firstVertex, colors3);
1140: } else if (vertexColor4) {
1141: colors4 = new Color4f[validVertexCount];
1142: for (i = 0; i < validVertexCount; i++)
1143: colors4[i] = new Color4f();
1144:
1145: ga.getColors(firstVertex, colors4);
1146: }
1147: }
1148:
1149: public void processVertex(int v, int stripFlag) {
1150: Point3f p = positions[v];
1151: int r = meshBuffer.getMeshReference(p);
1152:
1153: if ((r == meshBuffer.NOT_FOUND)
1154: || (vertexNormals && noMeshNormalSubstitution && (!normals[v]
1155: .equals(meshBuffer.getNormal(r))))) {
1156:
1157: Vector3f n = vertexNormals ? normals[v] : null;
1158: Object c = vertexColor3 ? (Object) colors3[v]
1159: : vertexColor4 ? (Object) colors4[v] : null;
1160:
1161: addVertex(p, n, c, stripFlag, MESH_PUSH);
1162: meshBuffer.push(p, c, n);
1163: } else {
1164: if (vertexNormals
1165: && !noMeshNormalSubstitution
1166: && (!normals[v].equals(meshBuffer.getNormal(r))))
1167: addNormal(normals[v]);
1168:
1169: if (vertexColor3
1170: && (!colors3[v].equals(meshBuffer.getColor3(r))))
1171: addColor(colors3[v]);
1172:
1173: else if (vertexColor4
1174: && (!colors4[v].equals(meshBuffer.getColor4(r))))
1175: addColor(colors4[v]);
1176:
1177: addMeshReference(stripFlag, r);
1178: }
1179: }
1180: }
1181:
1182: /**
1183: * Class which holds index array references for a geometry array.
1184: */
1185: private static class IndexArrays {
1186: int colorIndices[] = null;
1187: int normalIndices[] = null;
1188: int positionIndices[] = null;
1189: }
1190:
1191: /**
1192: * Retrieves index array references for the specified IndexedGeometryArray.
1193: * Index arrays are copied starting from initialIndexIndex.
1194: */
1195: private void getIndexArrays(GeometryArray ga, IndexArrays ia) {
1196: IndexedGeometryArray iga = (IndexedGeometryArray) ga;
1197:
1198: int initialIndexIndex = iga.getInitialIndexIndex();
1199: int indexCount = iga.getValidIndexCount();
1200: int vertexFormat = iga.getVertexFormat();
1201:
1202: boolean useCoordIndexOnly = false;
1203: if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
1204: if (debug)
1205: System.out.println("useCoordIndexOnly");
1206: useCoordIndexOnly = true;
1207: }
1208:
1209: ia.positionIndices = new int[indexCount];
1210: iga.getCoordinateIndices(initialIndexIndex, ia.positionIndices);
1211:
1212: if (vertexNormals) {
1213: if (useCoordIndexOnly) {
1214: ia.normalIndices = ia.positionIndices;
1215: } else {
1216: ia.normalIndices = new int[indexCount];
1217: iga.getNormalIndices(initialIndexIndex,
1218: ia.normalIndices);
1219: }
1220: }
1221: if (vertexColor3 || vertexColor4) {
1222: if (useCoordIndexOnly) {
1223: ia.colorIndices = ia.positionIndices;
1224: } else {
1225: ia.colorIndices = new int[indexCount];
1226: iga.getColorIndices(initialIndexIndex, ia.colorIndices);
1227: }
1228: }
1229: }
1230:
1231: /**
1232: * Class which holds indices for a specific vertex of an
1233: * IndexedGeometryArray.
1234: */
1235: private static class VertexIndices {
1236: int pi, ni, ci;
1237: }
1238:
1239: /**
1240: * Retrieves vertex indices for a specific vertex in an
1241: * IndexedGeometryArray.
1242: */
1243: private void getVertexIndices(int v, IndexArrays ia,
1244: VertexIndices vi) {
1245: vi.pi = ia.positionIndices[v];
1246: if (vertexNormals)
1247: vi.ni = ia.normalIndices[v];
1248: if (vertexColors)
1249: vi.ci = ia.colorIndices[v];
1250: }
1251:
1252: /**
1253: * This class implements the GeometryAccessor interface for indexed
1254: * geometry arrays accessed with by-copy semantics.
1255: */
1256: private class IndexedByCopyGeometry extends ByCopyGeometry {
1257: IndexArrays ia = new IndexArrays();
1258: VertexIndices vi = new VertexIndices();
1259:
1260: IndexedByCopyGeometry(GeometryArray ga) {
1261: super (ga, 0, ga.getVertexCount());
1262: getIndexArrays(ga, ia);
1263: }
1264:
1265: public void processVertex(int v, int stripFlag) {
1266: getVertexIndices(v, ia, vi);
1267: int r = meshBuffer.getMeshReference(vi.pi);
1268:
1269: if ((r == meshBuffer.NOT_FOUND)
1270: || (vertexNormals && noMeshNormalSubstitution && (vi.ni != meshBuffer
1271: .getNormalIndex(r)))) {
1272:
1273: Point3f p = positions[vi.pi];
1274: Vector3f n = vertexNormals ? normals[vi.ni] : null;
1275: Object c = vertexColor3 ? (Object) colors3[vi.ci]
1276: : vertexColor4 ? (Object) colors4[vi.ci] : null;
1277:
1278: addVertex(p, n, c, stripFlag, MESH_PUSH);
1279: meshBuffer.push(vi.pi, vi.ci, vi.ni);
1280: } else {
1281: if (vertexNormals && !noMeshNormalSubstitution
1282: && vi.ni != meshBuffer.getNormalIndex(r))
1283: addNormal(normals[vi.ni]);
1284:
1285: if (vertexColor3
1286: && vi.ci != meshBuffer.getColorIndex(r))
1287: addColor(colors3[vi.ci]);
1288:
1289: else if (vertexColor4
1290: && vi.ci != meshBuffer.getColorIndex(r))
1291: addColor(colors4[vi.ci]);
1292:
1293: addMeshReference(stripFlag, r);
1294: }
1295: }
1296: }
1297:
1298: //
1299: // NOTE: For now, copies are made of all GeometryArray vertex components
1300: // even when by-reference access is available.
1301: //
1302: private static class VertexCopy {
1303: Object c = null;
1304: Point3f p = null;
1305: Vector3f n = null;
1306: Color3f c3 = null;
1307: Color4f c4 = null;
1308: }
1309:
1310: private void processVertexCopy(VertexCopy vc, int stripFlag) {
1311: int r = meshBuffer.getMeshReference(vc.p);
1312:
1313: if ((r == meshBuffer.NOT_FOUND)
1314: || (vertexNormals && noMeshNormalSubstitution && (!vc.n
1315: .equals(meshBuffer.getNormal(r))))) {
1316:
1317: addVertex(vc.p, vc.n, vc.c, stripFlag, MESH_PUSH);
1318: meshBuffer.push(vc.p, vc.c, vc.n);
1319: } else {
1320: if (vertexNormals && !noMeshNormalSubstitution
1321: && (!vc.n.equals(meshBuffer.getNormal(r))))
1322: addNormal(vc.n);
1323:
1324: if (vertexColor3
1325: && (!vc.c3.equals(meshBuffer.getColor3(r))))
1326: addColor(vc.c3);
1327:
1328: else if (vertexColor4
1329: && (!vc.c4.equals(meshBuffer.getColor4(r))))
1330: addColor(vc.c4);
1331:
1332: addMeshReference(stripFlag, r);
1333: }
1334: }
1335:
1336: private void processIndexedVertexCopy(VertexCopy vc,
1337: VertexIndices vi, int stripFlag) {
1338:
1339: int r = meshBuffer.getMeshReference(vi.pi);
1340:
1341: if ((r == meshBuffer.NOT_FOUND)
1342: || (vertexNormals && noMeshNormalSubstitution && (vi.ni != meshBuffer
1343: .getNormalIndex(r)))) {
1344:
1345: addVertex(vc.p, vc.n, vc.c, stripFlag, MESH_PUSH);
1346: meshBuffer.push(vi.pi, vi.ci, vi.ni);
1347: } else {
1348: if (vertexNormals && !noMeshNormalSubstitution
1349: && vi.ni != meshBuffer.getNormalIndex(r))
1350: addNormal(vc.n);
1351:
1352: if (vertexColor3 && vi.ci != meshBuffer.getColorIndex(r))
1353: addColor(vc.c3);
1354:
1355: else if (vertexColor4
1356: && vi.ci != meshBuffer.getColorIndex(r))
1357: addColor(vc.c4);
1358:
1359: addMeshReference(stripFlag, r);
1360: }
1361: }
1362:
1363: /**
1364: * This abstract class implements the GeometryAccessor interface for
1365: * concrete subclasses which handle float and NIO interleaved geometry
1366: * arrays.
1367: */
1368: private abstract class InterleavedGeometry implements
1369: GeometryAccessor {
1370: VertexCopy vc = new VertexCopy();
1371:
1372: int vstride = 0;
1373: int coffset = 0;
1374: int noffset = 0;
1375: int poffset = 0;
1376: int tstride = 0;
1377: int tcount = 0;
1378:
1379: InterleavedGeometry(GeometryArray ga) {
1380: if (vertexTextures) {
1381: if (vertexTexture2)
1382: tstride = 2;
1383: else if (vertexTexture3)
1384: tstride = 3;
1385: else if (vertexTexture4)
1386: tstride = 4;
1387:
1388: tcount = ga.getTexCoordSetCount();
1389: vstride += tcount * tstride;
1390: }
1391:
1392: if (vertexColors) {
1393: coffset = vstride;
1394: if (vertexColor3)
1395: vstride += 3;
1396: else
1397: vstride += 4;
1398: }
1399:
1400: if (vertexNormals) {
1401: noffset = vstride;
1402: vstride += 3;
1403: }
1404:
1405: poffset = vstride;
1406: vstride += 3;
1407: }
1408:
1409: abstract void copyVertex(int pi, int ni, int ci, VertexCopy vc);
1410:
1411: public void processVertex(int v, int stripFlag) {
1412: copyVertex(v, v, v, vc);
1413: processVertexCopy(vc, stripFlag);
1414: }
1415: }
1416:
1417: /**
1418: * This class implements the GeometryAccessor interface for float
1419: * interleaved geometry arrays.
1420: */
1421: private class InterleavedGeometryFloat extends InterleavedGeometry {
1422: float[] vdata = null;
1423:
1424: InterleavedGeometryFloat(GeometryArray ga) {
1425: super (ga);
1426: vdata = ga.getInterleavedVertices();
1427: }
1428:
1429: void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1430: int voffset;
1431: voffset = pi * vstride;
1432: vc.p = new Point3f(vdata[voffset + poffset + 0],
1433: vdata[voffset + poffset + 1], vdata[voffset
1434: + poffset + 2]);
1435:
1436: if (vertexNormals) {
1437: voffset = ni * vstride;
1438: vc.n = new Vector3f(vdata[voffset + noffset + 0],
1439: vdata[voffset + noffset + 1], vdata[voffset
1440: + noffset + 2]);
1441: }
1442: if (vertexColor3) {
1443: voffset = ci * vstride;
1444: vc.c3 = new Color3f(vdata[voffset + coffset + 0],
1445: vdata[voffset + coffset + 1], vdata[voffset
1446: + coffset + 2]);
1447: vc.c = vc.c3;
1448: } else if (vertexColor4) {
1449: voffset = ci * vstride;
1450: vc.c4 = new Color4f(vdata[voffset + coffset + 0],
1451: vdata[voffset + coffset + 1], vdata[voffset
1452: + coffset + 2], vdata[voffset + coffset
1453: + 3]);
1454: vc.c = vc.c4;
1455: }
1456: }
1457: }
1458:
1459: /**
1460: * This class implements the GeometryAccessor interface for indexed
1461: * interleaved geometry arrays.
1462: */
1463: private class IndexedInterleavedGeometryFloat extends
1464: InterleavedGeometryFloat {
1465:
1466: IndexArrays ia = new IndexArrays();
1467: VertexIndices vi = new VertexIndices();
1468:
1469: IndexedInterleavedGeometryFloat(GeometryArray ga) {
1470: super (ga);
1471: getIndexArrays(ga, ia);
1472: }
1473:
1474: public void processVertex(int v, int stripFlag) {
1475: getVertexIndices(v, ia, vi);
1476: copyVertex(vi.pi, vi.ni, vi.ci, vc);
1477: processIndexedVertexCopy(vc, vi, stripFlag);
1478: }
1479: }
1480:
1481: /**
1482: * This class implements the GeometryAccessor interface for
1483: * interleaved NIO geometry arrays.
1484: */
1485: private class InterleavedGeometryNIO extends InterleavedGeometry {
1486: FloatBufferWrapper fbw = null;
1487:
1488: InterleavedGeometryNIO(GeometryArray ga) {
1489: super (ga);
1490: J3DBuffer buffer = ga.getInterleavedVertexBuffer();
1491: if (BufferWrapper.getBufferType(buffer) == BufferWrapper.TYPE_FLOAT) {
1492: fbw = new FloatBufferWrapper(buffer);
1493: } else {
1494: throw new IllegalArgumentException(
1495: "\ninterleaved vertex buffer must be FloatBuffer");
1496: }
1497: }
1498:
1499: void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1500: int voffset;
1501: voffset = pi * vstride;
1502: vc.p = new Point3f(fbw.get(voffset + poffset + 0), fbw
1503: .get(voffset + poffset + 1), fbw.get(voffset
1504: + poffset + 2));
1505:
1506: if (vertexNormals) {
1507: voffset = ni * vstride;
1508: vc.n = new Vector3f(fbw.get(voffset + noffset + 0), fbw
1509: .get(voffset + noffset + 1), fbw.get(voffset
1510: + noffset + 2));
1511: }
1512: if (vertexColor3) {
1513: voffset = ci * vstride;
1514: vc.c3 = new Color3f(fbw.get(voffset + coffset + 0), fbw
1515: .get(voffset + coffset + 1), fbw.get(voffset
1516: + coffset + 2));
1517: vc.c = vc.c3;
1518: } else if (vertexColor4) {
1519: voffset = ci * vstride;
1520: vc.c4 = new Color4f(fbw.get(voffset + coffset + 0), fbw
1521: .get(voffset + coffset + 1), fbw.get(voffset
1522: + coffset + 2), fbw.get(voffset + coffset + 3));
1523: vc.c = vc.c4;
1524: }
1525: }
1526: }
1527:
1528: /**
1529: * This class implements the GeometryAccessor interface for indexed
1530: * interleaved NIO geometry arrays.
1531: */
1532: private class IndexedInterleavedGeometryNIO extends
1533: InterleavedGeometryNIO {
1534: IndexArrays ia = new IndexArrays();
1535: VertexIndices vi = new VertexIndices();
1536:
1537: IndexedInterleavedGeometryNIO(GeometryArray ga) {
1538: super (ga);
1539: getIndexArrays(ga, ia);
1540: }
1541:
1542: public void processVertex(int v, int stripFlag) {
1543: getVertexIndices(v, ia, vi);
1544: copyVertex(vi.pi, vi.ni, vi.ci, vc);
1545: processIndexedVertexCopy(vc, vi, stripFlag);
1546: }
1547: }
1548:
1549: /**
1550: * This class implements the GeometryAccessor interface for
1551: * non-interleaved geometry arrays accessed with by-reference semantics.
1552: */
1553: private class ByRefGeometry implements GeometryAccessor {
1554: VertexCopy vc = new VertexCopy();
1555:
1556: byte[] colorsB = null;
1557: float[] colorsF = null;
1558: float[] normals = null;
1559: float[] positionsF = null;
1560: double[] positionsD = null;
1561:
1562: int initialPositionIndex = 0;
1563: int initialNormalIndex = 0;
1564: int initialColorIndex = 0;
1565:
1566: ByRefGeometry(GeometryArray ga) {
1567: positionsF = ga.getCoordRefFloat();
1568: if (debug && positionsF != null)
1569: System.out.println("float positions");
1570:
1571: positionsD = ga.getCoordRefDouble();
1572: if (debug && positionsD != null)
1573: System.out.println("double positions");
1574:
1575: if (positionsF == null && positionsD == null)
1576: throw new UnsupportedOperationException(
1577: "\nby-reference access to Point3{d,f} arrays");
1578:
1579: initialPositionIndex = ga.getInitialCoordIndex();
1580:
1581: if (vertexColors) {
1582: colorsB = ga.getColorRefByte();
1583: if (debug && colorsB != null)
1584: System.out.println("byte colors");
1585:
1586: colorsF = ga.getColorRefFloat();
1587: if (debug && colorsF != null)
1588: System.out.println("float colors");
1589:
1590: if (colorsB == null && colorsF == null)
1591: throw new UnsupportedOperationException(
1592: "\nby-reference access to Color{3b,3f,4b,4f} arrays");
1593:
1594: initialColorIndex = ga.getInitialColorIndex();
1595: }
1596:
1597: if (vertexNormals) {
1598: normals = ga.getNormalRefFloat();
1599: if (debug && normals != null)
1600: System.out.println("float normals");
1601:
1602: if (normals == null)
1603: throw new UnsupportedOperationException(
1604: "\nby-reference access to Normal3f array");
1605:
1606: initialNormalIndex = ga.getInitialNormalIndex();
1607: }
1608: }
1609:
1610: void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1611: pi *= 3;
1612: if (positionsF != null) {
1613: vc.p = new Point3f(positionsF[pi + 0],
1614: positionsF[pi + 1], positionsF[pi + 2]);
1615: } else {
1616: vc.p = new Point3f((float) positionsD[pi + 0],
1617: (float) positionsD[pi + 1],
1618: (float) positionsD[pi + 2]);
1619: }
1620:
1621: ni *= 3;
1622: if (vertexNormals) {
1623: vc.n = new Vector3f(normals[ni + 0], normals[ni + 1],
1624: normals[ni + 2]);
1625: }
1626:
1627: if (vertexColor3) {
1628: ci *= 3;
1629: if (colorsB != null) {
1630: vc.c3 = new Color3f(
1631: (colorsB[ci + 0] & 0xff) * ByteToFloatScale,
1632: (colorsB[ci + 1] & 0xff) * ByteToFloatScale,
1633: (colorsB[ci + 2] & 0xff) * ByteToFloatScale);
1634: } else {
1635: vc.c3 = new Color3f(colorsF[ci + 0],
1636: colorsF[ci + 1], colorsF[ci + 2]);
1637: }
1638: vc.c = vc.c3;
1639: } else if (vertexColor4) {
1640: ci *= 4;
1641: if (colorsB != null) {
1642: vc.c4 = new Color4f(
1643: (colorsB[ci + 0] & 0xff) * ByteToFloatScale,
1644: (colorsB[ci + 1] & 0xff) * ByteToFloatScale,
1645: (colorsB[ci + 2] & 0xff) * ByteToFloatScale,
1646: (colorsB[ci + 3] & 0xff) * ByteToFloatScale);
1647: } else {
1648: vc.c4 = new Color4f(colorsF[ci + 0],
1649: colorsF[ci + 1], colorsF[ci + 2],
1650: colorsF[ci + 3]);
1651: }
1652: vc.c = vc.c4;
1653: }
1654: }
1655:
1656: public void processVertex(int v, int stripFlag) {
1657: copyVertex(v + initialPositionIndex,
1658: v + initialNormalIndex, v + initialColorIndex, vc);
1659:
1660: processVertexCopy(vc, stripFlag);
1661: }
1662: }
1663:
1664: /**
1665: * This class implements the GeometryAccessor interface for indexed
1666: * non-interleaved geometry arrays accessed with by-reference semantics.
1667: */
1668: private class IndexedByRefGeometry extends ByRefGeometry {
1669: IndexArrays ia = new IndexArrays();
1670: VertexIndices vi = new VertexIndices();
1671:
1672: IndexedByRefGeometry(GeometryArray ga) {
1673: super (ga);
1674: getIndexArrays(ga, ia);
1675: }
1676:
1677: public void processVertex(int v, int stripFlag) {
1678: getVertexIndices(v, ia, vi);
1679: copyVertex(vi.pi, vi.ni, vi.ci, vc);
1680: processIndexedVertexCopy(vc, vi, stripFlag);
1681: }
1682: }
1683:
1684: /**
1685: * This class implements the GeometryAccessor interface for
1686: * non-interleaved geometry arrays accessed with NIO.
1687: */
1688: private class ByRefGeometryNIO implements GeometryAccessor {
1689: VertexCopy vc = new VertexCopy();
1690:
1691: ByteBufferWrapper colorsB = null;
1692: FloatBufferWrapper colorsF = null;
1693: FloatBufferWrapper normals = null;
1694: FloatBufferWrapper positionsF = null;
1695: DoubleBufferWrapper positionsD = null;
1696:
1697: int initialPositionIndex = 0;
1698: int initialNormalIndex = 0;
1699: int initialColorIndex = 0;
1700:
1701: ByRefGeometryNIO(GeometryArray ga) {
1702: J3DBuffer buffer;
1703: buffer = ga.getCoordRefBuffer();
1704: initialPositionIndex = ga.getInitialCoordIndex();
1705:
1706: switch (BufferWrapper.getBufferType(buffer)) {
1707: case BufferWrapper.TYPE_FLOAT:
1708: positionsF = new FloatBufferWrapper(buffer);
1709: if (debug)
1710: System.out.println("float positions buffer");
1711: break;
1712: case BufferWrapper.TYPE_DOUBLE:
1713: positionsD = new DoubleBufferWrapper(buffer);
1714: if (debug)
1715: System.out.println("double positions buffer");
1716: break;
1717: default:
1718: throw new IllegalArgumentException(
1719: "\nposition buffer must be FloatBuffer or DoubleBuffer");
1720: }
1721:
1722: if (vertexColors) {
1723: buffer = ga.getColorRefBuffer();
1724: initialColorIndex = ga.getInitialColorIndex();
1725:
1726: switch (BufferWrapper.getBufferType(buffer)) {
1727: case BufferWrapper.TYPE_BYTE:
1728: colorsB = new ByteBufferWrapper(buffer);
1729: if (debug)
1730: System.out.println("byte colors buffer");
1731: break;
1732: case BufferWrapper.TYPE_FLOAT:
1733: colorsF = new FloatBufferWrapper(buffer);
1734: if (debug)
1735: System.out.println("float colors buffer");
1736: break;
1737: default:
1738: throw new IllegalArgumentException(
1739: "\ncolor buffer must be ByteBuffer or FloatBuffer");
1740: }
1741: }
1742:
1743: if (vertexNormals) {
1744: buffer = ga.getNormalRefBuffer();
1745: initialNormalIndex = ga.getInitialNormalIndex();
1746:
1747: switch (BufferWrapper.getBufferType(buffer)) {
1748: case BufferWrapper.TYPE_FLOAT:
1749: normals = new FloatBufferWrapper(buffer);
1750: if (debug)
1751: System.out.println("float normals buffer");
1752: break;
1753: default:
1754: throw new IllegalArgumentException(
1755: "\nnormal buffer must be FloatBuffer");
1756: }
1757: }
1758: }
1759:
1760: void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1761: pi *= 3;
1762: if (positionsF != null) {
1763: vc.p = new Point3f(positionsF.get(pi + 0), positionsF
1764: .get(pi + 1), positionsF.get(pi + 2));
1765: } else {
1766: vc.p = new Point3f((float) positionsD.get(pi + 0),
1767: (float) positionsD.get(pi + 1),
1768: (float) positionsD.get(pi + 2));
1769: }
1770:
1771: ni *= 3;
1772: if (vertexNormals) {
1773: vc.n = new Vector3f(normals.get(ni + 0), normals
1774: .get(ni + 1), normals.get(ni + 2));
1775: }
1776:
1777: if (vertexColor3) {
1778: ci *= 3;
1779: if (colorsB != null) {
1780: vc.c3 = new Color3f((colorsB.get(ci + 0) & 0xff)
1781: * ByteToFloatScale,
1782: (colorsB.get(ci + 1) & 0xff)
1783: * ByteToFloatScale, (colorsB
1784: .get(ci + 2) & 0xff)
1785: * ByteToFloatScale);
1786: } else {
1787: vc.c3 = new Color3f(colorsF.get(ci + 0), colorsF
1788: .get(ci + 1), colorsF.get(ci + 2));
1789: }
1790: vc.c = vc.c3;
1791: } else if (vertexColor4) {
1792: ci *= 4;
1793: if (colorsB != null) {
1794: vc.c4 = new Color4f((colorsB.get(ci + 0) & 0xff)
1795: * ByteToFloatScale,
1796: (colorsB.get(ci + 1) & 0xff)
1797: * ByteToFloatScale, (colorsB
1798: .get(ci + 2) & 0xff)
1799: * ByteToFloatScale, (colorsB
1800: .get(ci + 3) & 0xff)
1801: * ByteToFloatScale);
1802: } else {
1803: vc.c4 = new Color4f(colorsF.get(ci + 0), colorsF
1804: .get(ci + 1), colorsF.get(ci + 2), colorsF
1805: .get(ci + 3));
1806: }
1807: vc.c = vc.c4;
1808: }
1809: }
1810:
1811: public void processVertex(int v, int stripFlag) {
1812: copyVertex(v + initialPositionIndex,
1813: v + initialNormalIndex, v + initialColorIndex, vc);
1814:
1815: processVertexCopy(vc, stripFlag);
1816: }
1817: }
1818:
1819: /**
1820: * This class implements the GeometryAccessor interface for
1821: * non-interleaved indexed geometry arrays accessed with NIO.
1822: */
1823: private class IndexedByRefGeometryNIO extends ByRefGeometryNIO {
1824: IndexArrays ia = new IndexArrays();
1825: VertexIndices vi = new VertexIndices();
1826:
1827: IndexedByRefGeometryNIO(GeometryArray ga) {
1828: super (ga);
1829: getIndexArrays(ga, ia);
1830: }
1831:
1832: public void processVertex(int v, int stripFlag) {
1833: getVertexIndices(v, ia, vi);
1834: copyVertex(vi.pi, vi.ni, vi.ci, vc);
1835: processIndexedVertexCopy(vc, vi, stripFlag);
1836: }
1837: }
1838:
1839: /**
1840: * Convert a GeometryArray to compression stream elements and add them to
1841: * this stream.
1842: *
1843: * @param ga GeometryArray to convert
1844: * @exception IllegalArgumentException if GeometryArray has a
1845: * dimensionality or vertex format inconsistent with the CompressionStream
1846: */
1847: void addGeometryArray(GeometryArray ga) {
1848: int firstVertex = 0;
1849: int validVertexCount = 0;
1850: int vertexFormat = ga.getVertexFormat();
1851: GeometryAccessor geometryAccessor = null;
1852:
1853: if (streamType != getStreamType(ga))
1854: throw new IllegalArgumentException(
1855: "GeometryArray has inconsistent dimensionality");
1856:
1857: if (vertexComponents != getVertexComponents(vertexFormat))
1858: throw new IllegalArgumentException(
1859: "GeometryArray has inconsistent vertex components");
1860:
1861: // Set up for vertex data access semantics.
1862: boolean NIO = (vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0;
1863: boolean byRef = (vertexFormat & GeometryArray.BY_REFERENCE) != 0;
1864: boolean interleaved = (vertexFormat & GeometryArray.INTERLEAVED) != 0;
1865: boolean indexedGeometry = ga instanceof IndexedGeometryArray;
1866:
1867: if (indexedGeometry) {
1868: if (debug)
1869: System.out.println("indexed");
1870: // Index arrays will be copied such that valid indices start at
1871: // offset 0 in the copied arrays.
1872: firstVertex = 0;
1873: validVertexCount = ((IndexedGeometryArray) ga)
1874: .getValidIndexCount();
1875: }
1876:
1877: if (!byRef) {
1878: if (debug)
1879: System.out.println("by-copy");
1880: if (indexedGeometry) {
1881: geometryAccessor = new IndexedByCopyGeometry(ga);
1882: } else {
1883: firstVertex = 0;
1884: validVertexCount = ga.getValidVertexCount();
1885: geometryAccessor = new ByCopyGeometry(ga);
1886: }
1887: } else if (interleaved && NIO) {
1888: if (debug)
1889: System.out.println("interleaved NIO");
1890: if (indexedGeometry) {
1891: geometryAccessor = new IndexedInterleavedGeometryNIO(ga);
1892: } else {
1893: firstVertex = ga.getInitialVertexIndex();
1894: validVertexCount = ga.getValidVertexCount();
1895: geometryAccessor = new InterleavedGeometryNIO(ga);
1896: }
1897: } else if (interleaved && !NIO) {
1898: if (debug)
1899: System.out.println("interleaved");
1900: if (indexedGeometry) {
1901: geometryAccessor = new IndexedInterleavedGeometryFloat(
1902: ga);
1903: } else {
1904: firstVertex = ga.getInitialVertexIndex();
1905: validVertexCount = ga.getValidVertexCount();
1906: geometryAccessor = new InterleavedGeometryFloat(ga);
1907: }
1908: } else if (!interleaved && NIO) {
1909: if (debug)
1910: System.out.println("non-interleaved NIO");
1911: if (indexedGeometry) {
1912: geometryAccessor = new IndexedByRefGeometryNIO(ga);
1913: } else {
1914: firstVertex = 0;
1915: validVertexCount = ga.getValidVertexCount();
1916: geometryAccessor = new ByRefGeometryNIO(ga);
1917: }
1918: } else if (!interleaved && !NIO) {
1919: if (debug)
1920: System.out.println("non-interleaved by-ref");
1921: if (indexedGeometry) {
1922: geometryAccessor = new IndexedByRefGeometry(ga);
1923: } else {
1924: firstVertex = 0;
1925: validVertexCount = ga.getValidVertexCount();
1926: geometryAccessor = new ByRefGeometry(ga);
1927: }
1928: }
1929:
1930: // Set up for topology.
1931: int stripCount = 0;
1932: int stripCounts[] = null;
1933: int constantStripLength = 0;
1934: int replaceCode = RESTART;
1935: boolean strips = false;
1936: boolean implicitStrips = false;
1937:
1938: if (ga instanceof TriangleStripArray
1939: || ga instanceof IndexedTriangleStripArray
1940: || ga instanceof LineStripArray
1941: || ga instanceof IndexedLineStripArray) {
1942:
1943: strips = true;
1944: replaceCode = REPLACE_OLDEST;
1945: if (debug)
1946: System.out.println("strips");
1947: } else if (ga instanceof TriangleFanArray
1948: || ga instanceof IndexedTriangleFanArray) {
1949:
1950: strips = true;
1951: replaceCode = REPLACE_MIDDLE;
1952: if (debug)
1953: System.out.println("fans");
1954: } else if (ga instanceof QuadArray
1955: || ga instanceof IndexedQuadArray) {
1956:
1957: // Handled as fan arrays with 4 vertices per fan.
1958: implicitStrips = true;
1959: constantStripLength = 4;
1960: replaceCode = REPLACE_MIDDLE;
1961: if (debug)
1962: System.out.println("quads");
1963: }
1964:
1965: // Get strip counts.
1966: if (strips) {
1967: if (indexedGeometry) {
1968: IndexedGeometryStripArray igsa;
1969: igsa = (IndexedGeometryStripArray) ga;
1970:
1971: stripCount = igsa.getNumStrips();
1972: stripCounts = new int[stripCount];
1973: igsa.getStripIndexCounts(stripCounts);
1974:
1975: } else {
1976: GeometryStripArray gsa;
1977: gsa = (GeometryStripArray) ga;
1978:
1979: stripCount = gsa.getNumStrips();
1980: stripCounts = new int[stripCount];
1981: gsa.getStripVertexCounts(stripCounts);
1982: }
1983: }
1984:
1985: // Build the compression stream for this shape's geometry.
1986: int v = firstVertex;
1987: if (strips) {
1988: for (int i = 0; i < stripCount; i++) {
1989: geometryAccessor.processVertex(v++, RESTART);
1990: for (int j = 1; j < stripCounts[i]; j++) {
1991: geometryAccessor.processVertex(v++, replaceCode);
1992: }
1993: }
1994: } else if (implicitStrips) {
1995: while (v < firstVertex + validVertexCount) {
1996: geometryAccessor.processVertex(v++, RESTART);
1997: for (int j = 1; j < constantStripLength; j++) {
1998: geometryAccessor.processVertex(v++, replaceCode);
1999: }
2000: }
2001: } else {
2002: while (v < firstVertex + validVertexCount) {
2003: geometryAccessor.processVertex(v++, RESTART);
2004: }
2005: }
2006: }
2007:
2008: /**
2009: * Print the stream to standard output.
2010: */
2011: void print() {
2012: System.out
2013: .println("\nstream has " + stream.size() + " entries");
2014: System.out.println("uncompressed size " + byteCount + " bytes");
2015: System.out.println("upper position bound: "
2016: + mcBounds[1].toString());
2017: System.out.println("lower position bound: "
2018: + mcBounds[0].toString());
2019: System.out.println("X, Y, Z centers (" + ((float) center[0])
2020: + " " + ((float) center[1]) + " " + ((float) center[2])
2021: + ")\n" + "scale " + ((float) scale) + "\n");
2022:
2023: Iterator i = stream.iterator();
2024: while (i.hasNext()) {
2025: System.out.println(i.next().toString() + "\n");
2026: }
2027: }
2028:
2029: ////////////////////////////////////////////////////////////////////////////
2030: // //
2031: // The following constructors and methods are currently the only public //
2032: // members of this class. All other members are subject to revision. //
2033: // //
2034: ////////////////////////////////////////////////////////////////////////////
2035:
2036: /**
2037: * Creates a CompressionStream from an array of Shape3D scene graph
2038: * objects. These Shape3D objects may only consist of a GeometryArray
2039: * component and an optional Appearance component. The resulting stream
2040: * may be used as input to the GeometryCompressor methods.<p>
2041: *
2042: * Each Shape3D in the array must be of the same dimensionality (point,
2043: * line, or surface) and have the same vertex format as the others.
2044: * Texture coordinates are ignored.<p>
2045: *
2046: * If a color is specified in the material attributes for a Shape3D then
2047: * that color is added to the CompressionStream as the current global
2048: * color. Subsequent colors as well as any colors bundled with vertices
2049: * will override it. Only the material diffuse colors are used; all other
2050: * appearance attributes are ignored.<p>
2051: *
2052: * @param positionQuant
2053: * number of bits to quantize each position's X, Y,
2054: * and Z components, ranging from 1 to 16
2055: *
2056: * @param colorQuant
2057: * number of bits to quantize each color's R, G, B, and
2058: * alpha components, ranging from 2 to 16
2059: *
2060: * @param normalQuant
2061: * number of bits for quantizing each normal's U and V components, ranging
2062: * from 0 to 6
2063: *
2064: * @param shapes
2065: * an array of Shape3D scene graph objects containing
2066: * GeometryArray objects, all with the same vertex format and
2067: * dimensionality
2068: *
2069: * @exception IllegalArgumentException if any Shape3D has an inconsistent
2070: * dimensionality or vertex format, or if any Shape3D contains a geometry
2071: * component that is not a GeometryArray
2072: *
2073: * @see Shape3D
2074: * @see GeometryArray
2075: * @see GeometryCompressor
2076: */
2077: public CompressionStream(int positionQuant, int colorQuant,
2078: int normalQuant, Shape3D shapes[]) {
2079: this ();
2080: if (debug)
2081: System.out.println("CompressionStream(Shape3D[]):");
2082:
2083: if (shapes == null)
2084: throw new IllegalArgumentException("null Shape3D array");
2085:
2086: if (shapes.length == 0)
2087: throw new IllegalArgumentException(
2088: "zero-length Shape3D array");
2089:
2090: if (shapes[0] == null)
2091: throw new IllegalArgumentException(
2092: "Shape3D at index 0 is null");
2093:
2094: long startTime = 0;
2095: if (benchmark)
2096: startTime = System.currentTimeMillis();
2097:
2098: Geometry g = shapes[0].getGeometry();
2099: if (!(g instanceof GeometryArray))
2100: throw new IllegalArgumentException(
2101: "Shape3D at index 0 is not a GeometryArray");
2102:
2103: GeometryArray ga = (GeometryArray) g;
2104: this .streamType = getStreamType(ga);
2105: this .vertexComponents = getVertexComponents(ga
2106: .getVertexFormat());
2107:
2108: // Add global quantization parameters to the start of the stream.
2109: addPositionQuantization(positionQuant);
2110: addColorQuantization(colorQuant);
2111: addNormalQuantization(normalQuant);
2112:
2113: // Loop through all shapes.
2114: for (int s = 0; s < shapes.length; s++) {
2115: if (debug)
2116: System.out.println("\nShape3D " + s + ":");
2117:
2118: g = shapes[s].getGeometry();
2119: if (!(g instanceof GeometryArray))
2120: throw new IllegalArgumentException("Shape3D at index "
2121: + s + " is not a GeometryArray");
2122:
2123: // Check for material color and add it to the stream if it exists.
2124: Appearance a = shapes[s].getAppearance();
2125: if (a != null) {
2126: Material m = a.getMaterial();
2127: if (m != null) {
2128: m.getDiffuseColor(c3f);
2129: if (vertexColor4) {
2130: c4f.set(c3f.x, c3f.y, c3f.z, 1.0f);
2131: addColor(c4f);
2132: } else
2133: addColor(c3f);
2134: }
2135: }
2136:
2137: // Add the geometry array to the stream.
2138: addGeometryArray((GeometryArray) g);
2139: }
2140:
2141: if (benchmark) {
2142: long t = System.currentTimeMillis() - startTime;
2143: System.out.println("\nCompressionStream:\n" + shapes.length
2144: + " shapes in " + (t / 1000f) + " sec");
2145: }
2146: }
2147:
2148: /**
2149: * Creates a CompressionStream from an array of Shape3D scene graph
2150: * objects. These Shape3D objects may only consist of a GeometryArray
2151: * component and an optional Appearance component. The resulting stream
2152: * may be used as input to the GeometryCompressor methods.<p>
2153: *
2154: * Each Shape3D in the array must be of the same dimensionality (point,
2155: * line, or surface) and have the same vertex format as the others.
2156: * Texture coordinates are ignored.<p>
2157: *
2158: * If a color is specified in the material attributes for a Shape3D then
2159: * that color is added to the CompressionStream as the current global
2160: * color. Subsequent colors as well as any colors bundled with vertices
2161: * will override it. Only the material diffuse colors are used; all other
2162: * appearance attributes are ignored.<p>
2163: *
2164: * Defaults of 16, 9, and 6 bits are used as the quantization values for
2165: * positions, colors, and normals respectively. These are the maximum
2166: * resolution values defined for positions and normals; the default of 9
2167: * for color is the equivalent of the 8 bits of RGBA component resolution
2168: * commonly available in graphics frame buffers.<p>
2169: *
2170: * @param shapes
2171: * an array of Shape3D scene graph objects containing
2172: * GeometryArray objects, all with the same vertex format and
2173: * dimensionality.
2174: *
2175: * @exception IllegalArgumentException if any Shape3D has an inconsistent
2176: * dimensionality or vertex format, or if any Shape3D contains a geometry
2177: * component that is not a GeometryArray
2178: *
2179: * @see Shape3D
2180: * @see GeometryArray
2181: * @see GeometryCompressor
2182: */
2183: public CompressionStream(Shape3D shapes[]) {
2184: this (16, 9, 6, shapes);
2185: }
2186:
2187: /**
2188: * Creates a CompressionStream from an array of GeometryInfo objects. The
2189: * resulting stream may be used as input to the GeometryCompressor
2190: * methods.<p>
2191: *
2192: * Each GeometryInfo in the array must be of the same dimensionality
2193: * (point, line, or surface) and have the same vertex format as the
2194: * others. Texture coordinates are ignored.<p>
2195: *
2196: * @param positionQuant
2197: * number of bits to quantize each position's X, Y,
2198: * and Z components, ranging from 1 to 16
2199: *
2200: * @param colorQuant
2201: * number of bits to quantize each color's R, G, B, and
2202: * alpha components, ranging from 2 to 16
2203: *
2204: * @param normalQuant
2205: * number of bits for quantizing each normal's U and V components, ranging
2206: * from 0 to 6
2207: *
2208: * @param geometry
2209: * an array of GeometryInfo objects, all with the same
2210: * vertex format and dimensionality
2211: *
2212: * @exception IllegalArgumentException if any GeometryInfo object has an
2213: * inconsistent dimensionality or vertex format
2214: *
2215: * @see GeometryInfo
2216: * @see GeometryCompressor
2217: */
2218: public CompressionStream(int positionQuant, int colorQuant,
2219: int normalQuant, GeometryInfo geometry[]) {
2220: this ();
2221: if (debug)
2222: System.out.println("CompressionStream(GeometryInfo[])");
2223:
2224: if (geometry == null)
2225: throw new IllegalArgumentException(
2226: "null GeometryInfo array");
2227:
2228: if (geometry.length == 0)
2229: throw new IllegalArgumentException(
2230: "zero-length GeometryInfo array");
2231:
2232: if (geometry[0] == null)
2233: throw new IllegalArgumentException(
2234: "GeometryInfo at index 0 is null");
2235:
2236: long startTime = 0;
2237: if (benchmark)
2238: startTime = System.currentTimeMillis();
2239:
2240: GeometryArray ga = geometry[0].getGeometryArray();
2241: this .streamType = getStreamType(ga);
2242: this .vertexComponents = getVertexComponents(ga
2243: .getVertexFormat());
2244:
2245: // Add global quantization parameters to the start of the stream.
2246: addPositionQuantization(positionQuant);
2247: addColorQuantization(colorQuant);
2248: addNormalQuantization(normalQuant);
2249:
2250: // Loop through all GeometryInfo objects and add them to the stream.
2251: for (int i = 0; i < geometry.length; i++) {
2252: if (debug)
2253: System.out.println("\nGeometryInfo " + i + ":");
2254: addGeometryArray(geometry[i].getGeometryArray());
2255: }
2256:
2257: if (benchmark) {
2258: long t = System.currentTimeMillis() - startTime;
2259: System.out.println("\nCompressionStream:\n"
2260: + geometry.length + " GeometryInfo objects in "
2261: + (t / 1000f) + " sec");
2262: }
2263: }
2264:
2265: /**
2266: * Creates a CompressionStream from an array of GeometryInfo objects. The
2267: * resulting stream may be used as input to the GeometryCompressor
2268: * methods.<p>
2269: *
2270: * Each GeometryInfo in the array must be of the same dimensionality
2271: * (point, line, or surface) and have the same vertex format as the
2272: * others. Texture coordinates are ignored.<p>
2273: *
2274: * Defaults of 16, 9, and 6 bits are used as the quantization values for
2275: * positions, colors, and normals respectively. These are the maximum
2276: * resolution values defined for positions and normals; the default of 9
2277: * for color is the equivalent of the 8 bits of RGBA component resolution
2278: * commonly available in graphics frame buffers.<p>
2279: *
2280: * @param geometry
2281: * an array of GeometryInfo objects, all with the same
2282: * vertex format and dimensionality
2283: *
2284: * @exception IllegalArgumentException if any GeometryInfo object has an
2285: * inconsistent dimensionality or vertex format
2286: *
2287: * @see GeometryInfo
2288: * @see GeometryCompressor
2289: */
2290: public CompressionStream(GeometryInfo geometry[]) {
2291: this (16, 9, 6, geometry);
2292: }
2293:
2294: /**
2295: * Get the original bounds of the coordinate data, in modeling coordinates.
2296: * Coordinate data is positioned and scaled to a normalized cube after
2297: * compression.
2298: *
2299: * @return Point3d array of length 2, where the 1st Point3d is the lower
2300: * bounds and the 2nd Point3d is the upper bounds.
2301: * @since Java 3D 1.3
2302: */
2303: public Point3d[] getModelBounds() {
2304: Point3d[] bounds = new Point3d[2];
2305: bounds[0] = new Point3d(mcBounds[0]);
2306: bounds[1] = new Point3d(mcBounds[1]);
2307: return bounds;
2308: }
2309:
2310: /**
2311: * Get the bounds of the compressed object in normalized coordinates.
2312: * These have an maximum bounds by [-1.0 .. +1.0] across each axis.
2313: *
2314: * @return Point3d array of length 2, where the 1st Point3d is the lower
2315: * bounds and the 2nd Point3d is the upper bounds.
2316: * @since Java 3D 1.3
2317: */
2318: public Point3d[] getNormalizedBounds() {
2319: Point3d[] bounds = new Point3d[2];
2320: bounds[0] = new Point3d(ncBounds[0]);
2321: bounds[1] = new Point3d(ncBounds[1]);
2322: return bounds;
2323: }
2324: }
|