All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scijava.java3d.utils.geometry.compression.CompressionStream Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistribution of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
 * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or
 * intended for use in the design, construction, operation or
 * maintenance of any nuclear facility.
 *
 */

package org.scijava.java3d.utils.geometry.compression;

import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;

import org.scijava.java3d.Appearance;
import org.scijava.java3d.Geometry;
import org.scijava.java3d.GeometryArray;
import org.scijava.java3d.GeometryStripArray;
import org.scijava.java3d.IndexedGeometryArray;
import org.scijava.java3d.IndexedGeometryStripArray;
import org.scijava.java3d.IndexedLineArray;
import org.scijava.java3d.IndexedLineStripArray;
import org.scijava.java3d.IndexedQuadArray;
import org.scijava.java3d.IndexedTriangleArray;
import org.scijava.java3d.IndexedTriangleFanArray;
import org.scijava.java3d.IndexedTriangleStripArray;
import org.scijava.java3d.J3DBuffer;
import org.scijava.java3d.LineArray;
import org.scijava.java3d.LineStripArray;
import org.scijava.java3d.Material;
import org.scijava.java3d.QuadArray;
import org.scijava.java3d.Shape3D;
import org.scijava.java3d.TriangleArray;
import org.scijava.java3d.TriangleFanArray;
import org.scijava.java3d.TriangleStripArray;
import org.scijava.vecmath.Color3f;
import org.scijava.vecmath.Color4f;
import org.scijava.vecmath.Point3d;
import org.scijava.vecmath.Point3f;
import org.scijava.vecmath.Point3i;
import org.scijava.vecmath.Vector3f;

import org.scijava.java3d.internal.BufferWrapper;
import org.scijava.java3d.utils.geometry.GeometryInfo;

/**
 * This class is used as input to a geometry compressor.  It collects elements
 * such as vertices, normals, colors, mesh references, and quantization
 * parameters in an ordered stream.  This stream is then traversed during
 * the compression process and used to build the compressed output buffer.
 *
 * @see GeometryCompressor
 *
 * @since Java 3D 1.5
 */
public class CompressionStream {
    //
    // NOTE: For now, copies are made of all GeometryArray vertex components
    // even when by-reference access is available.
    //
    // TODO: Retrofit all CompressionStreamElements and MeshBuffer to handle
    // offsets to vertex data array references so that vertex components don't
    // have to be copied.  New CompressionStreamElements could be defined to
    // set the current array reference during the quantization pass, or the
    // reference could be included in every CompressionStreamElement along
    // with the data offsets.
    //
    // TODO: Quantize on-the-fly when adding GeometryArray vertex data so that
    // CompressionStreamElements don't need references to the original float,
    // double, or byte data.  Quantization is currently a separate pass since
    // the 1st pass adds vertex data and gets the total object bounds, but
    // this can be computed by merging the bounds of each GeometryArray
    // compressed into a single object.  The 2nd pass quantization is still
    // needed for vertex data which isn't retrieved from a GeometryArray; for
    // example, apps that might use the addVertex() methods directly instead
    // of addGeometryArray().
    //
    // TODO: To further optimize memory, create new subclasses of
    // CompressionStream{Color, Normal} for bundled attributes and add them as
    // explicit stream elements.  Then CompressionStreamVertex won't need to
    // carry references to them.  This memory savings might be negated by the
    // extra overhead of adding more elements to the stream, however.
    //
    // TODO: Keep the absolute quantized values in the mesh buffer mirror so
    // that unmeshed CompressionStreamElements don't need to carry them.
    //
    // TODO: Support texture coordinate compression even though Level II is
    // not supported by any hardware decompressor on any graphics card.
    // Software decompression is still useful for applications interested in
    // minimizing file space, transmission time, and object loading time.
    //
    private static final boolean debug = false ;
    private static final boolean benchmark = false ;

    // Mesh buffer normal substitution is unavailable in Level I.
    private static final boolean noMeshNormalSubstitution = true ;

    /**
     * This flag indicates that a vertex starts a new triangle or line strip.
     */
    static final int RESTART = 1 ;

    /**
     * This flag indicates that the next triangle in the strip is defined by
     * replacing the middle vertex of the previous triangle in the strip.
     * Equivalent to REPLACE_OLDEST for line strips.
     */
    static final int REPLACE_MIDDLE = 2 ;

    /**
     * This flag indicates that the next triangle in the strip is defined by
     * replacing the oldest vertex of the previous triangle in the strip.
     * Equivalent to REPLACE_MIDDLE for line strips.
     */
    static final int REPLACE_OLDEST = 3 ;

    /**
     * This flag indicates that a vertex is to be pushed into the mesh buffer.
     */
    static final int MESH_PUSH = 1 ;

    /**
     * This flag indicates that a vertex does not use the mesh buffer.
     */
    static final int NO_MESH_PUSH = 0 ;

    /**
     * Byte to float scale factor for scaling byte color components.
     */
    static final float ByteToFloatScale = 1.0f/255.0f;

    /**
     * Type of this stream, either CompressedGeometryData.Header.POINT_BUFFER,
     * CompressedGeometryData.Header.LINE_BUFFER, or
     * CompressedGeometryData.Header.TRIANGLE_BUFFER
     */
    int streamType ;

    /**
     * A mask indicating which components are present in each vertex, as
     * defined by GeometryArray.
     */
    int vertexComponents ;

    /**
     * Boolean indicating colors are bundled with the vertices.
     */
    boolean vertexColors ;

    /**
     * Boolean indicating RGB colors are bundled with the vertices.
     */
    boolean vertexColor3 ;

    /**
     * Boolean indicating RGBA colors are bundled with the vertices.
     */
    boolean vertexColor4 ;

    /**
     * Boolean indicating normals are bundled with the vertices.
     */
    boolean vertexNormals ;

    /**
     * Boolean indicating texture coordinates are present.
     */
    boolean vertexTextures ;

    /**
     * Boolean indicating that 2D texture coordinates are used.
     * Currently only used to skip over textures in interleaved data.
     */
    boolean vertexTexture2 ;

    /**
     * Boolean indicating that 3D texture coordinates are used.
     * Currently only used to skip over textures in interleaved data.
     */
    boolean vertexTexture3 ;

    /**
     * Boolean indicating that 4D texture coordinates are used.
     * Currently only used to skip over textures in interleaved data.
     */
    boolean vertexTexture4 ;

    /**
     * Axes-aligned box enclosing all vertices in model coordinates.
     */
    Point3d mcBounds[] = new Point3d[2] ;

    /**
     * Axes-aligned box enclosing all vertices in normalized coordinates.
     */
    Point3d ncBounds[] = new Point3d[2] ;

    /**
     * Axes-aligned box enclosing all vertices in quantized coordinates.
     */
    Point3i qcBounds[] = new Point3i[2] ;

    /**
     * Center for normalizing positions to the unit cube.
     */
    double center[] = new double[3] ;

    /**
     * Maximum position range along the 3 axes.
     */
    double positionRangeMaximum ;

    /**
     * Scale for normalizing positions to the unit cube.
     */
    double scale ;

    /**
     * Current position component (X, Y, and Z) quantization value.  This can
     * range from 1 to 16 bits and has a default of 16.

* * At 1 bit of quantization it is not possible to express positive * absolute or delta positions. */ int positionQuant ; /** * Current color component (R, G, B, A) quantization value. This can * range from 2 to 16 bits and has a default of 9.

* * A color component is represented with a signed fixed-point value in * order to be able express negative deltas; the default of 9 bits * corresponds to the 8-bit color component range of the graphics hardware * commonly available. Colors must be non-negative, so the lower limit of * quantization is 2 bits. */ int colorQuant ; /** * Current normal component (U and V) quantization value. This can range * from 0 to 6 bits and has a default of 6.

* * At 0 bits of quantization normals are represented only as 6 bit * sextant/octant pairs and 14 specially encoded normals (the 6 axis * normals and the 8 octant midpoint normals); since U and V can only be 0 * at the minimum quantization, the totally number of unique normals is * 12 + 14 = 26. */ int normalQuant ; /** * Flag indicating position quantization change. */ boolean positionQuantChanged ; /** * Flag indicating color quantization change. */ boolean colorQuantChanged ; /** * Flag indicating normal quantization change. */ boolean normalQuantChanged ; /** * Last quantized position. */ int lastPosition[] = new int[3] ; /** * Last quantized color. */ int lastColor[] = new int[4] ; /** * Last quantized normal's sextant. */ int lastSextant ; /** * Last quantized normal's octant. */ int lastOctant ; /** * Last quantized normal's U encoding parameter. */ int lastU ; /** * Last quantized normal's V encoding parameter. */ int lastV ; /** * Flag indicating last normal used a special encoding. */ boolean lastSpecialNormal ; /** * Flag indicating the first position in this stream. */ boolean firstPosition ; /** * Flag indicating the first color in this stream. */ boolean firstColor ; /** * Flag indicating the first normal in this stream. */ boolean firstNormal ; /** * The total number of bytes used to create the uncompressed geometric * elements in this stream, useful for performance analysis. This * excludes mesh buffer references. */ int byteCount ; /** * The number of vertices created for this stream, excluding mesh buffer * references. */ int vertexCount ; /** * The number of mesh buffer references created for this stream. */ int meshReferenceCount ; /** * Mesh buffer mirror used for computing deltas during quantization pass * and a limited meshing algorithm for unstripped data. */ MeshBuffer meshBuffer = new MeshBuffer() ; // Collection which holds the elements of this stream. private Collection stream ; // True if preceding stream elements were colors or normals. Used to flag // color and normal mesh buffer substitution when computing deltas during // quantization pass. private boolean lastElementColor = false ; private boolean lastLastElementColor = false ; private boolean lastElementNormal = false ; private boolean lastLastElementNormal = false ; // Some convenient temporary holding variables. private Point3f p3f = new Point3f() ; private Color3f c3f = new Color3f() ; private Color4f c4f = new Color4f() ; private Vector3f n3f = new Vector3f() ; // Private constructor for common initializations. private CompressionStream() { this.stream = new LinkedList() ; byteCount = 0 ; vertexCount = 0 ; meshReferenceCount = 0 ; mcBounds[0] = new Point3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY) ; mcBounds[1] = new Point3d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY) ; qcBounds[0] = new Point3i(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE) ; qcBounds[1] = new Point3i(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE) ; /* normalized bounds computed from quantized bounds */ ncBounds[0] = new Point3d() ; ncBounds[1] = new Point3d() ; } /** * Creates a new CompressionStream for the specified geometry type and * vertex format.

* * @param streamType type of data in this stream, either * CompressedGeometryData.Header.POINT_BUFFER, * CompressedGeometryData.Header.LINE_BUFFER, or * CompressedGeometryData.Header.TRIANGLE_BUFFER * @param vertexComponents a mask indicating which components are present * in each vertex, as defined by GeometryArray: COORDINATES, NORMALS, and * COLOR_3 or COLOR_4. * @see GeometryCompressor * @see GeometryArray */ CompressionStream(int streamType, int vertexComponents) { this() ; this.streamType = streamType ; this.vertexComponents = getVertexComponents(vertexComponents) ; } // See what vertex geometry components are present. The byReference, // interleaved, useNIOBuffer, and useCoordIndexOnly flags are not // examined. private int getVertexComponents(int vertexFormat) { int components = 0 ; vertexColors = vertexColor3 = vertexColor4 = vertexNormals = vertexTextures = vertexTexture2 = vertexTexture3 = vertexTexture4 = false ; if ((vertexFormat & GeometryArray.NORMALS) != 0) { vertexNormals = true ; components &= GeometryArray.NORMALS ; if (debug) System.out.println("vertexNormals") ; } if ((vertexFormat & GeometryArray.COLOR_3) != 0) { vertexColors = true ; if ((vertexFormat & GeometryArray.COLOR_4) != 0) { vertexColor4 = true ; components &= GeometryArray.COLOR_4 ; if (debug) System.out.println("vertexColor4") ; } else { vertexColor3 = true ; components &= GeometryArray.COLOR_3 ; if (debug) System.out.println("vertexColor3") ; } } if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) { vertexTextures = true ; vertexTexture2 = true ; components &= GeometryArray.TEXTURE_COORDINATE_2 ; if (debug) System.out.println("vertexTexture2") ; } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) { vertexTextures = true ; vertexTexture3 = true ; components &= GeometryArray.TEXTURE_COORDINATE_3 ; if (debug) System.out.println("vertexTexture3") ; } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) { vertexTextures = true ; vertexTexture4 = true ; components &= GeometryArray.TEXTURE_COORDINATE_4 ; if (debug) System.out.println("vertexTexture4") ; } if (vertexTextures) // Throw exception for now until texture is supported. throw new UnsupportedOperationException ("\ncompression of texture coordinates is not supported") ; return components ; } // Get the streamType associated with a GeometryArray instance. private int getStreamType(GeometryArray ga) { if (ga instanceof TriangleStripArray || ga instanceof IndexedTriangleStripArray || ga instanceof TriangleFanArray || ga instanceof IndexedTriangleFanArray || ga instanceof TriangleArray || ga instanceof IndexedTriangleArray || ga instanceof QuadArray || ga instanceof IndexedQuadArray) return CompressedGeometryData.Header.TRIANGLE_BUFFER ; else if (ga instanceof LineArray || ga instanceof IndexedLineArray || ga instanceof LineStripArray || ga instanceof IndexedLineStripArray) return CompressedGeometryData.Header.LINE_BUFFER ; else return CompressedGeometryData.Header.POINT_BUFFER ; } /** * Iterates across all compression stream elements and applies * quantization parameters, encoding consecutive vertices as delta values * whenever possible. Each geometric element is mapped to a HuffmanNode * object containing its resulting bit length, right shift (trailing 0 * count), and absolute or relative status.

* * Positions are normalized to span a unit cube via an offset and a * uniform scale factor that maps the midpoint of the object extents along * each dimension to the origin, and the longest dimension of the object to * the open interval (-1.0 .. +1.0). The geometric endpoints along that * dimension are both one quantum away from unity; for example, at a * position quantization of 6 bits, an object would be normalized so that * its most negative dimension is at (-1 + 1/64) and the most positive is * at (1 - 1/64).

* * Normals are assumed to be of unit length. Color components are clamped * to the [0..1) range, where the right endpoint is one quantum less * than 1.0.

* * @param huffmanTable Table which will map geometric compression stream * elements to HuffmanNode objects describing each element's data * representation. This table can then be processed with Huffman's * algorithm to optimize the bit length of descriptor tags according to * the number of geometric elements mapped to each tag. */ void quantize(HuffmanTable huffmanTable) { // Set up default initial quantization parameters. The position and // color parameters specify the number of bits for each X, Y, Z, R, G, // B, or A component. The normal quantization parameter specifies the // number of bits for each U and V component. positionQuant = 16 ; colorQuant = 9 ; normalQuant = 6 ; // Compute position center and scaling for normalization to the unit // cube. This is a volume bounded by the open intervals (-1..1) on // each axis. center[0] = (mcBounds[1].x + mcBounds[0].x) / 2.0 ; center[1] = (mcBounds[1].y + mcBounds[0].y) / 2.0 ; center[2] = (mcBounds[1].z + mcBounds[0].z) / 2.0 ; double xRange = mcBounds[1].x - mcBounds[0].x ; double yRange = mcBounds[1].y - mcBounds[0].y ; double zRange = mcBounds[1].z - mcBounds[0].z ; if (xRange > yRange) positionRangeMaximum = xRange ; else positionRangeMaximum = yRange ; if (zRange > positionRangeMaximum) positionRangeMaximum = zRange ; // Adjust the range of the unit cube to match the default // quantization. // // This scale factor along with the center values computed above will // produce 16-bit integer representations of the floating point // position coordinates ranging symmetrically about 0 from -32767 to // +32767. -32768 is not used and the normalized floating point // position coordinates of -1.0 as well as +1.0 will not be // represented. // // Applications which wish to seamlessly stitch together compressed // objects will need to be aware that the range of normalized // positions will be one quantum away from the [-1..1] endpoints of // the unit cube and should adjust scale factors accordingly. scale = (2.0 / positionRangeMaximum) * (32767.0 / 32768.0) ; // Flag quantization change. positionQuantChanged = colorQuantChanged = normalQuantChanged = true ; // Flag first position, color, and normal. firstPosition = firstColor = firstNormal = true ; // Apply quantization. Iterator i = stream.iterator() ; while (i.hasNext()) { Object o = i.next() ; if (o instanceof CompressionStreamElement) { ((CompressionStreamElement)o).quantize(this, huffmanTable) ; // Keep track of whether last two elements were colors or // normals for mesh buffer component substitution semantics. lastLastElementColor = lastElementColor ; lastLastElementNormal = lastElementNormal ; lastElementColor = lastElementNormal = false ; if (o instanceof CompressionStreamColor) lastElementColor = true ; else if (o instanceof CompressionStreamNormal) lastElementNormal = true ; } } // Compute the bounds in normalized coordinates. ncBounds[0].x = (double)qcBounds[0].x / 32768.0 ; ncBounds[0].y = (double)qcBounds[0].y / 32768.0 ; ncBounds[0].z = (double)qcBounds[0].z / 32768.0 ; ncBounds[1].x = (double)qcBounds[1].x / 32768.0 ; ncBounds[1].y = (double)qcBounds[1].y / 32768.0 ; ncBounds[1].z = (double)qcBounds[1].z / 32768.0 ; } /** * Iterates across all compression stream elements and builds the * compressed geometry command stream output.

* * @param huffmanTable Table which maps geometric elements in this stream * to tags describing the encoding parameters (length, shift, and * absolute/relative status) to be used for their representations in the * compressed output. All tags must be 6 bits or less in length, and the * sum of the number of bits in the tag plus the number of bits in the * data it describes must be at least 6 bits in length. * * @param outputBuffer CommandStream to use for collecting the compressed * bits. */ void outputCommands(HuffmanTable huffmanTable, CommandStream outputBuffer) { // // The first command output is setState to indicate what data is // bundled with each vertex. Although the semantics of geometry // decompression allow setState to appear anywhere in the stream, this // cannot be handled by the current Java 3D software decompressor, // which internally decompresses an entire compressed buffer into a // single retained object sharing a single consistent vertex format. // This limitation may be removed in subsequent releases of Java 3D. // int bnv = (vertexNormals? 1 : 0) ; int bcv = ((vertexColor3 || vertexColor4)? 1 : 0) ; int cap = (vertexColor4? 1 : 0) ; int command = CommandStream.SET_STATE | bnv ; long data = (bcv << 2) | (cap << 1) ; // Output the setState command. outputBuffer.addCommand(command, 8, data, 3) ; // Output the Huffman table commands. huffmanTable.outputCommands(outputBuffer) ; // Output each compression stream element's data. Iterator i = stream.iterator() ; while (i.hasNext()) { Object o = i.next() ; if (o instanceof CompressionStreamElement) ((CompressionStreamElement)o).outputCommand(huffmanTable, outputBuffer) ; } // Finish the header-forwarding interleave and long-word align. outputBuffer.end() ; } /** * Retrieve the total size of the uncompressed geometric data in bytes, * excluding mesh buffer references. * @return uncompressed byte count */ int getByteCount() { return byteCount ; } /** * Retrieve the the number of vertices created for this stream, excluding * mesh buffer references. * @return vertex count */ int getVertexCount() { return vertexCount ; } /** * Retrieve the number of mesh buffer references created for this stream. * @return mesh buffer reference count */ int getMeshReferenceCount() { return meshReferenceCount ; } /** * Stream element that sets position quantization during quantize pass. */ private class PositionQuant extends CompressionStreamElement { int value ; PositionQuant(int value) { this.value = value ; } @Override void quantize(CompressionStream s, HuffmanTable t) { positionQuant = value ; positionQuantChanged = true ; // Adjust range of unit cube scaling to match quantization. scale = (2.0 / positionRangeMaximum) * (((double)((1 << (value-1)) - 1))/((double)(1 << (value-1)))) ; } @Override public String toString() { return "positionQuant: " + value ; } } /** * Stream element that sets normal quantization during quantize pass. */ private class NormalQuant extends CompressionStreamElement { int value ; NormalQuant(int value) { this.value = value ; } @Override void quantize(CompressionStream s, HuffmanTable t) { normalQuant = value ; normalQuantChanged = true ; } @Override public String toString() { return "normalQuant: " + value ; } } /** * Stream element that sets color quantization during quantize pass. */ private class ColorQuant extends CompressionStreamElement { int value ; ColorQuant(int value) { this.value = value ; } @Override void quantize(CompressionStream s, HuffmanTable t) { colorQuant = value ; colorQuantChanged = true ; } @Override public String toString() { return "colorQuant: " + value ; } } /** * Stream element that references the mesh buffer. */ private class MeshReference extends CompressionStreamElement { int stripFlag, meshIndex ; MeshReference(int stripFlag, int meshIndex) { this.stripFlag = stripFlag ; this.meshIndex = meshIndex ; meshReferenceCount++ ; } @Override void quantize(CompressionStream s, HuffmanTable t) { // Retrieve the vertex from the mesh buffer mirror and set up the // data needed for the next stream element to compute its deltas. CompressionStreamVertex v = meshBuffer.getVertex(meshIndex) ; lastPosition[0] = v.xAbsolute ; lastPosition[1] = v.yAbsolute ; lastPosition[2] = v.zAbsolute ; // Set up last color data if it exists and previous elements // don't override it. if (v.color != null && !lastElementColor && !(lastElementNormal && lastLastElementColor)) { lastColor[0] = v.color.rAbsolute ; lastColor[1] = v.color.gAbsolute ; lastColor[2] = v.color.bAbsolute ; lastColor[3] = v.color.aAbsolute ; } // Set up last normal data if it exists and previous element // doesn't override it. if (v.normal != null && !lastElementNormal && !(lastElementColor && lastLastElementNormal)) { lastSextant = v.normal.sextant ; lastOctant = v.normal.octant ; lastU = v.normal.uAbsolute ; lastV = v.normal.vAbsolute ; lastSpecialNormal = v.normal.specialNormal ; } } @Override void outputCommand(HuffmanTable t, CommandStream outputBuffer) { int command = CommandStream.MESH_B_R ; long data = stripFlag & 0x1 ; command |= (((meshIndex & 0xf) << 1) | (stripFlag >> 1)) ; outputBuffer.addCommand(command, 8, data, 1) ; } @Override public String toString() { return "meshReference: stripFlag " + stripFlag + " meshIndex " + meshIndex ; } } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param stripFlag vertex replacement flag, either RESTART, * REPLACE_OLDEST, or REPLACE_MIDDLE */ void addVertex(Point3f pos, int stripFlag) { stream.add(new CompressionStreamVertex(this, pos, (Vector3f)null, (Color3f)null, stripFlag, NO_MESH_PUSH)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param norm normal data * @param stripFlag vertex replacement flag, either RESTART, * REPLACE_OLDEST, or REPLACE_MIDDLE */ void addVertex(Point3f pos, Vector3f norm, int stripFlag) { stream.add(new CompressionStreamVertex (this, pos, norm, (Color3f)null, stripFlag, NO_MESH_PUSH)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param color color data * @param stripFlag vertex replacement flag, either RESTART, * REPLACE_OLDEST, or REPLACE_MIDDLE */ void addVertex(Point3f pos, Color3f color, int stripFlag) { stream.add(new CompressionStreamVertex (this, pos, (Vector3f)null, color, stripFlag, NO_MESH_PUSH)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param color color data * @param stripFlag vertex replacement flag, either RESTART, * REPLACE_OLDEST, or REPLACE_MIDDLE */ void addVertex(Point3f pos, Color4f color, int stripFlag) { stream.add(new CompressionStreamVertex (this, pos, (Vector3f)null, color, stripFlag, NO_MESH_PUSH)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param norm normal data * @param color color data * @param stripFlag vertex replacement flag, either RESTART, * REPLACE_OLDEST, or REPLACE_MIDDLE */ void addVertex(Point3f pos, Vector3f norm, Color3f color, int stripFlag) { stream.add(new CompressionStreamVertex (this, pos, norm, color, stripFlag, NO_MESH_PUSH)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param norm normal data * @param color color data * @param stripFlag vertex replacement flag, either RESTART, * REPLACE_OLDEST, or REPLACE_MIDDLE */ void addVertex(Point3f pos, Vector3f norm, Color4f color, int stripFlag) { stream.add(new CompressionStreamVertex (this, pos, norm, color, stripFlag, NO_MESH_PUSH)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST, * or REPLACE_MIDDLE * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer */ void addVertex(Point3f pos, int stripFlag, int meshFlag) { stream.add(new CompressionStreamVertex (this, pos, (Vector3f)null, (Color3f)null, stripFlag, meshFlag)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param norm normal data * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST, * or REPLACE_MIDDLE * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer */ void addVertex(Point3f pos, Vector3f norm, int stripFlag, int meshFlag) { stream.add(new CompressionStreamVertex (this, pos, norm, (Color3f)null, stripFlag, meshFlag)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param color color data * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST, * or REPLACE_MIDDLE * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer */ void addVertex(Point3f pos, Color3f color, int stripFlag, int meshFlag) { stream.add(new CompressionStreamVertex (this, pos, (Vector3f)null, color, stripFlag, meshFlag)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param color color data * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST, * or REPLACE_MIDDLE * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer */ void addVertex(Point3f pos, Color4f color, int stripFlag, int meshFlag) { stream.add(new CompressionStreamVertex (this, pos, (Vector3f)null, color, stripFlag, meshFlag)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param norm normal data * @param color color data * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST, * or REPLACE_MIDDLE * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer */ void addVertex(Point3f pos, Vector3f norm, Color3f color, int stripFlag, int meshFlag) { stream.add(new CompressionStreamVertex (this, pos, norm, color, stripFlag, meshFlag)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param norm normal data * @param color color data * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST, * or REPLACE_MIDDLE * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer */ void addVertex(Point3f pos, Vector3f norm, Color4f color, int stripFlag, int meshFlag) { stream.add(new CompressionStreamVertex (this, pos, norm, color, stripFlag, meshFlag)) ; } /** * Copy vertex data and add it to the end of this stream. * @param pos position data * @param norm normal data * @param color color data, either Color3f or Color4f, determined by * current vertex format * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST, * or REPLACE_MIDDLE * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer */ void addVertex(Point3f pos, Vector3f norm, Object color, int stripFlag, int meshFlag) { if (vertexColor3) stream.add(new CompressionStreamVertex (this, pos, norm, (Color3f)color, stripFlag, meshFlag)) ; else stream.add(new CompressionStreamVertex (this, pos, norm, (Color4f)color, stripFlag, meshFlag)) ; } /** * Add a mesh buffer reference to this stream. * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST, * or REPLACE_MIDDLE * @param meshIndex index of vertex to retrieve from the mesh buffer */ void addMeshReference(int stripFlag, int meshIndex) { stream.add(new MeshReference(stripFlag, meshIndex)) ; } /** * Copy the given color to the end of this stream and use it as a global * state change that applies to all subsequent vertices. */ void addColor(Color3f c3f) { stream.add(new CompressionStreamColor(this, c3f)) ; } /** * Copy the given color to the end of this stream and use it as a global * state change that applies to all subsequent vertices. */ void addColor(Color4f c4f) { stream.add(new CompressionStreamColor(this, c4f)) ; } /** * Copy the given normal to the end of this stream and use it as a global * state change that applies to all subsequent vertices. */ void addNormal(Vector3f n) { stream.add(new CompressionStreamNormal(this, n)) ; } /** * Add a new position quantization value to the end of this stream that * will apply to all subsequent vertex positions. * * @param value number of bits to quantize each position's X, Y, * and Z components, ranging from 1 to 16 with a default of 16 */ void addPositionQuantization(int value) { stream.add(new PositionQuant(value)) ; } /** * Add a new color quantization value to the end of this stream that will * apply to all subsequent colors. * * @param value number of bits to quantize each color's R, G, B, and * alpha components, ranging from 2 to 16 with a default of 9 */ void addColorQuantization(int value) { stream.add(new ColorQuant(value)) ; } /** * Add a new normal quantization value to the end of this stream that will * apply to all subsequent normals. This value specifies the number of * bits for each normal's U and V components. * * @param value number of bits for quantizing U and V, ranging from 0 to * 6 with a default of 6 */ void addNormalQuantization(int value) { stream.add(new NormalQuant(value)) ; } /** * Interface to access GeometryArray vertex components and add them to the * compression stream. * * A processVertex() implementation retrieves vertex components using the * appropriate access semantics of a particular GeometryArray, and adds * them to the compression stream. * * The implementation always pushes vertices into the mesh buffer unless * they match ones already there; if they do, it generates mesh buffer * references instead. This reduces the number of vertices when * non-stripped abutting facets are added to the stream. * * Note: Level II geometry compression semantics allow the mesh buffer * normals to be substituted with the value of an immediately * preceding SetNormal command, but this is unavailable in Level I. * * @param index vertex offset from the beginning of its data array * @param stripFlag RESTART, REPLACE_MIDDLE, or REPLACE_OLDEST */ private interface GeometryAccessor { void processVertex(int index, int stripFlag) ; } /** * This class implements the GeometryAccessor interface for geometry * arrays accessed with by-copy semantics. */ private class ByCopyGeometry implements GeometryAccessor { Point3f[] positions = null ; Vector3f[] normals = null ; Color3f[] colors3 = null ; Color4f[] colors4 = null ; ByCopyGeometry(GeometryArray ga) { this(ga, ga.getInitialVertexIndex(), ga.getValidVertexCount()) ; } ByCopyGeometry(GeometryArray ga, int firstVertex, int validVertexCount) { int i ; positions = new Point3f[validVertexCount] ; for (i = 0 ; i < validVertexCount ; i++) positions[i] = new Point3f() ; ga.getCoordinates(firstVertex, positions) ; if (vertexNormals) { normals = new Vector3f[validVertexCount] ; for (i = 0 ; i < validVertexCount ; i++) normals[i] = new Vector3f() ; ga.getNormals(firstVertex, normals) ; } if (vertexColor3) { colors3 = new Color3f[validVertexCount] ; for (i = 0 ; i < validVertexCount ; i++) colors3[i] = new Color3f() ; ga.getColors(firstVertex, colors3) ; } else if (vertexColor4) { colors4 = new Color4f[validVertexCount] ; for (i = 0 ; i < validVertexCount ; i++) colors4[i] = new Color4f() ; ga.getColors(firstVertex, colors4) ; } } @Override public void processVertex(int v, int stripFlag) { Point3f p = positions[v] ; int r = meshBuffer.getMeshReference(p) ; if ((r == meshBuffer.NOT_FOUND) || (vertexNormals && noMeshNormalSubstitution && (! normals[v].equals(meshBuffer.getNormal(r))))) { Vector3f n = vertexNormals? normals[v] : null ; Object c = vertexColor3? (Object)colors3[v] : vertexColor4? (Object)colors4[v] : null ; addVertex(p, n, c, stripFlag, MESH_PUSH) ; meshBuffer.push(p, c, n) ; } else { if (vertexNormals && !noMeshNormalSubstitution && (! normals[v].equals(meshBuffer.getNormal(r)))) addNormal(normals[v]) ; if (vertexColor3 && (! colors3[v].equals(meshBuffer.getColor3(r)))) addColor(colors3[v]) ; else if (vertexColor4 && (! colors4[v].equals(meshBuffer.getColor4(r)))) addColor(colors4[v]) ; addMeshReference(stripFlag, r) ; } } } /** * Class which holds index array references for a geometry array. */ private static class IndexArrays { int colorIndices[] = null ; int normalIndices[] = null ; int positionIndices[] = null ; } /** * Retrieves index array references for the specified IndexedGeometryArray. * Index arrays are copied starting from initialIndexIndex. */ private void getIndexArrays(GeometryArray ga, IndexArrays ia) { IndexedGeometryArray iga = (IndexedGeometryArray)ga ; int initialIndexIndex = iga.getInitialIndexIndex() ; int indexCount = iga.getValidIndexCount() ; int vertexFormat = iga.getVertexFormat() ; boolean useCoordIndexOnly = false ; if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) { if (debug) System.out.println("useCoordIndexOnly") ; useCoordIndexOnly = true ; } ia.positionIndices = new int[indexCount] ; iga.getCoordinateIndices(initialIndexIndex, ia.positionIndices) ; if (vertexNormals) { if (useCoordIndexOnly) { ia.normalIndices = ia.positionIndices ; } else { ia.normalIndices = new int[indexCount] ; iga.getNormalIndices(initialIndexIndex, ia.normalIndices) ; } } if (vertexColor3 || vertexColor4) { if (useCoordIndexOnly) { ia.colorIndices = ia.positionIndices ; } else { ia.colorIndices = new int[indexCount] ; iga.getColorIndices(initialIndexIndex, ia.colorIndices) ; } } } /** * Class which holds indices for a specific vertex of an * IndexedGeometryArray. */ private static class VertexIndices { int pi, ni, ci ; } /** * Retrieves vertex indices for a specific vertex in an * IndexedGeometryArray. */ private void getVertexIndices(int v, IndexArrays ia, VertexIndices vi) { vi.pi = ia.positionIndices[v] ; if (vertexNormals) vi.ni = ia.normalIndices[v] ; if (vertexColors) vi.ci = ia.colorIndices[v] ; } /** * This class implements the GeometryAccessor interface for indexed * geometry arrays accessed with by-copy semantics. */ private class IndexedByCopyGeometry extends ByCopyGeometry { IndexArrays ia = new IndexArrays() ; VertexIndices vi = new VertexIndices() ; IndexedByCopyGeometry(GeometryArray ga) { super(ga, 0, ga.getVertexCount()) ; getIndexArrays(ga, ia) ; } @Override public void processVertex(int v, int stripFlag) { getVertexIndices(v, ia, vi) ; int r = meshBuffer.getMeshReference(vi.pi) ; if ((r == meshBuffer.NOT_FOUND) || (vertexNormals && noMeshNormalSubstitution && (vi.ni != meshBuffer.getNormalIndex(r)))) { Point3f p = positions[vi.pi] ; Vector3f n = vertexNormals? normals[vi.ni] : null ; Object c = vertexColor3? (Object)colors3[vi.ci] : vertexColor4? (Object)colors4[vi.ci] : null ; addVertex(p, n, c, stripFlag, MESH_PUSH) ; meshBuffer.push(vi.pi, vi.ci, vi.ni) ; } else { if (vertexNormals && !noMeshNormalSubstitution && vi.ni != meshBuffer.getNormalIndex(r)) addNormal(normals[vi.ni]) ; if (vertexColor3 && vi.ci != meshBuffer.getColorIndex(r)) addColor(colors3[vi.ci]) ; else if (vertexColor4 && vi.ci != meshBuffer.getColorIndex(r)) addColor(colors4[vi.ci]) ; addMeshReference(stripFlag, r) ; } } } // // NOTE: For now, copies are made of all GeometryArray vertex components // even when by-reference access is available. // private static class VertexCopy { Object c = null ; Point3f p = null ; Vector3f n = null ; Color3f c3 = null ; Color4f c4 = null ; } private void processVertexCopy(VertexCopy vc, int stripFlag) { int r = meshBuffer.getMeshReference(vc.p) ; if ((r == meshBuffer.NOT_FOUND) || (vertexNormals && noMeshNormalSubstitution && (! vc.n.equals(meshBuffer.getNormal(r))))) { addVertex(vc.p, vc.n, vc.c, stripFlag, MESH_PUSH) ; meshBuffer.push(vc.p, vc.c, vc.n) ; } else { if (vertexNormals && !noMeshNormalSubstitution && (! vc.n.equals(meshBuffer.getNormal(r)))) addNormal(vc.n) ; if (vertexColor3 && (! vc.c3.equals(meshBuffer.getColor3(r)))) addColor(vc.c3) ; else if (vertexColor4 && (! vc.c4.equals(meshBuffer.getColor4(r)))) addColor(vc.c4) ; addMeshReference(stripFlag, r) ; } } private void processIndexedVertexCopy(VertexCopy vc, VertexIndices vi, int stripFlag) { int r = meshBuffer.getMeshReference(vi.pi) ; if ((r == meshBuffer.NOT_FOUND) || (vertexNormals && noMeshNormalSubstitution && (vi.ni != meshBuffer.getNormalIndex(r)))) { addVertex(vc.p, vc.n, vc.c, stripFlag, MESH_PUSH) ; meshBuffer.push(vi.pi, vi.ci, vi.ni) ; } else { if (vertexNormals && !noMeshNormalSubstitution && vi.ni != meshBuffer.getNormalIndex(r)) addNormal(vc.n) ; if (vertexColor3 && vi.ci != meshBuffer.getColorIndex(r)) addColor(vc.c3) ; else if (vertexColor4 && vi.ci != meshBuffer.getColorIndex(r)) addColor(vc.c4) ; addMeshReference(stripFlag, r) ; } } /** * This abstract class implements the GeometryAccessor interface for * concrete subclasses which handle float and NIO interleaved geometry * arrays. */ private abstract class InterleavedGeometry implements GeometryAccessor { VertexCopy vc = new VertexCopy() ; int vstride = 0 ; int coffset = 0 ; int noffset = 0 ; int poffset = 0 ; int tstride = 0 ; int tcount = 0 ; InterleavedGeometry(GeometryArray ga) { if (vertexTextures) { if (vertexTexture2) tstride = 2 ; else if (vertexTexture3) tstride = 3 ; else if (vertexTexture4) tstride = 4 ; tcount = ga.getTexCoordSetCount() ; vstride += tcount * tstride ; } if (vertexColors) { coffset = vstride ; if (vertexColor3) vstride += 3 ; else vstride += 4 ; } if (vertexNormals) { noffset = vstride ; vstride += 3 ; } poffset = vstride ; vstride += 3 ; } abstract void copyVertex(int pi, int ni, int ci, VertexCopy vc) ; @Override public void processVertex(int v, int stripFlag) { copyVertex(v, v, v, vc) ; processVertexCopy(vc, stripFlag) ; } } /** * This class implements the GeometryAccessor interface for float * interleaved geometry arrays. */ private class InterleavedGeometryFloat extends InterleavedGeometry { float[] vdata = null ; InterleavedGeometryFloat(GeometryArray ga) { super(ga) ; vdata = ga.getInterleavedVertices() ; } @Override void copyVertex(int pi, int ni, int ci, VertexCopy vc) { int voffset ; voffset = pi * vstride ; vc.p = new Point3f(vdata[voffset + poffset + 0], vdata[voffset + poffset + 1], vdata[voffset + poffset + 2]) ; if (vertexNormals) { voffset = ni * vstride ; vc.n = new Vector3f(vdata[voffset + noffset + 0], vdata[voffset + noffset + 1], vdata[voffset + noffset + 2]) ; } if (vertexColor3) { voffset = ci * vstride ; vc.c3 = new Color3f(vdata[voffset + coffset + 0], vdata[voffset + coffset + 1], vdata[voffset + coffset + 2]) ; vc.c = vc.c3 ; } else if (vertexColor4) { voffset = ci * vstride ; vc.c4 = new Color4f(vdata[voffset + coffset + 0], vdata[voffset + coffset + 1], vdata[voffset + coffset + 2], vdata[voffset + coffset + 3]) ; vc.c = vc.c4 ; } } } /** * This class implements the GeometryAccessor interface for indexed * interleaved geometry arrays. */ private class IndexedInterleavedGeometryFloat extends InterleavedGeometryFloat { IndexArrays ia = new IndexArrays() ; VertexIndices vi = new VertexIndices() ; IndexedInterleavedGeometryFloat(GeometryArray ga) { super(ga) ; getIndexArrays(ga, ia) ; } @Override public void processVertex(int v, int stripFlag) { getVertexIndices(v, ia, vi) ; copyVertex(vi.pi, vi.ni, vi.ci, vc) ; processIndexedVertexCopy(vc, vi, stripFlag) ; } } /** * This class implements the GeometryAccessor interface for * interleaved NIO geometry arrays. */ private class InterleavedGeometryNIO extends InterleavedGeometry { FloatBuffer fbw = null ; InterleavedGeometryNIO(GeometryArray ga) { super(ga) ; J3DBuffer buffer = ga.getInterleavedVertexBuffer() ; if (BufferWrapper.getBufferType(buffer) == BufferWrapper.TYPE_FLOAT) { fbw = (FloatBuffer)buffer.getBuffer(); } else { throw new IllegalArgumentException ("\ninterleaved vertex buffer must be FloatBuffer") ; } } @Override void copyVertex(int pi, int ni, int ci, VertexCopy vc) { int voffset ; voffset = pi * vstride ; vc.p = new Point3f(fbw.get(voffset + poffset + 0), fbw.get(voffset + poffset + 1), fbw.get(voffset + poffset + 2)) ; if (vertexNormals) { voffset = ni * vstride ; vc.n = new Vector3f(fbw.get(voffset + noffset + 0), fbw.get(voffset + noffset + 1), fbw.get(voffset + noffset + 2)) ; } if (vertexColor3) { voffset = ci * vstride ; vc.c3 = new Color3f(fbw.get(voffset + coffset + 0), fbw.get(voffset + coffset + 1), fbw.get(voffset + coffset + 2)) ; vc.c = vc.c3 ; } else if (vertexColor4) { voffset = ci * vstride ; vc.c4 = new Color4f(fbw.get(voffset + coffset + 0), fbw.get(voffset + coffset + 1), fbw.get(voffset + coffset + 2), fbw.get(voffset + coffset + 3)) ; vc.c = vc.c4 ; } } } /** * This class implements the GeometryAccessor interface for indexed * interleaved NIO geometry arrays. */ private class IndexedInterleavedGeometryNIO extends InterleavedGeometryNIO { IndexArrays ia = new IndexArrays() ; VertexIndices vi = new VertexIndices() ; IndexedInterleavedGeometryNIO(GeometryArray ga) { super(ga) ; getIndexArrays(ga, ia) ; } @Override public void processVertex(int v, int stripFlag) { getVertexIndices(v, ia, vi) ; copyVertex(vi.pi, vi.ni, vi.ci, vc) ; processIndexedVertexCopy(vc, vi, stripFlag) ; } } /** * This class implements the GeometryAccessor interface for * non-interleaved geometry arrays accessed with by-reference semantics. */ private class ByRefGeometry implements GeometryAccessor { VertexCopy vc = new VertexCopy() ; byte[] colorsB = null ; float[] colorsF = null ; float[] normals = null ; float[] positionsF = null ; double[] positionsD = null ; int initialPositionIndex = 0 ; int initialNormalIndex = 0 ; int initialColorIndex = 0 ; ByRefGeometry(GeometryArray ga) { positionsF = ga.getCoordRefFloat() ; if (debug && positionsF != null) System.out.println("float positions") ; positionsD = ga.getCoordRefDouble() ; if (debug && positionsD != null) System.out.println("double positions") ; if (positionsF == null && positionsD == null) throw new UnsupportedOperationException ("\nby-reference access to Point3{d,f} arrays") ; initialPositionIndex = ga.getInitialCoordIndex() ; if (vertexColors) { colorsB = ga.getColorRefByte() ; if (debug && colorsB != null) System.out.println("byte colors") ; colorsF = ga.getColorRefFloat() ; if (debug && colorsF != null) System.out.println("float colors") ; if (colorsB == null && colorsF == null) throw new UnsupportedOperationException ("\nby-reference access to Color{3b,3f,4b,4f} arrays") ; initialColorIndex = ga.getInitialColorIndex() ; } if (vertexNormals) { normals = ga.getNormalRefFloat() ; if (debug && normals != null) System.out.println("float normals") ; if (normals == null) throw new UnsupportedOperationException ("\nby-reference access to Normal3f array") ; initialNormalIndex = ga.getInitialNormalIndex() ; } } void copyVertex(int pi, int ni, int ci, VertexCopy vc) { pi *= 3 ; if (positionsF != null) { vc.p = new Point3f(positionsF[pi + 0], positionsF[pi + 1], positionsF[pi + 2]) ; } else { vc.p = new Point3f((float)positionsD[pi + 0], (float)positionsD[pi + 1], (float)positionsD[pi + 2]) ; } ni *= 3 ; if (vertexNormals) { vc.n = new Vector3f(normals[ni + 0], normals[ni + 1], normals[ni + 2]) ; } if (vertexColor3) { ci *= 3 ; if (colorsB != null) { vc.c3 = new Color3f ((colorsB[ci + 0] & 0xff) * ByteToFloatScale, (colorsB[ci + 1] & 0xff) * ByteToFloatScale, (colorsB[ci + 2] & 0xff) * ByteToFloatScale) ; } else { vc.c3 = new Color3f(colorsF[ci + 0], colorsF[ci + 1], colorsF[ci + 2]) ; } vc.c = vc.c3 ; } else if (vertexColor4) { ci *= 4 ; if (colorsB != null) { vc.c4 = new Color4f ((colorsB[ci + 0] & 0xff) * ByteToFloatScale, (colorsB[ci + 1] & 0xff) * ByteToFloatScale, (colorsB[ci + 2] & 0xff) * ByteToFloatScale, (colorsB[ci + 3] & 0xff) * ByteToFloatScale) ; } else { vc.c4 = new Color4f(colorsF[ci + 0], colorsF[ci + 1], colorsF[ci + 2], colorsF[ci + 3]) ; } vc.c = vc.c4 ; } } @Override public void processVertex(int v, int stripFlag) { copyVertex(v + initialPositionIndex, v + initialNormalIndex, v + initialColorIndex, vc) ; processVertexCopy(vc, stripFlag) ; } } /** * This class implements the GeometryAccessor interface for indexed * non-interleaved geometry arrays accessed with by-reference semantics. */ private class IndexedByRefGeometry extends ByRefGeometry { IndexArrays ia = new IndexArrays() ; VertexIndices vi = new VertexIndices() ; IndexedByRefGeometry(GeometryArray ga) { super(ga) ; getIndexArrays(ga, ia) ; } @Override public void processVertex(int v, int stripFlag) { getVertexIndices(v, ia, vi) ; copyVertex(vi.pi, vi.ni, vi.ci, vc) ; processIndexedVertexCopy(vc, vi, stripFlag) ; } } /** * This class implements the GeometryAccessor interface for * non-interleaved geometry arrays accessed with NIO. */ private class ByRefGeometryNIO implements GeometryAccessor { VertexCopy vc = new VertexCopy() ; ByteBuffer colorsB = null ; FloatBuffer colorsF = null ; FloatBuffer normals = null ; FloatBuffer positionsF = null ; DoubleBuffer positionsD = null ; int initialPositionIndex = 0 ; int initialNormalIndex = 0 ; int initialColorIndex = 0 ; ByRefGeometryNIO(GeometryArray ga) { J3DBuffer buffer ; buffer = ga.getCoordRefBuffer() ; initialPositionIndex = ga.getInitialCoordIndex() ; switch (BufferWrapper.getBufferType(buffer)) { case BufferWrapper.TYPE_FLOAT: positionsF = (FloatBuffer)buffer.getBuffer(); if (debug) System.out.println("float positions buffer") ; break ; case BufferWrapper.TYPE_DOUBLE: positionsD = (DoubleBuffer)buffer.getBuffer(); if (debug) System.out.println("double positions buffer") ; break ; default: throw new IllegalArgumentException ("\nposition buffer must be FloatBuffer or DoubleBuffer") ; } if (vertexColors) { buffer = ga.getColorRefBuffer() ; initialColorIndex = ga.getInitialColorIndex() ; switch (BufferWrapper.getBufferType(buffer)) { case BufferWrapper.TYPE_BYTE: colorsB = (ByteBuffer)buffer.getBuffer(); if (debug) System.out.println("byte colors buffer") ; break ; case BufferWrapper.TYPE_FLOAT: colorsF = (FloatBuffer)buffer.getBuffer(); if (debug) System.out.println("float colors buffer") ; break ; default: throw new IllegalArgumentException ("\ncolor buffer must be ByteBuffer or FloatBuffer") ; } } if (vertexNormals) { buffer = ga.getNormalRefBuffer() ; initialNormalIndex = ga.getInitialNormalIndex() ; switch (BufferWrapper.getBufferType(buffer)) { case BufferWrapper.TYPE_FLOAT: normals = (FloatBuffer)buffer.getBuffer(); if (debug) System.out.println("float normals buffer") ; break ; default: throw new IllegalArgumentException ("\nnormal buffer must be FloatBuffer") ; } } } void copyVertex(int pi, int ni, int ci, VertexCopy vc) { pi *= 3 ; if (positionsF != null) { vc.p = new Point3f(positionsF.get(pi + 0), positionsF.get(pi + 1), positionsF.get(pi + 2)) ; } else { vc.p = new Point3f((float)positionsD.get(pi + 0), (float)positionsD.get(pi + 1), (float)positionsD.get(pi + 2)) ; } ni *= 3 ; if (vertexNormals) { vc.n = new Vector3f(normals.get(ni + 0), normals.get(ni + 1), normals.get(ni + 2)) ; } if (vertexColor3) { ci *= 3 ; if (colorsB != null) { vc.c3 = new Color3f ((colorsB.get(ci + 0) & 0xff) * ByteToFloatScale, (colorsB.get(ci + 1) & 0xff) * ByteToFloatScale, (colorsB.get(ci + 2) & 0xff) * ByteToFloatScale) ; } else { vc.c3 = new Color3f(colorsF.get(ci + 0), colorsF.get(ci + 1), colorsF.get(ci + 2)) ; } vc.c = vc.c3 ; } else if (vertexColor4) { ci *= 4 ; if (colorsB != null) { vc.c4 = new Color4f ((colorsB.get(ci + 0) & 0xff) * ByteToFloatScale, (colorsB.get(ci + 1) & 0xff) * ByteToFloatScale, (colorsB.get(ci + 2) & 0xff) * ByteToFloatScale, (colorsB.get(ci + 3) & 0xff) * ByteToFloatScale) ; } else { vc.c4 = new Color4f(colorsF.get(ci + 0), colorsF.get(ci + 1), colorsF.get(ci + 2), colorsF.get(ci + 3)) ; } vc.c = vc.c4 ; } } @Override public void processVertex(int v, int stripFlag) { copyVertex(v + initialPositionIndex, v + initialNormalIndex, v + initialColorIndex, vc) ; processVertexCopy(vc, stripFlag) ; } } /** * This class implements the GeometryAccessor interface for * non-interleaved indexed geometry arrays accessed with NIO. */ private class IndexedByRefGeometryNIO extends ByRefGeometryNIO { IndexArrays ia = new IndexArrays() ; VertexIndices vi = new VertexIndices() ; IndexedByRefGeometryNIO(GeometryArray ga) { super(ga) ; getIndexArrays(ga, ia) ; } @Override public void processVertex(int v, int stripFlag) { getVertexIndices(v, ia, vi) ; copyVertex(vi.pi, vi.ni, vi.ci, vc) ; processIndexedVertexCopy(vc, vi, stripFlag) ; } } /** * Convert a GeometryArray to compression stream elements and add them to * this stream. * * @param ga GeometryArray to convert * @exception IllegalArgumentException if GeometryArray has a * dimensionality or vertex format inconsistent with the CompressionStream */ void addGeometryArray(GeometryArray ga) { int firstVertex = 0 ; int validVertexCount = 0 ; int vertexFormat = ga.getVertexFormat() ; GeometryAccessor geometryAccessor = null ; if (streamType != getStreamType(ga)) throw new IllegalArgumentException ("GeometryArray has inconsistent dimensionality") ; if (vertexComponents != getVertexComponents(vertexFormat)) throw new IllegalArgumentException ("GeometryArray has inconsistent vertex components") ; // Set up for vertex data access semantics. boolean NIO = (vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0 ; boolean byRef = (vertexFormat & GeometryArray.BY_REFERENCE) != 0 ; boolean interleaved = (vertexFormat & GeometryArray.INTERLEAVED) != 0 ; boolean indexedGeometry = ga instanceof IndexedGeometryArray ; if (indexedGeometry) { if (debug) System.out.println("indexed") ; // Index arrays will be copied such that valid indices start at // offset 0 in the copied arrays. firstVertex = 0 ; validVertexCount = ((IndexedGeometryArray)ga).getValidIndexCount() ; } if (!byRef) { if (debug) System.out.println("by-copy") ; if (indexedGeometry) { geometryAccessor = new IndexedByCopyGeometry(ga) ; } else { firstVertex = 0 ; validVertexCount = ga.getValidVertexCount() ; geometryAccessor = new ByCopyGeometry(ga) ; } } else if (interleaved && NIO) { if (debug) System.out.println("interleaved NIO") ; if (indexedGeometry) { geometryAccessor = new IndexedInterleavedGeometryNIO(ga) ; } else { firstVertex = ga.getInitialVertexIndex() ; validVertexCount = ga.getValidVertexCount() ; geometryAccessor = new InterleavedGeometryNIO(ga) ; } } else if (interleaved && !NIO) { if (debug) System.out.println("interleaved") ; if (indexedGeometry) { geometryAccessor = new IndexedInterleavedGeometryFloat(ga) ; } else { firstVertex = ga.getInitialVertexIndex() ; validVertexCount = ga.getValidVertexCount() ; geometryAccessor = new InterleavedGeometryFloat(ga) ; } } else if (!interleaved && NIO) { if (debug) System.out.println("non-interleaved NIO") ; if (indexedGeometry) { geometryAccessor = new IndexedByRefGeometryNIO(ga) ; } else { firstVertex = 0 ; validVertexCount = ga.getValidVertexCount() ; geometryAccessor = new ByRefGeometryNIO(ga) ; } } else if (!interleaved && !NIO) { if (debug) System.out.println("non-interleaved by-ref") ; if (indexedGeometry) { geometryAccessor = new IndexedByRefGeometry(ga) ; } else { firstVertex = 0 ; validVertexCount = ga.getValidVertexCount() ; geometryAccessor = new ByRefGeometry(ga) ; } } // Set up for topology. int stripCount = 0 ; int stripCounts[] = null ; int constantStripLength = 0 ; int replaceCode = RESTART ; boolean strips = false ; boolean implicitStrips = false ; if (ga instanceof TriangleStripArray || ga instanceof IndexedTriangleStripArray || ga instanceof LineStripArray || ga instanceof IndexedLineStripArray) { strips = true ; replaceCode = REPLACE_OLDEST ; if (debug) System.out.println("strips") ; } else if (ga instanceof TriangleFanArray || ga instanceof IndexedTriangleFanArray) { strips = true ; replaceCode = REPLACE_MIDDLE ; if (debug) System.out.println("fans") ; } else if (ga instanceof QuadArray || ga instanceof IndexedQuadArray) { // Handled as fan arrays with 4 vertices per fan. implicitStrips = true ; constantStripLength = 4 ; replaceCode = REPLACE_MIDDLE ; if (debug) System.out.println("quads") ; } // Get strip counts. if (strips) { if (indexedGeometry) { IndexedGeometryStripArray igsa ; igsa = (IndexedGeometryStripArray)ga ; stripCount = igsa.getNumStrips() ; stripCounts = new int[stripCount] ; igsa.getStripIndexCounts(stripCounts) ; } else { GeometryStripArray gsa ; gsa = (GeometryStripArray)ga ; stripCount = gsa.getNumStrips() ; stripCounts = new int[stripCount] ; gsa.getStripVertexCounts(stripCounts) ; } } // Build the compression stream for this shape's geometry. int v = firstVertex ; if (strips) { for (int i = 0 ; i < stripCount ; i++) { geometryAccessor.processVertex(v++, RESTART) ; for (int j = 1 ; j < stripCounts[i] ; j++) { geometryAccessor.processVertex(v++, replaceCode) ; } } } else if (implicitStrips) { while (v < firstVertex + validVertexCount) { geometryAccessor.processVertex(v++, RESTART) ; for (int j = 1 ; j < constantStripLength ; j++) { geometryAccessor.processVertex(v++, replaceCode) ; } } } else { while (v < firstVertex + validVertexCount) { geometryAccessor.processVertex(v++, RESTART) ; } } } /** * Print the stream to standard output. */ void print() { System.out.println("\nstream has " + stream.size() + " entries") ; System.out.println("uncompressed size " + byteCount + " bytes") ; System.out.println("upper position bound: " + mcBounds[1].toString()) ; System.out.println("lower position bound: " + mcBounds[0].toString()) ; System.out.println("X, Y, Z centers (" + ((float)center[0]) + " " + ((float)center[1]) + " " + ((float)center[2]) + ")\n" + "scale " + ((float)scale) + "\n") ; Iterator i = stream.iterator() ; while (i.hasNext()) { System.out.println(i.next().toString() + "\n") ; } } //////////////////////////////////////////////////////////////////////////// // // // The following constructors and methods are currently the only public // // members of this class. All other members are subject to revision. // // // //////////////////////////////////////////////////////////////////////////// /** * Creates a CompressionStream from an array of Shape3D scene graph * objects. These Shape3D objects may only consist of a GeometryArray * component and an optional Appearance component. The resulting stream * may be used as input to the GeometryCompressor methods.

* * Each Shape3D in the array must be of the same dimensionality (point, * line, or surface) and have the same vertex format as the others. * Texture coordinates are ignored.

* * If a color is specified in the material attributes for a Shape3D then * that color is added to the CompressionStream as the current global * color. Subsequent colors as well as any colors bundled with vertices * will override it. Only the material diffuse colors are used; all other * appearance attributes are ignored.

* * @param positionQuant * number of bits to quantize each position's X, Y, * and Z components, ranging from 1 to 16 * * @param colorQuant * number of bits to quantize each color's R, G, B, and * alpha components, ranging from 2 to 16 * * @param normalQuant * number of bits for quantizing each normal's U and V components, ranging * from 0 to 6 * * @param shapes * an array of Shape3D scene graph objects containing * GeometryArray objects, all with the same vertex format and * dimensionality * * @exception IllegalArgumentException if any Shape3D has an inconsistent * dimensionality or vertex format, or if any Shape3D contains a geometry * component that is not a GeometryArray * * @see Shape3D * @see GeometryArray * @see GeometryCompressor */ public CompressionStream(int positionQuant, int colorQuant, int normalQuant, Shape3D shapes[]) { this() ; if (debug) System.out.println("CompressionStream(Shape3D[]):") ; if (shapes == null) throw new IllegalArgumentException("null Shape3D array") ; if (shapes.length == 0) throw new IllegalArgumentException("zero-length Shape3D array") ; if (shapes[0] == null) throw new IllegalArgumentException("Shape3D at index 0 is null") ; long startTime = 0 ; if (benchmark) startTime = System.currentTimeMillis() ; Geometry g = shapes[0].getGeometry() ; if (! (g instanceof GeometryArray)) throw new IllegalArgumentException ("Shape3D at index 0 is not a GeometryArray") ; GeometryArray ga = (GeometryArray)g ; this.streamType = getStreamType(ga) ; this.vertexComponents = getVertexComponents(ga.getVertexFormat()) ; // Add global quantization parameters to the start of the stream. addPositionQuantization(positionQuant) ; addColorQuantization(colorQuant) ; addNormalQuantization(normalQuant) ; // Loop through all shapes. for (int s = 0 ; s < shapes.length ; s++) { if (debug) System.out.println("\nShape3D " + s + ":") ; g = shapes[s].getGeometry() ; if (! (g instanceof GeometryArray)) throw new IllegalArgumentException ("Shape3D at index " + s + " is not a GeometryArray") ; // Check for material color and add it to the stream if it exists. Appearance a = shapes[s].getAppearance() ; if (a != null) { Material m = a.getMaterial() ; if (m != null) { m.getDiffuseColor(c3f) ; if (vertexColor4) { c4f.set(c3f.x, c3f.y, c3f.z, 1.0f) ; addColor(c4f) ; } else addColor(c3f) ; } } // Add the geometry array to the stream. addGeometryArray((GeometryArray)g) ; } if (benchmark) { long t = System.currentTimeMillis() - startTime ; System.out.println ("\nCompressionStream:\n" + shapes.length + " shapes in " + (t / 1000f) + " sec") ; } } /** * Creates a CompressionStream from an array of Shape3D scene graph * objects. These Shape3D objects may only consist of a GeometryArray * component and an optional Appearance component. The resulting stream * may be used as input to the GeometryCompressor methods.

* * Each Shape3D in the array must be of the same dimensionality (point, * line, or surface) and have the same vertex format as the others. * Texture coordinates are ignored.

* * If a color is specified in the material attributes for a Shape3D then * that color is added to the CompressionStream as the current global * color. Subsequent colors as well as any colors bundled with vertices * will override it. Only the material diffuse colors are used; all other * appearance attributes are ignored.

* * Defaults of 16, 9, and 6 bits are used as the quantization values for * positions, colors, and normals respectively. These are the maximum * resolution values defined for positions and normals; the default of 9 * for color is the equivalent of the 8 bits of RGBA component resolution * commonly available in graphics frame buffers.

* * @param shapes * an array of Shape3D scene graph objects containing * GeometryArray objects, all with the same vertex format and * dimensionality. * * @exception IllegalArgumentException if any Shape3D has an inconsistent * dimensionality or vertex format, or if any Shape3D contains a geometry * component that is not a GeometryArray * * @see Shape3D * @see GeometryArray * @see GeometryCompressor */ public CompressionStream(Shape3D shapes[]) { this(16, 9, 6, shapes) ; } /** * Creates a CompressionStream from an array of GeometryInfo objects. The * resulting stream may be used as input to the GeometryCompressor * methods.

* * Each GeometryInfo in the array must be of the same dimensionality * (point, line, or surface) and have the same vertex format as the * others. Texture coordinates are ignored.

* * @param positionQuant * number of bits to quantize each position's X, Y, * and Z components, ranging from 1 to 16 * * @param colorQuant * number of bits to quantize each color's R, G, B, and * alpha components, ranging from 2 to 16 * * @param normalQuant * number of bits for quantizing each normal's U and V components, ranging * from 0 to 6 * * @param geometry * an array of GeometryInfo objects, all with the same * vertex format and dimensionality * * @exception IllegalArgumentException if any GeometryInfo object has an * inconsistent dimensionality or vertex format * * @see GeometryInfo * @see GeometryCompressor */ public CompressionStream(int positionQuant, int colorQuant, int normalQuant, GeometryInfo geometry[]) { this() ; if (debug) System.out.println("CompressionStream(GeometryInfo[])") ; if (geometry == null) throw new IllegalArgumentException("null GeometryInfo array") ; if (geometry.length == 0) throw new IllegalArgumentException ("zero-length GeometryInfo array") ; if (geometry[0] == null) throw new IllegalArgumentException ("GeometryInfo at index 0 is null") ; long startTime = 0 ; if (benchmark) startTime = System.currentTimeMillis() ; GeometryArray ga = geometry[0].getGeometryArray() ; this.streamType = getStreamType(ga) ; this.vertexComponents = getVertexComponents(ga.getVertexFormat()) ; // Add global quantization parameters to the start of the stream. addPositionQuantization(positionQuant) ; addColorQuantization(colorQuant) ; addNormalQuantization(normalQuant) ; // Loop through all GeometryInfo objects and add them to the stream. for (int i = 0 ; i < geometry.length ; i++) { if (debug) System.out.println("\nGeometryInfo " + i + ":") ; addGeometryArray(geometry[i].getGeometryArray()) ; } if (benchmark) { long t = System.currentTimeMillis() - startTime ; System.out.println ("\nCompressionStream:\n" + geometry.length + " GeometryInfo objects in " + (t / 1000f) + " sec") ; } } /** * Creates a CompressionStream from an array of GeometryInfo objects. The * resulting stream may be used as input to the GeometryCompressor * methods.

* * Each GeometryInfo in the array must be of the same dimensionality * (point, line, or surface) and have the same vertex format as the * others. Texture coordinates are ignored.

* * Defaults of 16, 9, and 6 bits are used as the quantization values for * positions, colors, and normals respectively. These are the maximum * resolution values defined for positions and normals; the default of 9 * for color is the equivalent of the 8 bits of RGBA component resolution * commonly available in graphics frame buffers.

* * @param geometry * an array of GeometryInfo objects, all with the same * vertex format and dimensionality * * @exception IllegalArgumentException if any GeometryInfo object has an * inconsistent dimensionality or vertex format * * @see GeometryInfo * @see GeometryCompressor */ public CompressionStream(GeometryInfo geometry[]) { this(16, 9, 6, geometry) ; } /** * Get the original bounds of the coordinate data, in modeling coordinates. * Coordinate data is positioned and scaled to a normalized cube after * compression. * * @return Point3d array of length 2, where the 1st Point3d is the lower * bounds and the 2nd Point3d is the upper bounds. * @since Java 3D 1.3 */ public Point3d[] getModelBounds() { Point3d[] bounds = new Point3d[2] ; bounds[0] = new Point3d(mcBounds[0]) ; bounds[1] = new Point3d(mcBounds[1]) ; return bounds ; } /** * Get the bounds of the compressed object in normalized coordinates. * These have an maximum bounds by [-1.0 .. +1.0] across each axis. * * @return Point3d array of length 2, where the 1st Point3d is the lower * bounds and the 2nd Point3d is the upper bounds. * @since Java 3D 1.3 */ public Point3d[] getNormalizedBounds() { Point3d[] bounds = new Point3d[2] ; bounds[0] = new Point3d(ncBounds[0]) ; bounds[1] = new Point3d(ncBounds[1]) ; return bounds ; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy