summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Rushforth <[email protected]>2006-09-29 18:04:59 +0000
committerKevin Rushforth <[email protected]>2006-09-29 18:04:59 +0000
commit84db704dbd19cf4249879cb1e026568632712f30 (patch)
tree6a74aa076b3b7e114826110eacc5f71b4a411bf4
parent5b994f050b817515e3425f0554bd93afee449373 (diff)
Merged dev-1_5 branch back to MAIN trunk
git-svn-id: https://svn.java.net/svn/j3d-core-utils~svn/trunk@129 9497e636-51bd-65ba-982d-a4982e1767a5
-rw-r--r--src/classes/ToolsVersion2
-rw-r--r--src/classes/share/com/sun/j3d/ExceptionStrings.properties10
-rw-r--r--src/classes/share/com/sun/j3d/exp/swing/JCanvas3D.java (renamed from src/classes/share/com/sun/j3d/internal/UtilFreelistManager.java)59
-rw-r--r--src/classes/share/com/sun/j3d/exp/swing/impl/AutoOffScreenCanvas3D.java58
-rw-r--r--src/classes/share/com/sun/j3d/exp/swing/package.html13
-rw-r--r--src/classes/share/com/sun/j3d/internal/UtilMemoryFreelist.java292
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/picking/package.html2
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CommandStream.java2
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryFile.java12
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressionStream.java51
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressionStreamColor.java6
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressionStreamElement.java2
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNormal.java5
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVertex.java8
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.java13
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/HuffmanNode.java6
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/HuffmanTable.java10
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/MeshBuffer.java8
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/package.html4
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/CommandStream.java265
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryData.java546
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryFile.java1011
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryRetained.java282
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStream.java2321
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamColor.java272
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamElement.java359
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamNormal.java674
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamVertex.java322
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedStrip.java908
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedStripFlags.java105
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedVertexList.java429
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryCompressor.java204
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDecompressor.java1236
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDecompressorShape3D.java523
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/HuffmanNode.java225
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/HuffmanTable.java483
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/MeshBuffer.java242
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/compression/package.html13
-rw-r--r--src/classes/share/com/sun/j3d/utils/image/TextureLoader.java65
-rw-r--r--src/classes/share/com/sun/j3d/utils/pickfast/PickTool.java11
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/PickResult.java246
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/PickTool.java36
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/behaviors/PickMouseBehavior.java4
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/behaviors/PickRotateBehavior.java3
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/behaviors/PickTranslateBehavior.java3
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/behaviors/PickZoomBehavior.java3
-rw-r--r--src/classes/share/com/sun/j3d/utils/timer/J3DTimer.java32
-rw-r--r--src/classes/share/com/sun/j3d/utils/timer/package.html4
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ViewInfo.java10
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/Viewer.java47
-rw-r--r--src/native/share/J3DTimer.c175
51 files changed, 10767 insertions, 855 deletions
diff --git a/src/classes/ToolsVersion b/src/classes/ToolsVersion
index d6e665f..35a52f3 100644
--- a/src/classes/ToolsVersion
+++ b/src/classes/ToolsVersion
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
Specification-Title:
-Specification-Version: 1.4
+Specification-Version: 1.5
Specification-Vendor: Sun Microsystems, Inc.
Implementation-Title: Java 3D Utilities Runtime Environment
Implementation-Version: @VERSION_BASE@
diff --git a/src/classes/share/com/sun/j3d/ExceptionStrings.properties b/src/classes/share/com/sun/j3d/ExceptionStrings.properties
index bf031b7..363945c 100644
--- a/src/classes/share/com/sun/j3d/ExceptionStrings.properties
+++ b/src/classes/share/com/sun/j3d/ExceptionStrings.properties
@@ -24,6 +24,16 @@ KBSplinePathInterpolator1=KBSplinePathInterpolator: first key frame should have
KBSplinePathInterpolator2=KBSplinePathInterpolator: last key frame should have knot value of 1.0
KBSplinePathInterpolator3=KBSplinePathInterpolator: Key Frame knot values not in sequence
KBCubicSplineCurve0=KBCubicSplineCurve needs at least 4 key frames
+CompressedGeometry0=CompressedGeometry: start+size exceeds geometry length
+CompressedGeometry4=CompressedGeometry: target buffer is too small
+CompressedGeometry7=CompressedGeometry: cannot directly access data in byReference mode
+CompressedGeometry8=CompressedGeometry: must be in byReference mode to use this method
+CompressedGeometry9=CompressedGeometry: NIO buffer support is not implemented
+GeneralizedStrip0=GeneralizedStrip: strip ended incompletely
+GeometryDecompressor0=GeometryDecompressor: start+length > data array size
+GeometryDecompressor1=GeometryDecompressor: bad delta normal in compressed buffer
+GeometryDecompressorShape3D0=GeometryDecompressorShape3D: bad triangle output type
+GeometryDecompressorShape3D1=GeometryDecompressorShape3D: bad buffer data type
GeometryInfo0=Illegal primitive.
GeometryInfo1=Illegal use of deprecated setTextureCoordinateIndices(int[])
GeometryInfo2=Length of float array not a multiple of dimensionality of texcoords
diff --git a/src/classes/share/com/sun/j3d/internal/UtilFreelistManager.java b/src/classes/share/com/sun/j3d/exp/swing/JCanvas3D.java
index 05f7ea4..2bc3692 100644
--- a/src/classes/share/com/sun/j3d/internal/UtilFreelistManager.java
+++ b/src/classes/share/com/sun/j3d/exp/swing/JCanvas3D.java
@@ -42,57 +42,12 @@
* $State$
*/
-package com.sun.j3d.internal;
+package com.sun.j3d.exp.swing;
-
-public class UtilFreelistManager {
-
- private static final boolean DEBUG = false;
-
- // constants that represent the freelists managed by the Manager
- public static final int VECTOR3D = 0;
- public static final int POINT3D = 1;
- public static final int PICKRESULT = 2;
- public static final int MAXINT = 2;
-
- // what list we are going to shrink next
- private static int currlist = 0;
-
- // the freelists managed by the manager
- public static UtilMemoryFreelist vector3dFreelist = new UtilMemoryFreelist("javax.vecmath.Vector3d");
- public static UtilMemoryFreelist point3dFreelist = new UtilMemoryFreelist("javax.vecmath.Point3d");
- public static UtilMemoryFreelist pickResultFreelist = new UtilMemoryFreelist("com.sun.j3d.utils.picking.PickResult");
-
-
-// static MemoryFreeList[] freelist = new MemoryFreeList[MAXINT+1];
-
-// static void createFreeLists() {
-// freelist[VECTOR3D] = new MemoryFreeList("javax.vecmath.Vector3d");
-// freelist[POINT3D] = new MemoryFreeList("javax.vecmath.Point3d");
-// }
-
-
-// // see if the current list can be shrunk
-// static void manageLists() {
-// // System.out.println("manageLists");
-// if (freelist[currlist] != null) {
-// freelist[currlist].shrink();
-// }
-
-// currlist++;
-// if (currlist > MAXINT) currlist = 0;
-// }
-
-// // return the freelist specified by the list param
-// static MemoryFreeList getFreeList(int list) {
-// if (list < 0 || list > MAXINT) {
-// if (DEBUG) System.out.println("illegal list");
-// return null;
-// }
-// else {
-// return freelist[list];
-// }
-// }
-
-
+/**
+ * Placeholder for JCanvas3D class.
+ */
+public class JCanvas3D {
+ private JCanvas3D() {
+ }
}
diff --git a/src/classes/share/com/sun/j3d/exp/swing/impl/AutoOffScreenCanvas3D.java b/src/classes/share/com/sun/j3d/exp/swing/impl/AutoOffScreenCanvas3D.java
new file mode 100644
index 0000000..9517506
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/exp/swing/impl/AutoOffScreenCanvas3D.java
@@ -0,0 +1,58 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.exp.swing.impl;
+
+/**
+ * Tagging interface for Java 3D off-screen Canvas3D objects that are
+ * automatically rendered. This is used internally by the JCanvas3D
+ * implementation.
+ * <p>
+ * NOTE: this is an experimental interface, which is not intended for use
+ * by applications.
+ *
+ * @author pepe
+ */
+public interface AutoOffScreenCanvas3D {
+}
diff --git a/src/classes/share/com/sun/j3d/exp/swing/package.html b/src/classes/share/com/sun/j3d/exp/swing/package.html
new file mode 100644
index 0000000..e1cff44
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/exp/swing/package.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+ <title>com.sun.j3d.exp.swing</title>
+</head>
+<body>
+<p><i>EXPERIMENTAL</i>: Provides a lightweight JCanvas3D class.
+Note that the API in this package is highly experimental and
+subject to change at any time.</p>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/internal/UtilMemoryFreelist.java b/src/classes/share/com/sun/j3d/internal/UtilMemoryFreelist.java
deleted file mode 100644
index 9e94287..0000000
--- a/src/classes/share/com/sun/j3d/internal/UtilMemoryFreelist.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * $RCSfile$
- *
- * Copyright (c) 2006 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.
- *
- * $Revision$
- * $Date$
- * $State$
- */
-
-package com.sun.j3d.internal;
-
-import java.util.*;
-
-// this class must be synchronized because different threads may try to access
-// the freelists
-public class UtilMemoryFreelist { // extends AbstractList {
-
- // never go smaller than the initial capacity
- ArrayList elementData = null;
- int size = 0;
- int currBlockSize = 10;
- Object[] currBlock = null;
- int currBlockIndex = 0;
- int spaceUsed = 0;
- int numBlocks = 0;
- int capacity = 0;
- int minBlockSize = 0;
- boolean justShrunk = false;
- int initcap = 10;
-
- // the minimum size since the last shrink
- int minSize = 0;
-
- Class c = null;
-
- public UtilMemoryFreelist(String className) {
- this(className, 10);
- }
-
- public UtilMemoryFreelist(String className, int initialCapacity) {
- if (initialCapacity < 0) {
- throw new IllegalArgumentException ("Illegal Capacity: " +
- initialCapacity);
- }
-
- try {
- c = Class.forName(className);
- }
- catch (Exception e) {
-// System.out.println(e);
- }
-
- initcap = initialCapacity;
- currBlockSize = initialCapacity;
- minBlockSize = currBlockSize;
- elementData = new ArrayList();
- // add the first block of memory to the arraylist
- currBlock = new Object[currBlockSize];
- elementData.add(currBlock);
- numBlocks++;
- capacity += currBlockSize;
- }
-
- public UtilMemoryFreelist(String className, Collection collection) {
- try {
- c = Class.forName(className);
- }
- catch (Exception e) {
-// System.out.println(e);
- }
- size = collection.size();
- initcap = size;
- currBlockSize = size;
- minBlockSize = currBlockSize;
- elementData = new ArrayList();
- currBlock = new Object[currBlockSize];
- collection.toArray(currBlock);
- elementData.add(currBlock);
- numBlocks++;
- capacity += currBlockSize;
- spaceUsed = size;
- }
-
- public synchronized int size() {
- return size;
- }
-
-
- public synchronized boolean add(Object o) {
- if (justShrunk) {
- // empty some space out in the current block instead of
- // adding this message
- if ((currBlockSize/2) < spaceUsed) {
- size -= (spaceUsed - (currBlockSize/2));
- spaceUsed = (currBlockSize/2);
- Arrays.fill(currBlock, spaceUsed, currBlockSize-1, null);
- }
- justShrunk = false;
- return false;
- }
- else {
- ensureCapacity(size+1);
-
- // check to see if the whole block is used and if so, reset the
- // current block
-// System.out.println("spaceUsed = " + spaceUsed + " currBlockSize = " +
-// currBlockSize + " currBlockIndex = " +
-// currBlockIndex + " currBlock = " + currBlock);
- if ((currBlockIndex == -1) || (spaceUsed >= currBlockSize)) {
- currBlockIndex++;
- currBlock = (Object[])elementData.get(currBlockIndex);
- currBlockSize = currBlock.length;
- spaceUsed = 0;
- }
- int index = spaceUsed++;
- currBlock[index] = o;
- size++;
-
- return true;
- }
- }
-
- private synchronized Object removeLastElement() {
-// System.out.println("removeLastElement: size = " + size);
- int index = --spaceUsed;
-// System.out.println("index = " + index);
- Object elm = currBlock[index];
- currBlock[index] = null;
- size--;
-
- // see if this block is empty now, and if it is set the previous
- // block to the current block
- if (spaceUsed == 0) {
- currBlockIndex--;
- if (currBlockIndex < 0) {
- currBlock = null;
- currBlockSize = 0;
- }
- else {
- currBlock = (Object[])elementData.get(currBlockIndex);
- currBlockSize = currBlock.length;
- }
- spaceUsed = currBlockSize;
- }
-
- return elm;
- }
-
-
- public synchronized void shrink() {
-// System.out.println("shrink size = " + size + " minSize = " +
-// minSize);
- if ((minSize > minBlockSize) && (numBlocks > 1)) {
- justShrunk = true;
-
-// System.out.println("removing a block");
-// Runtime r = Runtime.getRuntime();
-// r.gc();
-// System.out.println("numBlocks = " + numBlocks + " size = " + size);
-// System.out.println("free memory before shrink: " + r.freeMemory());
-
- // remove the last block
- Object[] block = (Object[])elementData.remove(numBlocks-1);
- numBlocks--;
- capacity -= block.length;
-
- // we only need to do this if the block removed was the current
- // block. otherwise we just removed a null block.
- if (numBlocks == currBlockIndex) {
- size -= spaceUsed;
- // set the current block to the last one
- currBlockIndex = numBlocks-1;
- currBlock = (Object[])elementData.get(currBlockIndex);
- currBlockSize = currBlock.length;
-
- spaceUsed = currBlockSize;
-
- }
-
-// r.gc();
-// System.out.println("free memory after shrink: " + r.freeMemory());
-// System.out.println("numBlocks = " + numBlocks + " size = " + size);
- }
- else {
- justShrunk = false;
- }
- minSize = size;
- }
-
- public synchronized void ensureCapacity(int minCapacity) {
-// System.out.println("ensureCapacity: size = " + size + " capacity: " +
-// elementData.length);
-// System.out.println("minCapacity = " + minCapacity + " capacity = "
-// + capacity);
-
- if (minCapacity > capacity) {
-// System.out.println("adding a block: numBlocks = " + numBlocks);
- int lastBlockSize =
- ((Object[])elementData.get(numBlocks-1)).length;
- int prevBlockSize = 0;
- if (numBlocks > 1) {
- prevBlockSize =
- ((Object[])elementData.get(numBlocks-2)).length;
- }
- currBlockSize = lastBlockSize + prevBlockSize;
- currBlock = new Object[currBlockSize];
- elementData.add(currBlock);
- numBlocks++;
- currBlockIndex++;
- capacity += currBlockSize;
- // there is nothing used in this block yet
- spaceUsed = 0;
- }
- }
-
- synchronized void rangeCheck(int index) {
- if (index >= size || index < 0) {
- throw new IndexOutOfBoundsException("Index: " + index +
- ", Size: " + size);
- }
- }
-
- public synchronized void clear() {
-// System.out.println("clear");
- elementData.clear();
-
- // put an empty block in
- currBlockSize = initcap;
- minBlockSize = currBlockSize;
- currBlock = new Object[currBlockSize];
- elementData.add(currBlock);
- numBlocks = 1;
- capacity = currBlockSize;
- spaceUsed = 0;
- size = 0;
- currBlockIndex = 0;
- justShrunk = false;
- }
-
- public synchronized Object getObject() {
- if (size > 0) {
- return removeLastElement();
- }
- else {
- try {
- return c.newInstance();
- }
- catch (Exception e) {
-// System.out.println("caught exception");
-// System.out.print(e);
- return null;
- }
- }
- }
-
-}
-
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/picking/package.html b/src/classes/share/com/sun/j3d/utils/behaviors/picking/package.html
index a0f813c..b5d0b77 100644
--- a/src/classes/share/com/sun/j3d/utils/behaviors/picking/package.html
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/picking/package.html
@@ -6,7 +6,7 @@
<title>com.sun.j3d.utils.behaviors.picking</title>
</head>
<body>
-<p><i><b>Deprecated</b>: this package is deprecated; use <code><a
+<p><i><b>Deprecated</b>: Use <code><a
href="../../pickfast/behaviors/package-summary.html">com.sun.j3d.utils.pickfast.behaviors</a></code>
instead.</i></p>
</body>
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CommandStream.java b/src/classes/share/com/sun/j3d/utils/compression/CommandStream.java
index 15739a6..6be092e 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/CommandStream.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/CommandStream.java
@@ -42,7 +42,7 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
+package com.sun.j3d.utils.compression;
/**
* This class is used to build the bit-level compression command stream which
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryFile.java b/src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryFile.java
index ef42300..57bcad8 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryFile.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryFile.java
@@ -42,10 +42,13 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
+package com.sun.j3d.utils.compression;
-import java.io.* ;
-import javax.media.j3d.* ;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import javax.media.j3d.CompressedGeometry;
+import javax.media.j3d.CompressedGeometryHeader;
//
// The compressed geometry file format supported by this class has a 32
@@ -65,6 +68,9 @@ import javax.media.j3d.* ;
* files. These files usually end with the .cg extension and support
* sequential as well as random access to multiple compressed geometry
* objects.
+ *
+ * @deprecated As of Java 3D 1.5, replaced by
+ * com.sun.j3d.utils.geometry.compression.{@link com.sun.j3d.utils.geometry.compression.CompressedGeometryFile}.
*/
public class CompressedGeometryFile {
private static final boolean print = false ;
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressionStream.java b/src/classes/share/com/sun/j3d/utils/compression/CompressionStream.java
index 7757bed..c401103 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/CompressionStream.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressionStream.java
@@ -42,16 +42,44 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
-
-import java.util.* ;
-import javax.vecmath.* ;
-import javax.media.j3d.* ;
-import com.sun.j3d.utils.geometry.* ;
-import com.sun.j3d.internal.ByteBufferWrapper ;
-import com.sun.j3d.internal.BufferWrapper ;
-import com.sun.j3d.internal.FloatBufferWrapper ;
-import com.sun.j3d.internal.DoubleBufferWrapper ;
+package com.sun.j3d.utils.compression;
+
+import com.sun.j3d.internal.BufferWrapper;
+import com.sun.j3d.internal.ByteBufferWrapper;
+import com.sun.j3d.internal.DoubleBufferWrapper;
+import com.sun.j3d.internal.FloatBufferWrapper;
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import javax.media.j3d.Appearance;
+import javax.media.j3d.CompressedGeometryHeader;
+import javax.media.j3d.Geometry;
+import javax.media.j3d.GeometryArray;
+import javax.media.j3d.GeometryStripArray;
+import javax.media.j3d.IndexedGeometryArray;
+import javax.media.j3d.IndexedGeometryStripArray;
+import javax.media.j3d.IndexedLineArray;
+import javax.media.j3d.IndexedLineStripArray;
+import javax.media.j3d.IndexedQuadArray;
+import javax.media.j3d.IndexedTriangleArray;
+import javax.media.j3d.IndexedTriangleFanArray;
+import javax.media.j3d.IndexedTriangleStripArray;
+import javax.media.j3d.J3DBuffer;
+import javax.media.j3d.LineArray;
+import javax.media.j3d.LineStripArray;
+import javax.media.j3d.Material;
+import javax.media.j3d.QuadArray;
+import javax.media.j3d.Shape3D;
+import javax.media.j3d.TriangleArray;
+import javax.media.j3d.TriangleFanArray;
+import javax.media.j3d.TriangleStripArray;
+import javax.vecmath.Color3f;
+import javax.vecmath.Color4f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Point3f;
+import javax.vecmath.Point3i;
+import javax.vecmath.Vector3f;
/**
* This class is used as input to a geometry compressor. It collects elements
@@ -60,6 +88,9 @@ import com.sun.j3d.internal.DoubleBufferWrapper ;
* the compression process and used to build the compressed output buffer.
*
* @see GeometryCompressor
+ *
+ * @deprecated As of Java 3D 1.5, replaced by
+ * com.sun.j3d.utils.geometry.compression.{@link com.sun.j3d.utils.geometry.compression.CompressionStream}.
*/
public class CompressionStream {
//
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamColor.java b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamColor.java
index 63528b9..bc1527b 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamColor.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamColor.java
@@ -42,8 +42,10 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
-import javax.vecmath.* ;
+package com.sun.j3d.utils.compression;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Color4f;
/**
* This class represents a color in a compression stream. It maintains both
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamElement.java b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamElement.java
index 55d0c3d..240d35e 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamElement.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamElement.java
@@ -42,7 +42,7 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
+package com.sun.j3d.utils.compression;
/**
* Instances of this class are used as elements in a CompressionStream.
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNormal.java b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNormal.java
index 4cc9acc..642c7e0 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNormal.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNormal.java
@@ -42,8 +42,9 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
-import javax.vecmath.* ;
+package com.sun.j3d.utils.compression;
+
+import javax.vecmath.Vector3f;
/**
* This class represents a normal in a compression stream. It maintains both
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVertex.java b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVertex.java
index 4a562e2..1fe7e7a 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVertex.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVertex.java
@@ -42,8 +42,12 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
-import javax.vecmath.* ;
+package com.sun.j3d.utils.compression;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Color4f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
/**
* This class represents a vertex in a compression stream. It maintains both
diff --git a/src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.java b/src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.java
index 43007d1..d4479a4 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.java
@@ -42,12 +42,12 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
+package com.sun.j3d.utils.compression;
-import javax.media.j3d.* ;
-import javax.vecmath.* ;
-import java.util.* ;
-import java.io.* ;
+import java.io.IOException;
+import javax.media.j3d.CompressedGeometry;
+import javax.media.j3d.CompressedGeometryHeader;
+import javax.vecmath.Point3d;
/**
* A GeometryCompressor takes a stream of geometric elements and
@@ -60,6 +60,9 @@ import java.io.* ;
* @see CompressionStream
* @see CompressedGeometry
* @see CompressedGeometryFile
+ *
+ * @deprecated As of Java 3D 1.5, replaced by
+ * com.sun.j3d.utils.geometry.compression.{@link com.sun.j3d.utils.geometry.compression.GeometryCompressor}.
*/
public class GeometryCompressor {
private static final boolean benchmark = false ;
diff --git a/src/classes/share/com/sun/j3d/utils/compression/HuffmanNode.java b/src/classes/share/com/sun/j3d/utils/compression/HuffmanNode.java
index e2b7d9c..d4b6279 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/HuffmanNode.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/HuffmanNode.java
@@ -42,8 +42,10 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
-import java.util.* ;
+package com.sun.j3d.utils.compression;
+
+import java.util.Collection;
+import java.util.Comparator;
/**
* Instances of this class are used as the nodes of binary trees representing
diff --git a/src/classes/share/com/sun/j3d/utils/compression/HuffmanTable.java b/src/classes/share/com/sun/j3d/utils/compression/HuffmanTable.java
index 0f47135..3023eed 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/HuffmanTable.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/HuffmanTable.java
@@ -42,8 +42,14 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
-import java.util.* ;
+package com.sun.j3d.utils.compression;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.ListIterator;
/**
* This class maintains a map from compression stream elements (tokens) onto
diff --git a/src/classes/share/com/sun/j3d/utils/compression/MeshBuffer.java b/src/classes/share/com/sun/j3d/utils/compression/MeshBuffer.java
index b399042..cd8f873 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/MeshBuffer.java
+++ b/src/classes/share/com/sun/j3d/utils/compression/MeshBuffer.java
@@ -42,8 +42,12 @@
* $State$
*/
-package com.sun.j3d.utils.compression ;
-import javax.vecmath.* ;
+package com.sun.j3d.utils.compression;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Color4f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
/**
* This class mirrors the vertex mesh buffer stack supported by the geometry
diff --git a/src/classes/share/com/sun/j3d/utils/compression/package.html b/src/classes/share/com/sun/j3d/utils/compression/package.html
index 538b9ea..628453b 100644
--- a/src/classes/share/com/sun/j3d/utils/compression/package.html
+++ b/src/classes/share/com/sun/j3d/utils/compression/package.html
@@ -6,6 +6,8 @@
<title>com.sun.j3d.utils.compression</title>
</head>
<body>
-<p>Provides geometry compression utility classes.</p>
+<p><i><b>Deprecated</b>: Use <code><a
+ href="../geometry/compression/package-summary.html">com.sun.j3d.utils.geometry.compression</a></code>
+instead.</i></p>
</body>
</html>
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/CommandStream.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/CommandStream.java
new file mode 100644
index 0000000..1f94a0d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/CommandStream.java
@@ -0,0 +1,265 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+/**
+ * This class is used to build the bit-level compression command stream which
+ * is the final result of the compression process. It defines the bit
+ * representations of the compression commands and provides a mechanism for
+ * the interleaving and forwarding of command headers and bodies required by
+ * the geometry compression specification.
+ */
+class CommandStream {
+ // Geometry compression commands.
+ static final int SET_NORM = 0xC0 ;
+ static final int SET_COLOR = 0x80 ;
+ static final int VERTEX = 0x40 ;
+ static final int MESH_B_R = 0x20 ;
+ static final int SET_STATE = 0x18 ;
+ static final int SET_TABLE = 0x10 ;
+ static final int V_NO_OP = 0x01 ;
+
+ // Huffman table indices.
+ static final int POSITION_TABLE = 0 ;
+ static final int COLOR_TABLE = 1 ;
+ static final int NORMAL_TABLE = 2 ;
+
+ // The buffer of compressed data and the current offset.
+ private byte bytes[] ;
+ private int byteOffset ;
+ private int bitOffset ;
+
+ // Last command body for header forwarding.
+ private long lastBody ;
+ private int lastBodyLength ;
+
+ /**
+ * Create an empty CommandStream with a default initial size.
+ */
+ CommandStream() {
+ this(65536) ;
+ }
+
+ /**
+ * Create an empty CommandStream with the given initial size.
+ *
+ * @param initSize initial capacity of CommandStream in bytes
+ */
+ CommandStream(int initSize) {
+ bytes = new byte[initSize] ;
+ clear() ;
+ }
+
+ /**
+ * Mark the CommandStream as empty so that its storage will be reused.
+ */
+ void clear() {
+ // Initialize the first byte to 0.
+ // Subsequent bytes are cleared as they are written.
+ bytes[0] = 0 ;
+
+ // Reset the number of valid bits.
+ bitOffset = 0 ;
+ byteOffset = 0 ;
+
+ // The first command header is always followed by the body of an
+ // implicit variable length no-op to start the header-forwarding
+ // interleave required by hardware decompressor implementations. The
+ // only necessary bits are 5 bits of length set to zeros to indicate a
+ // fill of zero length.
+ lastBody = 0 ;
+ lastBodyLength = 5 ;
+ }
+
+ /**
+ * Add a compression command to this instance.<p>
+ *
+ * A compression command includes an 8-bit header and can range up to 72
+ * bits in length. The command with the maximum length is a 2-bit color
+ * command with a 6-bit tag in the header, followed by four 16-bit color
+ * components of data.<p>
+ *
+ * A subcommand is either a position, normal, or color, though in practice
+ * a position subcommand can only be part of a vertex command. Normal and
+ * color subcommands can be parts of separate global normal and color
+ * commands as well as parts of a vertex command.<p>
+ *
+ * A subcommand includes a 6-bit header. Its length is 2 bits less than
+ * the length of the corresponding command.
+ *
+ * @param header contains compression command header bits, right-justified
+ * within the bits of the int
+ * @param headerLength number of bits in header, either 8 for commands or
+ * 6 for subcommands
+ * @param body contains the body of the compression command,
+ * right-justified within the bits of the long
+ * @param bodyLength number of bits in the body
+ */
+ void addCommand(int header, int headerLength, long body, int bodyLength) {
+ addByte(header, headerLength) ;
+ addLong(lastBody, lastBodyLength) ;
+
+ lastBody = body ;
+ lastBodyLength = bodyLength ;
+ }
+
+ //
+ // Add the rightmost bitCount bits of b to the end of the command stream.
+ //
+ private void addByte(int b, int bitCount) {
+ int bitsEmpty = 8 - bitOffset ;
+ b &= (int)CompressionStreamElement.lengthMask[bitCount] ;
+
+ if (bitCount <= bitsEmpty) {
+ bytes[byteOffset] |= (b << (bitsEmpty - bitCount)) ;
+ bitOffset += bitCount ;
+ return ;
+ }
+
+ if (bytes.length == byteOffset + 1) {
+ byte newBytes[] = new byte[bytes.length * 2] ;
+ System.arraycopy(bytes, 0, newBytes, 0, bytes.length) ;
+ bytes = newBytes ;
+ }
+
+ bitOffset = bitCount - bitsEmpty ;
+ bytes[byteOffset] |= (b >>> bitOffset) ;
+
+ byteOffset++ ;
+ bytes[byteOffset] = (byte)(b << (8 - bitOffset)) ;
+ }
+
+ //
+ // Add the rightmost bitCount bits of l to the end of the command stream.
+ //
+ private void addLong(long l, int bitCount) {
+ int byteCount = bitCount / 8 ;
+ int excessBits = bitCount - byteCount * 8 ;
+
+ if (excessBits > 0)
+ addByte((int)(l >>> (byteCount * 8)), excessBits) ;
+
+ while (byteCount > 0) {
+ addByte((int)((l >>> ((byteCount - 1) * 8)) & 0xff), 8) ;
+ byteCount-- ;
+ }
+ }
+
+ /**
+ * Add a no-op and the last command body. Pad out with additional no-ops
+ * to a 64-bit boundary if necessary. A call to this method is required
+ * in order to create a valid compression command stream.
+ */
+ void end() {
+ int excessBytes, padBits ;
+
+ // Add the 1st no-op and the last body.
+ addByte(V_NO_OP, 8) ;
+ addLong(lastBody, lastBodyLength) ;
+
+ excessBytes = (byteOffset + 1) % 8 ;
+ if (excessBytes == 0 && bitOffset == 8)
+ // No padding necessary.
+ return ;
+
+ // Need to add padding with a 2nd no-op.
+ addByte(V_NO_OP, 8) ;
+ excessBytes = (byteOffset + 1) % 8 ;
+
+ if (excessBytes == 0)
+ padBits = 8 - bitOffset ;
+ else {
+ int fillBytes = 8 - excessBytes ;
+ padBits = (8 * fillBytes) + (8 - bitOffset) ;
+ }
+
+ // The minimum length for a no-op command body is 5 bits.
+ if (padBits < 5)
+ // Have to cross the next 64-bit boundary.
+ padBits += 64 ;
+
+ // The maximum length of a no-op body is a 5-bit length + 31 bits of
+ // fill for a total of 36.
+ if (padBits < 37) {
+ // Pad with the body of the 1st no-op.
+ addLong((padBits - 5) << (padBits - 5), padBits) ;
+ return ;
+ }
+
+ // The number of bits to pad at this point is [37..68]. Knock off 24
+ // bits with the body of the 1st no-op to reduce the number of pad
+ // bits to [13..44], which can be filled with 1 more no-op.
+ addLong(19 << 19, 24) ;
+ padBits -= 24 ;
+
+ // Add a 3rd no-op.
+ addByte(V_NO_OP, 8) ;
+ padBits -= 8 ;
+
+ // Complete padding with the body of the 2nd no-op.
+ addLong((padBits - 5) << (padBits - 5), padBits) ;
+ }
+
+ /**
+ * Get the number of bytes in the compression command stream.
+ *
+ * @return size of compressed data in bytes
+ */
+ int getByteCount() {
+ if (byteOffset + bitOffset == 0)
+ return 0 ;
+ else
+ return byteOffset + 1 ;
+ }
+
+ /**
+ * Get the bytes composing the compression command stream.
+ *
+ * @return reference to array of bytes containing the compressed data
+ */
+ byte[] getBytes() {
+ return bytes ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryData.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryData.java
new file mode 100644
index 0000000..f8a8679
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryData.java
@@ -0,0 +1,546 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import com.sun.j3d.internal.J3dUtilsI18N;
+import javax.media.j3d.J3DBuffer;
+import javax.media.j3d.Shape3D;
+import javax.vecmath.Point3d;
+
+/**
+ * The compressed geometry object is used to store geometry in a
+ * compressed format. Using compressed geometry may increase the speed
+ * objects can be sent over the network. Note that the geometry will
+ * be decompressed in memory, so the application will not see any
+ * memory savings.
+ * <p>
+ * Compressed geometry may be passed to this CompressedGeometryData object
+ * in one of two ways: by copying the data into this object using the
+ * existing constructor, or by passing a reference to the data.
+ * <p>
+ * <ul>
+ * <li>
+ * <b>By Copying:</b>
+ * In by-copy mode, the CompressedGeometryData constructor copies the buffer of
+ * compressed geometry data into this CompressedGeometryData object. This
+ * is appropriate for many applications, and allows Java 3D to verify
+ * the data once and then not worry about it again.
+ * </li>
+ * <li><b>By Reference:</b>
+ * In by-reference mode, the
+ * compressed geometry data is accessed by reference, directly from
+ * the user's array. To use this feature, you need to construct a
+ * CompressedGeometryData object with the <code>byReference</code> flag
+ * set to <code>true</code>. In this mode, a reference to the input
+ * data is saved, but the data itself is not necessarily copied. Note
+ * that the compressed geometry header is still copied into this
+ * compressed geometry object. Data referenced by a
+ * CompressedGeometryData object must not be modified after the
+ * CompressedGeometryData object is constructed.
+ * Applications
+ * must exercise care not to violate this rule. If any referenced
+ * compressed geometry data is modified after construction,
+ * the results are undefined.
+ * </li>
+ * </ul>
+ *
+ * @since Java 3D 1.5
+ */
+public class CompressedGeometryData extends Object {
+
+ private Header cgHeader;
+ private CompressedGeometryRetained retained;
+
+
+ /**
+ * Creates a new CompressedGeometryData object by copying
+ * the specified compressed geometry data into this object.
+ * If the version number of compressed geometry, as specified by
+ * the Header, is incompatible with the
+ * supported version of compressed geometry, then an exception
+ * will be thrown.
+ *
+ * @param hdr the compressed geometry header. This is copied
+ * into this CompressedGeometryData object.
+ *
+ * @param compressedGeometry the compressed geometry data. The
+ * geometry must conform to the format described in Appendix B of
+ * the <i>Java 3D API Specification</i>.
+ *
+ * @exception IllegalArgumentException if a problem is detected with the
+ * header.
+ */
+ public CompressedGeometryData(Header hdr,
+ byte[] compressedGeometry) {
+
+ this(hdr, compressedGeometry, false);
+ }
+
+ /**
+ * Creates a new CompressedGeometryData object. The
+ * specified compressed geometry data is either copied into this
+ * object or is accessed by reference.
+ * If the version number of compressed geometry, as specified by
+ * the Header, is incompatible with the
+ * supported version of compressed geometry, then an exception
+ * will be thrown.
+ *
+ * @param hdr the compressed geometry header. This is copied
+ * into the CompressedGeometryData object.
+ *
+ * @param compressedGeometry the compressed geometry data. The
+ * geometry must conform to the format described in Appendix B of
+ * the <i>Java 3D API Specification</i>.
+ *
+ * @param byReference a flag that indicates whether the data is copied
+ * into this compressed geometry object or is accessed by reference.
+ *
+ * @exception IllegalArgumentException if a problem is detected with the
+ * header.
+ */
+ public CompressedGeometryData(Header hdr,
+ byte[] compressedGeometry,
+ boolean byReference) {
+
+ if ((hdr.size + hdr.start) > compressedGeometry.length) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("CompressedGeometry0"));
+ }
+
+ // Create a separate copy of the given header.
+ cgHeader = new Header();
+ hdr.copy(cgHeader);
+
+ // Create the retained object.
+ retained = new CompressedGeometryRetained();
+ this.retained.createCompressedGeometry(cgHeader, compressedGeometry, byReference);
+
+ // This constructor is designed to accept byte arrays that may contain
+ // possibly many large compressed geometry blocks interspersed with
+ // non-J3D-specific metadata. Only one of these blocks is used per
+ // CompressedGeometry object, so set the geometry offset to zero in
+ // the header if the data itself is copied.
+ if (!byReference)
+ cgHeader.start = 0;
+ }
+
+ /**
+ * Creates a new CompressedGeometryData object. The
+ * specified compressed geometry data is accessed by reference
+ * from the specified buffer.
+ * If the version number of compressed geometry, as specified by
+ * the Header, is incompatible with the
+ * supported version of compressed geometry, then an exception
+ * will be thrown.
+ *
+ * @param hdr the compressed geometry header. This is copied
+ * into the CompressedGeometryData object.
+ *
+ * @param compressedGeometry a buffer containing an NIO byte buffer
+ * of compressed geometry data. The
+ * geometry must conform to the format described in Appendix B of
+ * the <i>Java 3D API Specification</i>.
+ *
+ * @exception UnsupportedOperationException this method is not
+ * yet implemented
+ *
+ * @exception IllegalArgumentException if a problem is detected with the
+ * header,
+ * or if the java.nio.Buffer contained in the specified J3DBuffer
+ * is not a java.nio.ByteBuffer object.
+ *
+ * @see Header
+ */
+ public CompressedGeometryData(Header hdr,
+ J3DBuffer compressedGeometry) {
+
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+
+ /**
+ * Returns the size, in bytes, of the compressed geometry buffer.
+ * The size of the compressed geometry header is not included.
+ *
+ * @return the size, in bytes, of the compressed geometry buffer.
+ */
+ public int getByteCount() {
+ return cgHeader.size;
+ }
+
+ /**
+ * Copies the compressed geometry header from the CompressedGeometryData
+ * object into the passed in parameter.
+ *
+ * @param hdr the Header object into which to copy the
+ * CompressedGeometryData object's header; the offset field may differ
+ * from that which was originally specified if a copy of the original
+ * compressed geometry byte array was created.
+ */
+ public void getCompressedGeometryHeader(Header hdr) {
+ cgHeader.copy(hdr);
+ }
+
+ /**
+ * Retrieves the compressed geometry associated with the
+ * CompressedGeometryData object. Copies the compressed
+ * geometry from the CompressedGeometryData node into the given array.
+ * The array must be large enough to hold all of the bytes.
+ * The individual array elements must be allocated by the caller.
+ *
+ * @param compressedGeometry the array into which to copy the compressed
+ * geometry.
+ *
+ * @exception IllegalStateException if the data access mode for this
+ * object is by-reference.
+ *
+ * @exception ArrayIndexOutOfBoundsException if compressedGeometry byte
+ * array is not large enough to receive the compressed geometry
+ */
+ public void getCompressedGeometry(byte[] compressedGeometry) {
+ if (isByReference()) {
+ throw new IllegalStateException(
+ J3dUtilsI18N.getString("CompressedGeometry7"));
+ }
+
+ if (cgHeader.size > compressedGeometry.length) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dUtilsI18N.getString("CompressedGeometry4"));
+ }
+
+ this.retained.copy(compressedGeometry);
+ }
+
+ /**
+ * Decompresses the compressed geometry. Returns an array of Shape nodes
+ * containing the decompressed geometry objects, or null if the version
+ * number of the compressed geometry is incompatible with the decompressor
+ * in the current version of Java 3D.
+ *
+ * @return an array of Shape nodes containing the
+ * geometry decompressed from this CompressedGeometryData
+ * object, or null if its version is incompatible
+ */
+ public Shape3D[] decompress() {
+ CompressedGeometryRetained cgr = this.retained;
+
+ GeometryDecompressorShape3D decompressor =
+ new GeometryDecompressorShape3D();
+
+ // Decompress the geometry as TriangleStripArrays. A combination of
+ // TriangleStripArrays and TrianglesFanArrays is more compact but
+ // requires twice as many Shape3D objects, resulting in slower
+ // rendering performance.
+ //
+ // Using TriangleArray output is currently the fastest, given the
+ // strip sizes observed from various compressed geometry objects, but
+ // produces about twice as many vertices. TriangleStripArray produces
+ // the same number of Shape3D objects as TriangleArray using 1/2
+ // to 2/3 of the vertices, with only a marginal performance penalty.
+ //
+ return decompressor.toTriangleStripArrays(cgr);
+ }
+
+
+ /**
+ * Retrieves the data access mode for this CompressedGeometryData object.
+ *
+ * @return <code>true</code> if the data access mode for this
+ * CompressedGeometryData object is by-reference;
+ * <code>false</code> if the data access mode is by-copying.
+ */
+ public boolean isByReference() {
+ return this.retained.isByReference();
+ }
+
+
+ /**
+ * Gets the compressed geometry data reference.
+ *
+ * @return the current compressed geometry data reference.
+ *
+ * @exception IllegalStateException if the data access mode for this
+ * object is not by-reference.
+ */
+ public byte[] getCompressedGeometryRef() {
+ if (!isByReference()) {
+ throw new IllegalStateException(
+ J3dUtilsI18N.getString("CompressedGeometry8"));
+ }
+
+ return this.retained.getReference();
+ }
+
+
+ /**
+ * Gets the compressed geometry data buffer reference, which is
+ * always null since NIO buffers are not supported for
+ * CompressedGeometryData objects.
+ *
+ * @return null
+ */
+ public J3DBuffer getCompressedGeometryBuffer() {
+ return null;
+ }
+
+
+ /**
+ * The Header class is a data container for the header information,
+ * used in conjunction with a CompressedGeometryData object.
+ * This information is used to aid the decompression of the compressed geometry.
+ *
+ * <p>
+ * All instance data is declared public and no get or set methods are
+ * provided.
+ *
+ * @since Java 3D 1.5
+ */
+ public static class Header extends Object {
+
+ /**
+ * bufferType: compressed geometry is made up of individual points.
+ */
+ public static final int POINT_BUFFER = 0;
+
+ /**
+ * bufferType: compressed geometry is made up of line segments.
+ */
+ public static final int LINE_BUFFER = 1;
+
+ /**
+ * bufferType: compressed geometry is made up of triangles.
+ */
+ public static final int TRIANGLE_BUFFER = 2;
+
+ // Valid values for the bufferDataPresent field.
+
+ /**
+ * bufferDataPresent: bit indicating that normal information is
+ * bundled with the vertices in the compressed geometry buffer.
+ */
+ public static final int NORMAL_IN_BUFFER = 1;
+
+ /**
+ * bufferDataPresent: bit indicating that RGB color information is
+ * bundled with the vertices in the compressed geometry buffer.
+ */
+ public static final int COLOR_IN_BUFFER = 2;
+
+ /**
+ * bufferDataPresent: bit indicating that alpha information is
+ * bundled with the vertices in the compressed geometry buffer.
+ */
+ public static final int ALPHA_IN_BUFFER = 4;
+
+ /**
+ * The major version number for the compressed geometry format that
+ * was used to compress the geometry.
+ * If the version number of compressed geometry is incompatible
+ * with the supported version of compressed geometry in the
+ * current version of Java 3D, the compressed geometry obejct will
+ * not be rendered.
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public int majorVersionNumber;
+
+ /**
+ * The minor version number for the compressed geometry format that
+ * was used to compress the geometry.
+ * If the version number of compressed geometry is incompatible
+ * with the supported version of compressed geometry in the
+ * current version of Java 3D, the compressed geometry obejct will
+ * not be rendered.
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public int minorVersionNumber;
+
+ /**
+ * The minor-minor version number for the compressed geometry format
+ * that was used to compress the geometry.
+ * If the version number of compressed geometry is incompatible
+ * with the supported version of compressed geometry in the
+ * current version of Java 3D, the compressed geometry obejct will
+ * not be rendered.
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public int minorMinorVersionNumber;
+
+ /**
+ * Describes the type of data in the compressed geometry buffer.
+ * Only one type may be present in any given compressed geometry
+ * buffer.
+ */
+ public int bufferType;
+
+ /**
+ * Contains bits indicating what data is bundled with the vertices in the
+ * compressed geometry buffer. If this data is not present (e.g. color)
+ * then this info will be inherited from the Appearance node.
+ */
+ public int bufferDataPresent;
+
+ /**
+ * Size of the compressed geometry in bytes.
+ */
+ public int size;
+
+ /**
+ * Offset in bytes of the start of the compressed geometry from the
+ * beginning of the compressed geometry byte array passed to the
+ * CompressedGeometryData constructor. <p>
+ *
+ * If the CompressedGeometryData is created with reference access semantics,
+ * then this allow external compressors or file readers to embed several
+ * blocks of compressed geometry in a single large byte array, possibly
+ * interspersed with metadata that is not specific to Java 3D, without
+ * having to copy each block to a separate byte array. <p>
+ *
+ * If the CompressedGeometryData is created with copy access semantics, then
+ * <code>size</code> bytes of compressed geometry data are copied from the
+ * offset indicated by <code>start</code> instead of copying the entire
+ * byte array. The getCompressedGeometry() method will return only the
+ * bytes used to construct the object, and the getCompressedGeometryHeader()
+ * method will return a header with the <code>start</code> field set to 0.
+ */
+ public int start;
+
+ /**
+ * A point that defines the lower bound of the <i>x</i>,
+ * <i>y</i>, and <i>z</i> components for all positions in the
+ * compressed geometry buffer. If null, a lower bound of
+ * (-1,-1,-1) is assumed. Java 3D will use this information to
+ * construct a bounding box around compressed geometry objects
+ * that are used in nodes for which the auto compute bounds flag
+ * is true. The default value for this point is null.
+ */
+ public Point3d lowerBound = null;
+
+ /**
+ * A point that defines the upper bound of the <i>x</i>,
+ * <i>y</i>, and <i>z</i> components for all positions in the
+ * compressed geometry buffer. If null, an upper bound of (1,1,1)
+ * is assumed. Java 3D will use this information to construct a
+ * bounding box around compressed geometry objects that are used
+ * in nodes for which the auto compute bounds flag is true. The
+ * default value for this point is null.
+ */
+ public Point3d upperBound = null;
+
+ /**
+ * Creates a new Header object used for the
+ * creation of a CompressedGeometryData object.
+ * All instance data is declared public and no get or set methods are
+ * provided. All values are set to 0 by default and must be filled
+ * in by the application.
+ *
+ * @see CompressedGeometryData
+ */
+ public Header() {
+ }
+
+ /**
+ * Package-scoped method to copy current Header object
+ * to the passed-in Header object.
+ *
+ * @param hdr the Header object into which to copy the
+ * current Header.
+ */
+ void copy(Header hdr) {
+ hdr.majorVersionNumber = this.majorVersionNumber;
+ hdr.minorVersionNumber = this.minorVersionNumber;
+ hdr.minorMinorVersionNumber = this.minorMinorVersionNumber;
+ hdr.bufferType = this.bufferType;
+ hdr.bufferDataPresent = this.bufferDataPresent;
+ hdr.size = this.size;
+ hdr.start = this.start;
+ hdr.lowerBound = this.lowerBound;
+ hdr.upperBound = this.upperBound;
+ }
+
+ /**
+ * Returns a String describing the contents of the
+ * Header object.
+ *
+ * @return a String describing contents of the compressed geometry header
+ */
+ public String toString() {
+ String type = "UNKNOWN";
+ switch (bufferType) {
+ case POINT_BUFFER: type = "POINT_BUFFER"; break;
+ case LINE_BUFFER: type = "LINE_BUFFER"; break;
+ case TRIANGLE_BUFFER: type = "TRIANGLE_BUFFER"; break;
+ }
+
+ String data = "";
+ if ((bufferDataPresent & NORMAL_IN_BUFFER) != 0)
+ data = data + "NORMALS ";
+ if ((bufferDataPresent & COLOR_IN_BUFFER) != 0)
+ data = data + "COLORS ";
+ if ((bufferDataPresent & ALPHA_IN_BUFFER) != 0)
+ data = data + "ALPHA ";
+
+ String lbound = "null";
+ if (lowerBound != null)
+ lbound = lowerBound.toString();
+
+ String ubound = "null";
+ if (upperBound != null)
+ ubound = upperBound.toString();
+
+ return
+ "majorVersionNumber: " + majorVersionNumber + " " +
+ "minorVersionNumber: " + minorVersionNumber + " " +
+ "minorMinorVersionNumber: " + minorMinorVersionNumber + "\n" +
+ "bufferType: " + type + " " +
+ "bufferDataPresent: " + data + "\n" +
+ "size: " + size + " " +
+ "start: " + start + "\n" +
+ "lower bound: " + lbound + "\n" +
+ "upper bound: " + ubound + " ";
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryFile.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryFile.java
new file mode 100644
index 0000000..075f3cf
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryFile.java
@@ -0,0 +1,1011 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+//
+// The compressed geometry file format supported by this class has a 32
+// byte header followed by multiple compressed geometry objects.
+//
+// Each object consists of a block of compressed data and an 8-byte
+// individual block header describing its contents.
+//
+// The file ends with a directory data structure used for random access,
+// containing a 64-bit offset for each object in the order in which it
+// appears in the file. This is also used to find the size of the largest
+// object in the file and must be present.
+//
+
+/**
+ * This class provides methods to read and write compressed geometry resource
+ * files. These files usually end with the .cg extension and support
+ * sequential as well as random access to multiple compressed geometry
+ * objects.
+ *
+ * @since Java 3D 1.5
+ */
+public class CompressedGeometryFile {
+ private static final boolean print = false ;
+ private static final boolean benchmark = false ;
+
+ /**
+ * The magic number which identifies the compressed geometry file type.
+ */
+ static final int MAGIC_NUMBER = 0xbaddfab4 ;
+
+ /**
+ * Byte offset of the magic number from start of file.
+ */
+ static final int MAGIC_NUMBER_OFFSET = 0 ;
+
+ /**
+ * Byte offset of the major version number from start of file.
+ */
+ static final int MAJOR_VERSION_OFFSET = 4 ;
+
+ /**
+ * Byte offset of the minor version number from start of file.
+ */
+ static final int MINOR_VERSION_OFFSET = 8 ;
+
+ /**
+ * Byte offset of the minor minor version number from start of file.
+ */
+ static final int MINOR_MINOR_VERSION_OFFSET = 12 ;
+
+ /**
+ * Byte offset of the number of objects from start of file.
+ */
+ static final int OBJECT_COUNT_OFFSET = 16 ;
+
+ /**
+ * Byte offset of the directory offset from start of file.
+ * This offset is long word aligned since the directory offset is a long.
+ */
+ static final int DIRECTORY_OFFSET_OFFSET = 24 ;
+
+ /**
+ * File header total size in bytes.
+ */
+ static final int HEADER_SIZE = 32 ;
+
+ /**
+ * Byte offset of the object size from start of individual compressed
+ * geometry block.
+ */
+ static final int OBJECT_SIZE_OFFSET = 0 ;
+
+ /**
+ * Byte offset of the compressed geometry data descriptor from start of
+ * individual compressed geometry block.
+ */
+ static final int GEOM_DATA_OFFSET = 4 ;
+
+ /**
+ * Bits in compressed geometry data descriptor which encode the buffer type.
+ */
+ static final int TYPE_MASK = 0x03 ;
+
+ /**
+ * Bit in compressed geometry data descriptor encoding presence of normals.
+ */
+ static final int NORMAL_PRESENT_MASK = 0x04 ;
+
+ /**
+ * Bit in compressed geometry data descriptor encoding presence of colors.
+ */
+ static final int COLOR_PRESENT_MASK = 0x08 ;
+
+ /**
+ * Bit in compressed geometry data descriptor encoding presence of alphas.
+ */
+ static final int ALPHA_PRESENT_MASK = 0x10 ;
+
+ /**
+ * Value in compressed geometry data descriptor for a point buffer type.
+ */
+ static final int TYPE_POINT = 1 ;
+
+ /**
+ * Value in compressed geometry data descriptor for a line buffer type.
+ */
+ static final int TYPE_LINE = 2 ;
+
+ /**
+ * Value in compressed geometry data descriptor for a triangle buffer type.
+ */
+ static final int TYPE_TRIANGLE = 3 ;
+
+ /**
+ * Block header total size in bytes.
+ */
+ static final int BLOCK_HEADER_SIZE = 8 ;
+
+ // The name of the compressed geometry resource file.
+ String fileName = null ;
+
+ // The major, minor, and subminor version number of the most recent
+ // compressor used to compress any of the objects in the compressed
+ // geometry resource file.
+ int majorVersionNumber ;
+ int minorVersionNumber ;
+ int minorMinorVersionNumber ;
+
+ // The number of objects in the compressed geometry resource file.
+ int objectCount ;
+
+ // The index of the current object in the file.
+ int objectIndex = 0 ;
+
+ // The random access file associated with this instance.
+ RandomAccessFile cgFile = null ;
+
+ // The magic number identifying the file type.
+ int magicNumber ;
+
+ // These fields are set from each individual block of compressed geometry.
+ byte cgBuffer[] ;
+ int geomSize ;
+ int geomStart ;
+ int geomDataType ;
+
+ // The directory of object offsets is read from the end of the file.
+ long directory[] ;
+ long directoryOffset ;
+
+ // The object sizes are computed from the directory offsets. These are
+ // used to allocate a buffer large enough to hold the largest object and
+ // to determine how many consecutive objects can be read into that buffer.
+ int objectSizes[] ;
+ int bufferObjectStart ;
+ int bufferObjectCount ;
+ int bufferNextObjectCount ;
+ int bufferNextObjectOffset ;
+
+ // The shared compressed geometry header object.
+ CompressedGeometryData.Header cgh ;
+
+ // Flag indicating file update.
+ boolean fileUpdate = false ;
+
+ /**
+ * Construct a new CompressedGeometryFile instance associated with the
+ * specified file. An attempt is made to open the file with read-only
+ * access; if this fails then a FileNotFoundException is thrown.
+ *
+ * @param file path to the compressed geometry resource file
+ * @exception FileNotFoundException if file doesn't exist or
+ * cannot be read
+ * @exception IllegalArgumentException if the file is not a compressed
+ * geometry resource file
+ * @exception IOException if there is a header or directory read error
+ */
+ public CompressedGeometryFile(String file) throws IOException {
+ this(file, false) ;
+ }
+
+ /**
+ * Construct a new CompressedGeometryFile instance associated with the
+ * specified file.
+ *
+ * @param file path to the compressed geometry resource file
+ * @param rw if true, opens the file for read and write access or attempts
+ * to create one if it doesn't exist; if false, opens the file with
+ * read-only access
+ * @exception FileNotFoundException if file doesn't exist or
+ * access permissions disallow access
+ * @exception IllegalArgumentException if the file is not a compressed
+ * geometry resource file
+ * @exception IOException if there is a header or directory read error
+ */
+ public CompressedGeometryFile(String file, boolean rw) throws IOException {
+ // Open the file and read the file header.
+ open(file, rw) ;
+
+ // Copy the file name.
+ fileName = new String(file) ;
+
+ // Set up the file fields.
+ initialize() ;
+ }
+
+ /**
+ * Construct a new CompressedGeometryFile instance associated with a
+ * currently open RandomAccessFile.
+ *
+ * @param file currently open RandomAccessFile
+ * @exception IllegalArgumentException if the file is not a compressed
+ * geometry resource file
+ * @exception IOException if there is a header or directory read error
+ */
+ public CompressedGeometryFile(RandomAccessFile file) throws IOException {
+ // Copy the file reference.
+ cgFile = file ;
+
+ // Set up the file fields.
+ initialize() ;
+ }
+
+ /**
+ * Delete all compressed objects from this instance. This method may only
+ * be called after successfully creating a CompressedGeometryFile instance
+ * with read-write access, so a corrupted or otherwise invalid resource
+ * must be removed manually before it can be rewritten. The close()
+ * method must be called sometime after invoking clear() in order to write
+ * out the new directory structure.
+ *
+ * @exception IOException if clear fails
+ */
+ public void clear() throws IOException {
+ // Truncate the file.
+ cgFile.setLength(0) ;
+
+ // Set up the file fields.
+ initialize() ;
+ }
+
+ /**
+ * Return a string containing the file name associated with this instance
+ * or null if there is none.
+ *
+ * @return file name associated with this instance or null if there is
+ * none
+ */
+ public String getFileName() {
+ return fileName ;
+ }
+
+ /**
+ * Return the major version number of the most recent compressor used to
+ * compress any of the objects in this instance.
+ *
+ * @return major version number
+ */
+ public int getMajorVersionNumber() {
+ return majorVersionNumber ;
+ }
+
+ /**
+ * Return the minor version number of the most recent compressor used to
+ * compress any of the objects in this instance.
+ *
+ * @return minor version number
+ */
+ public int getMinorVersionNumber() {
+ return minorVersionNumber ;
+ }
+
+ /**
+ * Return the subminor version number of the most recent compressor used to
+ * compress any of the objects in this instance.
+ *
+ * @return subminor version number
+ */
+ public int getMinorMinorVersionNumber() {
+ return minorMinorVersionNumber ;
+ }
+
+ /**
+ * Return the number of compressed objects in this instance.
+ *
+ * @return number of compressed objects
+ */
+ public int getObjectCount() {
+ return objectCount ;
+ }
+
+ /**
+ * Return the current object index associated with this instance. This is
+ * the index of the object that would be returned by an immediately
+ * following call to the readNext() method. Its initial value is 0; -1
+ * is returned if the last object has been read.
+ *
+ * @return current object index, or -1 if at end
+ */
+ public int getCurrentIndex() {
+ if (objectIndex == objectCount)
+ return -1 ;
+ else
+ return objectIndex ;
+ }
+
+ /**
+ * Read the next compressed geometry object in the instance. This is
+ * initially the first object (index 0) in the instance; otherwise, it is
+ * whatever object is next after the last one read. The current object
+ * index is incremented by 1 after the read. When the last object is read
+ * the index becomes invalid and an immediately subsequent call to
+ * readNext() returns null.
+ *
+ *
+ * @return a CompressedGeometryData node component, or null if the last object
+ * has been read
+ * @exception IOException if read fails
+ */
+ public CompressedGeometryData readNext() throws IOException {
+ return readNext(cgBuffer.length) ;
+ }
+
+ /**
+ * Read all compressed geometry objects contained in the instance. The
+ * current object index becomes invalid; an immediately following call
+ * to readNext() will return null.
+ *
+ * @return an array of CompressedGeometryData node components.
+ * @exception IOException if read fails
+ */
+ public CompressedGeometryData[] read() throws IOException {
+ long startTime = 0 ;
+ CompressedGeometryData cg[] = new CompressedGeometryData[objectCount] ;
+
+ if (benchmark)
+ startTime = System.currentTimeMillis() ;
+
+ objectIndex = 0 ;
+ setFilePointer(directory[0]) ;
+ bufferNextObjectCount = 0 ;
+
+ for (int i = 0 ; i < objectCount ; i++)
+ cg[i] = readNext(cgBuffer.length) ;
+
+ if (benchmark) {
+ long t = System.currentTimeMillis() - startTime ;
+ System.out.println("read " + objectCount +
+ " objects " + cgFile.length() +
+ " bytes in " + (t/1000f) + " sec.") ;
+ System.out.println((cgFile.length()/(float)t) + " Kbytes/sec.") ;
+ }
+
+ return cg ;
+ }
+
+ /**
+ * Read the compressed geometry object at the specified index. The
+ * current object index is set to the subsequent object unless the last
+ * object has been read, in which case the index becomes invalid and an
+ * immediately following call to readNext() will return null.
+ *
+ * @param index compressed geometry object to read
+ * @return a CompressedGeometryData node component
+ * @exception IndexOutOfBoundsException if object index is
+ * out of range
+ * @exception IOException if read fails
+ */
+ public CompressedGeometryData read(int index) throws IOException {
+ objectIndex = index ;
+
+ if (objectIndex < 0) {
+ throw new IndexOutOfBoundsException
+ ("\nobject index must be >= 0") ;
+ }
+ if (objectIndex >= objectCount) {
+ throw new IndexOutOfBoundsException
+ ("\nobject index must be < " + objectCount) ;
+ }
+
+ // Check if object is in cache.
+ if ((objectIndex >= bufferObjectStart) &&
+ (objectIndex < bufferObjectStart + bufferObjectCount)) {
+ if (print) System.out.println("\ngetting object from cache\n") ;
+
+ bufferNextObjectOffset = (int)
+ (directory[objectIndex] - directory[bufferObjectStart]) ;
+
+ bufferNextObjectCount =
+ bufferObjectCount - (objectIndex - bufferObjectStart) ;
+
+ return readNext() ;
+
+ } else {
+ // Move file pointer to correct offset.
+ setFilePointer(directory[objectIndex]) ;
+
+ // Force a read from current offset. Disable cache read-ahead
+ // since cache hits are unlikely with random access.
+ bufferNextObjectCount = 0 ;
+ return readNext(objectSizes[objectIndex]) ;
+ }
+ }
+
+
+ /**
+ * Add a compressed geometry node component to the end of the instance.
+ * The current object index becomes invalid; an immediately following call
+ * to readNext() will return null. The close() method must be called at
+ * some later time in order to create a valid compressed geometry file.
+ *
+ * @param cg a compressed geometry node component
+ * @exception CapabilityNotSetException if unable to get compressed
+ * geometry data from the node component
+ * @exception IOException if write fails
+ */
+ public void write(CompressedGeometryData cg) throws IOException {
+ CompressedGeometryData.Header cgh = new CompressedGeometryData.Header() ;
+ cg.getCompressedGeometryHeader(cgh) ;
+
+ // Update the read/write buffer size if necessary.
+ if (cgh.size + BLOCK_HEADER_SIZE > cgBuffer.length) {
+ cgBuffer = new byte[cgh.size + BLOCK_HEADER_SIZE] ;
+ if (print) System.out.println("\ncgBuffer: reallocated " +
+ (cgh.size+BLOCK_HEADER_SIZE) +
+ " bytes") ;
+ }
+
+ cg.getCompressedGeometry(cgBuffer) ;
+ write(cgh, cgBuffer) ;
+ }
+
+ /**
+ * Add a buffer of compressed geometry data to the end of the
+ * resource. The current object index becomes invalid; an immediately
+ * following call to readNext() will return null. The close() method must
+ * be called at some later time in order to create a valid compressed
+ * geometry file.
+ *
+ * @param cgh a CompressedGeometryData.Header object describing the data.
+ * @param geometry the compressed geometry data
+ * @exception IOException if write fails
+ */
+ public void write(CompressedGeometryData.Header cgh, byte geometry[])
+ throws IOException {
+
+ // Update the read/write buffer size if necessary. It won't be used
+ // in this method, but should be big enough to read any object in
+ // the file, including the one to be written.
+ if (cgh.size + BLOCK_HEADER_SIZE > cgBuffer.length) {
+ cgBuffer = new byte[cgh.size + BLOCK_HEADER_SIZE] ;
+ if (print) System.out.println("\ncgBuffer: reallocated " +
+ (cgh.size+BLOCK_HEADER_SIZE) +
+ " bytes") ;
+ }
+
+ // Assuming backward compatibility, the version number of the file
+ // should be the maximum of all individual compressed object versions.
+ if ((cgh.majorVersionNumber > majorVersionNumber)
+ ||
+ ((cgh.majorVersionNumber == majorVersionNumber) &&
+ (cgh.minorVersionNumber > minorVersionNumber))
+ ||
+ ((cgh.majorVersionNumber == majorVersionNumber) &&
+ (cgh.minorVersionNumber == minorVersionNumber) &&
+ (cgh.minorMinorVersionNumber > minorMinorVersionNumber))) {
+
+ majorVersionNumber = cgh.majorVersionNumber ;
+ minorVersionNumber = cgh.minorVersionNumber ;
+ minorMinorVersionNumber = cgh.minorMinorVersionNumber ;
+
+ this.cgh.majorVersionNumber = cgh.majorVersionNumber ;
+ this.cgh.minorVersionNumber = cgh.minorVersionNumber ;
+ this.cgh.minorMinorVersionNumber = cgh.minorMinorVersionNumber ;
+ }
+
+ // Get the buffer type and see what vertex components are present.
+ int geomDataType = 0 ;
+
+ switch (cgh.bufferType) {
+ case CompressedGeometryData.Header.POINT_BUFFER:
+ geomDataType = TYPE_POINT ;
+ break ;
+ case CompressedGeometryData.Header.LINE_BUFFER:
+ geomDataType = TYPE_LINE ;
+ break ;
+ case CompressedGeometryData.Header.TRIANGLE_BUFFER:
+ geomDataType = TYPE_TRIANGLE ;
+ break ;
+ }
+
+ if ((cgh.bufferDataPresent &
+ CompressedGeometryData.Header.NORMAL_IN_BUFFER) != 0)
+ geomDataType |= NORMAL_PRESENT_MASK ;
+
+ if ((cgh.bufferDataPresent &
+ CompressedGeometryData.Header.COLOR_IN_BUFFER) != 0)
+ geomDataType |= COLOR_PRESENT_MASK ;
+
+ if ((cgh.bufferDataPresent &
+ CompressedGeometryData.Header.ALPHA_IN_BUFFER) != 0)
+ geomDataType |= ALPHA_PRESENT_MASK ;
+
+ // Allocate new directory and object size arrays if necessary.
+ if (objectCount == directory.length) {
+ long newDirectory[] = new long[2*objectCount] ;
+ int newObjectSizes[] = new int[2*objectCount] ;
+
+ System.arraycopy(directory, 0,
+ newDirectory, 0, objectCount) ;
+ System.arraycopy(objectSizes, 0,
+ newObjectSizes, 0, objectCount) ;
+
+ directory = newDirectory ;
+ objectSizes = newObjectSizes ;
+
+ if (print)
+ System.out.println("\ndirectory and size arrays: reallocated " +
+ (2*objectCount) + " entries") ;
+ }
+
+ // Update directory and object size array.
+ directory[objectCount] = directoryOffset ;
+ objectSizes[objectCount] = cgh.size + BLOCK_HEADER_SIZE ;
+ objectCount++ ;
+
+ // Seek to the directory and overwrite from there.
+ setFilePointer(directoryOffset) ;
+ cgFile.writeInt(cgh.size) ;
+ cgFile.writeInt(geomDataType) ;
+ cgFile.write(geometry, 0, cgh.size) ;
+ if (print)
+ System.out.println("\nwrote " + cgh.size +
+ " byte compressed object to " + fileName +
+ "\nfile offset " + directoryOffset) ;
+
+ // Update the directory offset.
+ directoryOffset += cgh.size + BLOCK_HEADER_SIZE ;
+
+ // Return end-of-file on next read.
+ objectIndex = objectCount ;
+
+ // Flag file update so close() will write out the directory.
+ fileUpdate = true ;
+ }
+
+ /**
+ * Release the resources associated with this instance.
+ * Write out final header and directory if contents were updated.
+ * This method must be called in order to create a valid compressed
+ * geometry resource file if any updates were made.
+ */
+ public void close() {
+ if (cgFile != null) {
+ try {
+ if (fileUpdate) {
+ writeFileDirectory() ;
+ writeFileHeader() ;
+ }
+ cgFile.close() ;
+ }
+ catch (IOException e) {
+ // Don't propagate this exception.
+ System.out.println("\nException: " + e.getMessage()) ;
+ System.out.println("failed to close " + fileName) ;
+ }
+ }
+ cgFile = null ;
+ cgBuffer = null ;
+ directory = null ;
+ objectSizes = null ;
+ }
+
+
+ //
+ // Open the file. Specifying a non-existent file creates a new one if
+ // access permissions allow.
+ //
+ void open(String fname, boolean rw)
+ throws FileNotFoundException, IOException {
+
+ cgFile = null ;
+ String mode ;
+
+ if (rw)
+ mode = "rw" ;
+ else
+ mode = "r" ;
+
+ try {
+ cgFile = new RandomAccessFile(fname, mode) ;
+ if (print) System.out.println("\n" + fname +
+ ": opened mode " + mode) ;
+ }
+ catch (FileNotFoundException e) {
+ // N.B. this exception is also thrown on access permission errors
+ throw new FileNotFoundException(e.getMessage() + "\n" + fname +
+ ": open mode " + mode + " failed") ;
+ }
+ }
+
+ //
+ // Seek to the specified offset in the file.
+ //
+ void setFilePointer(long offset) throws IOException {
+ cgFile.seek(offset) ;
+
+ // Reset number of objects that can be read sequentially from cache.
+ bufferNextObjectCount = 0 ;
+ }
+
+ //
+ // Initialize directory, object size array, read/write buffer, and the
+ // shared compressed geometry header.
+ //
+ void initialize() throws IOException {
+ int maxSize = 0 ;
+
+ if (cgFile.length() == 0) {
+ // New file for writing: allocate nominal initial sizes for arrays.
+ objectCount = 0 ;
+ cgBuffer = new byte[32768] ;
+ directory = new long[16] ;
+ objectSizes = new int[directory.length] ;
+
+ // Set fields as if they have been read.
+ magicNumber = MAGIC_NUMBER ;
+ majorVersionNumber = 1 ;
+ minorVersionNumber = 0 ;
+ minorMinorVersionNumber = 0 ;
+ directoryOffset = HEADER_SIZE ;
+
+ // Write the file header.
+ writeFileHeader() ;
+
+ } else {
+ // Read the file header.
+ readFileHeader() ;
+
+ // Check file type.
+ if (magicNumber != MAGIC_NUMBER) {
+ close() ;
+ throw new IllegalArgumentException
+ ("\n" + fileName + " is not a compressed geometry file") ;
+ }
+
+ // Read the directory and determine object sizes.
+ directory = new long[objectCount] ;
+ readDirectory(directoryOffset, directory) ;
+
+ objectSizes = new int[objectCount] ;
+ for (int i = 0 ; i < objectCount-1 ; i++) {
+ objectSizes[i] = (int)(directory[i+1] - directory[i]) ;
+ if (objectSizes[i] > maxSize) maxSize = objectSizes[i] ;
+ }
+
+ if (objectCount > 0) {
+ objectSizes[objectCount-1] =
+ (int)(directoryOffset - directory[objectCount-1]) ;
+
+ if (objectSizes[objectCount-1] > maxSize)
+ maxSize = objectSizes[objectCount-1] ;
+ }
+
+ // Allocate a buffer big enough to read the largest object.
+ cgBuffer = new byte[maxSize] ;
+
+ // Move to the first object.
+ setFilePointer(HEADER_SIZE) ;
+ }
+
+ // Set up common parts of the compressed geometry object header.
+ cgh = new CompressedGeometryData.Header() ;
+ cgh.majorVersionNumber = this.majorVersionNumber ;
+ cgh.minorVersionNumber = this.minorVersionNumber ;
+ cgh.minorMinorVersionNumber = this.minorMinorVersionNumber ;
+
+ if (print) {
+ System.out.println(fileName + ": " + objectCount + " objects") ;
+ System.out.println("magic number 0x" +
+ Integer.toHexString(magicNumber) +
+ ", version number " + majorVersionNumber +
+ "." + minorVersionNumber +
+ "." + minorMinorVersionNumber) ;
+ System.out.println("largest object is " + maxSize + " bytes") ;
+ }
+ }
+
+ //
+ // Read the file header.
+ //
+ void readFileHeader() throws IOException {
+ byte header[] = new byte[HEADER_SIZE] ;
+
+ try {
+ setFilePointer(0) ;
+ if (cgFile.read(header) != HEADER_SIZE) {
+ close() ;
+ throw new IOException("failed header read") ;
+ }
+ }
+ catch (IOException e) {
+ if (cgFile != null) {
+ close() ;
+ }
+ throw e ;
+ }
+
+ magicNumber =
+ ((header[MAGIC_NUMBER_OFFSET+0] & 0xff) << 24) |
+ ((header[MAGIC_NUMBER_OFFSET+1] & 0xff) << 16) |
+ ((header[MAGIC_NUMBER_OFFSET+2] & 0xff) << 8) |
+ ((header[MAGIC_NUMBER_OFFSET+3] & 0xff)) ;
+
+ majorVersionNumber =
+ ((header[MAJOR_VERSION_OFFSET+0] & 0xff) << 24) |
+ ((header[MAJOR_VERSION_OFFSET+1] & 0xff) << 16) |
+ ((header[MAJOR_VERSION_OFFSET+2] & 0xff) << 8) |
+ ((header[MAJOR_VERSION_OFFSET+3] & 0xff)) ;
+
+ minorVersionNumber =
+ ((header[MINOR_VERSION_OFFSET+0] & 0xff) << 24) |
+ ((header[MINOR_VERSION_OFFSET+1] & 0xff) << 16) |
+ ((header[MINOR_VERSION_OFFSET+2] & 0xff) << 8) |
+ ((header[MINOR_VERSION_OFFSET+3] & 0xff)) ;
+
+ minorMinorVersionNumber =
+ ((header[MINOR_MINOR_VERSION_OFFSET+0] & 0xff) << 24) |
+ ((header[MINOR_MINOR_VERSION_OFFSET+1] & 0xff) << 16) |
+ ((header[MINOR_MINOR_VERSION_OFFSET+2] & 0xff) << 8) |
+ ((header[MINOR_MINOR_VERSION_OFFSET+3] & 0xff)) ;
+
+ objectCount =
+ ((header[OBJECT_COUNT_OFFSET+0] & 0xff) << 24) |
+ ((header[OBJECT_COUNT_OFFSET+1] & 0xff) << 16) |
+ ((header[OBJECT_COUNT_OFFSET+2] & 0xff) << 8) |
+ ((header[OBJECT_COUNT_OFFSET+3] & 0xff)) ;
+
+ directoryOffset =
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+0] & 0xff) << 56) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+1] & 0xff) << 48) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+2] & 0xff) << 40) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+3] & 0xff) << 32) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+4] & 0xff) << 24) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+5] & 0xff) << 16) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+6] & 0xff) << 8) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+7] & 0xff)) ;
+ }
+
+ //
+ // Write the file header based on current field values.
+ //
+ void writeFileHeader() throws IOException {
+ setFilePointer(0) ;
+ try {
+ cgFile.writeInt(MAGIC_NUMBER) ;
+ cgFile.writeInt(majorVersionNumber) ;
+ cgFile.writeInt(minorVersionNumber) ;
+ cgFile.writeInt(minorMinorVersionNumber) ;
+ cgFile.writeInt(objectCount) ;
+ cgFile.writeInt(0) ; // long word alignment
+ cgFile.writeLong(directoryOffset) ;
+ if (print)
+ System.out.println("wrote file header for " + fileName) ;
+ }
+ catch (IOException e) {
+ throw new IOException
+ (e.getMessage() +
+ "\ncould not write file header for " + fileName) ;
+ }
+ }
+
+ //
+ // Read the directory of compressed geometry object offsets.
+ //
+ void readDirectory(long offset, long[] directory)
+ throws IOException {
+
+ byte buff[] = new byte[directory.length * 8] ;
+ setFilePointer(offset) ;
+
+ try {
+ cgFile.read(buff) ;
+ if (print)
+ System.out.println("read " + buff.length + " byte directory") ;
+ }
+ catch (IOException e) {
+ throw new IOException
+ (e.getMessage() +
+ "\nfailed to read " + buff.length +
+ " byte directory, offset " + offset + " in file " + fileName) ;
+ }
+
+ for (int i = 0 ; i < directory.length ; i++) {
+ directory[i] =
+ ((long)(buff[i*8+0] & 0xff) << 56) |
+ ((long)(buff[i*8+1] & 0xff) << 48) |
+ ((long)(buff[i*8+2] & 0xff) << 40) |
+ ((long)(buff[i*8+3] & 0xff) << 32) |
+ ((long)(buff[i*8+4] & 0xff) << 24) |
+ ((long)(buff[i*8+5] & 0xff) << 16) |
+ ((long)(buff[i*8+6] & 0xff) << 8) |
+ ((long)(buff[i*8+7] & 0xff)) ;
+ }
+ }
+
+ //
+ // Write the file directory.
+ //
+ void writeFileDirectory() throws IOException {
+ setFilePointer(directoryOffset) ;
+
+ int directoryAlign = (int)(directoryOffset % 8) ;
+ if (directoryAlign != 0) {
+ // Align to long word before writing directory of long offsets.
+ byte bytes[] = new byte[8-directoryAlign] ;
+
+ try {
+ cgFile.write(bytes) ;
+ if (print)
+ System.out.println ("wrote " + (8-directoryAlign) +
+ " bytes long alignment") ;
+ }
+ catch (IOException e) {
+ throw new IOException
+ (e.getMessage() +
+ "\ncould not write " + directoryAlign +
+ " bytes to long word align directory for " + fileName) ;
+ }
+ directoryOffset += 8-directoryAlign ;
+ }
+
+ try {
+ for (int i = 0 ; i < objectCount ; i++)
+ cgFile.writeLong(directory[i]) ;
+
+ if (print)
+ System.out.println("wrote file directory for " + fileName) ;
+ }
+ catch (IOException e) {
+ throw new IOException
+ (e.getMessage() +
+ "\ncould not write directory for " + fileName) ;
+ }
+ }
+
+ //
+ // Get the next compressed object in the file, either from the read-ahead
+ // cache or from the file itself.
+ //
+ CompressedGeometryData readNext(int bufferReadLimit)
+ throws IOException {
+ if (objectIndex == objectCount)
+ return null ;
+
+ if (bufferNextObjectCount == 0) {
+ // No valid objects are in the cache.
+ int curSize = 0 ;
+ bufferObjectCount = 0 ;
+
+ // See how much we have room to read.
+ for (int i = objectIndex ; i < objectCount ; i++) {
+ if (curSize + objectSizes[i] > bufferReadLimit) break ;
+ curSize += objectSizes[i] ;
+ bufferObjectCount++ ;
+ }
+
+ // Try to read that amount.
+ try {
+ int n = cgFile.read(cgBuffer, 0, curSize) ;
+ if (print)
+ System.out.println("\nread " + n +
+ " bytes from " + fileName) ;
+ }
+ catch (IOException e) {
+ throw new IOException
+ (e.getMessage() +
+ "\nfailed to read " + curSize +
+ " bytes, object " + objectIndex + " in file " + fileName) ;
+ }
+
+ // Point at the first object in the buffer.
+ bufferObjectStart = objectIndex ;
+ bufferNextObjectCount = bufferObjectCount ;
+ bufferNextObjectOffset = 0 ;
+ }
+
+ // Get block header info.
+ geomSize =
+ ((cgBuffer[bufferNextObjectOffset+OBJECT_SIZE_OFFSET+0]&0xff)<<24) |
+ ((cgBuffer[bufferNextObjectOffset+OBJECT_SIZE_OFFSET+1]&0xff)<<16) |
+ ((cgBuffer[bufferNextObjectOffset+OBJECT_SIZE_OFFSET+2]&0xff)<< 8) |
+ ((cgBuffer[bufferNextObjectOffset+OBJECT_SIZE_OFFSET+3]&0xff)) ;
+
+ geomDataType =
+ ((cgBuffer[bufferNextObjectOffset+GEOM_DATA_OFFSET+0]&0xff) << 24) |
+ ((cgBuffer[bufferNextObjectOffset+GEOM_DATA_OFFSET+1]&0xff) << 16) |
+ ((cgBuffer[bufferNextObjectOffset+GEOM_DATA_OFFSET+2]&0xff) << 8) |
+ ((cgBuffer[bufferNextObjectOffset+GEOM_DATA_OFFSET+3]&0xff)) ;
+
+ // Get offset of compressed geometry data from start of buffer.
+ geomStart = bufferNextObjectOffset + BLOCK_HEADER_SIZE ;
+
+ if (print) {
+ System.out.println("\nobject " + objectIndex +
+ "\nfile offset " + directory[objectIndex] +
+ ", buffer offset " + bufferNextObjectOffset) ;
+ System.out.println("size " + geomSize + " bytes, " +
+ "data descriptor 0x" +
+ Integer.toHexString(geomDataType)) ;
+ }
+
+ // Update cache info.
+ bufferNextObjectOffset += objectSizes[objectIndex] ;
+ bufferNextObjectCount-- ;
+ objectIndex++ ;
+
+ return newCG(geomSize, geomStart, geomDataType) ;
+ }
+
+
+ //
+ // Construct and return a compressed geometry node.
+ //
+ CompressedGeometryData newCG(int geomSize,
+ int geomStart,
+ int geomDataType) {
+ cgh.size = geomSize ;
+ cgh.start = geomStart ;
+
+ if ((geomDataType & TYPE_MASK) == TYPE_POINT)
+ cgh.bufferType = CompressedGeometryData.Header.POINT_BUFFER ;
+ else if ((geomDataType & TYPE_MASK) == TYPE_LINE)
+ cgh.bufferType = CompressedGeometryData.Header.LINE_BUFFER ;
+ else if ((geomDataType & TYPE_MASK) == TYPE_TRIANGLE)
+ cgh.bufferType = CompressedGeometryData.Header.TRIANGLE_BUFFER ;
+
+ cgh.bufferDataPresent = 0 ;
+
+ if ((geomDataType & NORMAL_PRESENT_MASK) != 0)
+ cgh.bufferDataPresent |=
+ CompressedGeometryData.Header.NORMAL_IN_BUFFER ;
+
+ if ((geomDataType & COLOR_PRESENT_MASK) != 0)
+ cgh.bufferDataPresent |=
+ CompressedGeometryData.Header.COLOR_IN_BUFFER ;
+
+ if ((geomDataType & ALPHA_PRESENT_MASK) != 0)
+ cgh.bufferDataPresent |=
+ CompressedGeometryData.Header.ALPHA_IN_BUFFER ;
+
+ return new CompressedGeometryData(cgh, cgBuffer) ;
+ }
+
+ /**
+ * Release file resources when this object is garbage collected.
+ */
+ protected void finalize() {
+ close() ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryRetained.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryRetained.java
new file mode 100644
index 0000000..4af6f50
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryRetained.java
@@ -0,0 +1,282 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import javax.media.j3d.BoundingBox;
+import javax.media.j3d.Bounds;
+import javax.media.j3d.Canvas3D;
+import javax.media.j3d.GeometryArray;
+import javax.media.j3d.PickInfo;
+import javax.media.j3d.PickShape;
+import javax.media.j3d.Transform3D;
+import javax.vecmath.Point3d;
+
+/**
+ * The compressed geometry object is used to store geometry in a
+ * compressed format. Using compressed geometry reduces the amount
+ * of memory needed by a Java 3D application and increases the speed
+ * objects can be sent over the network. Once geometry decompression
+ * hardware support becomes available, increased rendering performance
+ * will also result from the use of compressed geometry.
+ */
+class CompressedGeometryRetained extends Object {
+
+ // If not in by-reference mode, a 48-byte header as defined by the
+ // GL_SUNX_geometry_compression OpenGL extension is always concatenated to
+ // the beginning of the compressed geometry data and copied along with the
+ // it into a contiguous array. This allows hardware decompression using
+ // the obsolete experimental GL_SUNX_geometry_compression extension if
+ // that is all that is available.
+ //
+ // This is completely distinct and not to be confused with the cgHeader
+ // field on the non-retained side, although much of the data is
+ // essentially the same.
+ private static final int HEADER_LENGTH = 48 ;
+
+ // These are the header locations examined.
+ private static final int HEADER_MAJOR_VERSION_OFFSET = 0 ;
+ private static final int HEADER_MINOR_VERSION_OFFSET = 1 ;
+ private static final int HEADER_MINOR_MINOR_VERSION_OFFSET = 2 ;
+ private static final int HEADER_BUFFER_TYPE_OFFSET = 3 ;
+ private static final int HEADER_BUFFER_DATA_OFFSET = 4 ;
+
+ // The OpenGL compressed geometry extensions use bits instead of
+ // enumerations to represent the type of compressed geometry.
+ static final byte TYPE_POINT = 1 ;
+ static final byte TYPE_LINE = 2 ;
+ static final byte TYPE_TRIANGLE = 4 ;
+
+ // Version number of this compressed geometry object.
+ int majorVersionNumber ;
+ int minorVersionNumber ;
+ int minorMinorVersionNumber ;
+
+ // These fields are used by the native execute() method.
+ int packedVersion ;
+ int bufferType ;
+ int bufferContents ;
+ int renderFlags ;
+ int offset ;
+ int size ;
+ byte[] compressedGeometry ;
+
+ // A reference to the original byte array with which this object was
+ // created. If hardware decompression is available but it doesn't support
+ // by-reference semantics, then an internal copy of the original byte array
+ // is made even when by-reference semantics have been requested.
+ private byte[] originalCompressedGeometry = null ;
+
+ // Geometric bounds
+ private BoundingBox geoBounds = new BoundingBox();
+
+ // True if by-reference data access mode is in effect.
+ private boolean byReference = false ;
+
+ /**
+ * The package-scoped constructor.
+ */
+ CompressedGeometryRetained() {
+ // Compressed geometry is always bounded by [-1..1] on each axis, so
+ // set that as the initial bounding box.
+ geoBounds.setUpper( 1.0, 1.0, 1.0) ;
+ geoBounds.setLower(-1.0,-1.0,-1.0) ;
+ }
+
+ /**
+ * Return true if the data access mode is by-reference.
+ */
+ boolean isByReference() {
+ return this.byReference ;
+ }
+
+ private void createByCopy(byte[] geometry) {
+ // Always copy a header along with the compressed geometry into a
+ // contiguous array in order to support hardware acceleration with the
+ // GL_SUNX_geometry_compression extension. The header is unnecessary
+ // if only the newer GL_SUN_geometry_compression API needs support.
+ compressedGeometry = new byte[HEADER_LENGTH + this.size] ;
+
+ compressedGeometry[HEADER_MAJOR_VERSION_OFFSET] =
+ (byte)this.majorVersionNumber ;
+
+ compressedGeometry[HEADER_MINOR_VERSION_OFFSET] =
+ (byte)this.minorVersionNumber ;
+
+ compressedGeometry[HEADER_MINOR_MINOR_VERSION_OFFSET] =
+ (byte)this.minorMinorVersionNumber ;
+
+ compressedGeometry[HEADER_BUFFER_TYPE_OFFSET] =
+ (byte)this.bufferType ;
+
+ compressedGeometry[HEADER_BUFFER_DATA_OFFSET] =
+ (byte)this.bufferContents ;
+
+ System.arraycopy(geometry, this.offset,
+ compressedGeometry, HEADER_LENGTH, this.size) ;
+
+ this.offset = HEADER_LENGTH ;
+ }
+
+ /**
+ * Creates the retained compressed geometry data. Data from the header is
+ * always copied; the compressed geometry is copied as well if the data
+ * access mode is not by-reference.
+ *
+ * @param hdr the compressed geometry header
+ * @param geometry the compressed geometry
+ * @param byReference if true then by-reference semantics requested
+ */
+ void createCompressedGeometry(CompressedGeometryData.Header hdr,
+ byte[] geometry, boolean byReference) {
+
+ this.byReference = byReference ;
+
+ if (hdr.lowerBound != null)
+ this.geoBounds.setLower(hdr.lowerBound) ;
+
+ if (hdr.upperBound != null)
+ this.geoBounds.setUpper(hdr.upperBound) ;
+
+//// this.centroid.set(geoBounds.getCenter());
+//// recompCentroid = false;
+ this.majorVersionNumber = hdr.majorVersionNumber ;
+ this.minorVersionNumber = hdr.minorVersionNumber ;
+ this.minorMinorVersionNumber = hdr.minorMinorVersionNumber ;
+
+ this.packedVersion =
+ (hdr.majorVersionNumber << 24) |
+ (hdr.minorVersionNumber << 16) |
+ (hdr.minorMinorVersionNumber << 8) ;
+
+ switch(hdr.bufferType) {
+ case CompressedGeometryData.Header.POINT_BUFFER:
+ this.bufferType = TYPE_POINT ;
+ break ;
+ case CompressedGeometryData.Header.LINE_BUFFER:
+ this.bufferType = TYPE_LINE ;
+ break ;
+ case CompressedGeometryData.Header.TRIANGLE_BUFFER:
+ this.bufferType = TYPE_TRIANGLE ;
+ break ;
+ }
+
+ this.bufferContents = hdr.bufferDataPresent ;
+ this.renderFlags = 0 ;
+
+ this.size = hdr.size ;
+ this.offset = hdr.start ;
+
+ if (byReference) {
+ // Assume we can use the given reference, but maintain a second
+ // reference in case a copy is later needed.
+ this.compressedGeometry = geometry;
+ this.originalCompressedGeometry = geometry;
+ } else {
+ // Copy the original data into a format that can be used by both
+ // the software and native hardware decompressors.
+ createByCopy(geometry);
+ this.originalCompressedGeometry = null;
+ }
+ }
+
+ /**
+ * Return a vertex format mask that's compatible with GeometryArray
+ * objects.
+ */
+ int getVertexFormat() {
+ int vertexFormat = GeometryArray.COORDINATES;
+
+ if ((bufferContents & CompressedGeometryData.Header.NORMAL_IN_BUFFER) != 0) {
+ vertexFormat |= GeometryArray.NORMALS;
+ }
+
+ if ((bufferContents & CompressedGeometryData.Header.COLOR_IN_BUFFER) != 0) {
+ if ((bufferContents & CompressedGeometryData.Header.ALPHA_IN_BUFFER) != 0) {
+ vertexFormat |= GeometryArray.COLOR_4;
+ } else {
+ vertexFormat |= GeometryArray.COLOR_3;
+ }
+ }
+
+ return vertexFormat ;
+ }
+
+ /**
+ * Return a buffer type that's compatible with CompressedGeometryData.Header.
+ */
+ int getBufferType() {
+ switch(this.bufferType) {
+ case TYPE_POINT:
+ return CompressedGeometryData.Header.POINT_BUFFER ;
+ case TYPE_LINE:
+ return CompressedGeometryData.Header.LINE_BUFFER ;
+ default:
+ case TYPE_TRIANGLE:
+ return CompressedGeometryData.Header.TRIANGLE_BUFFER ;
+ }
+ }
+
+ /**
+ * Copies compressed geometry data into the given array of bytes.
+ * The internal header information is not copied.
+ *
+ * @param buff array of bytes into which to copy compressed geometry
+ */
+ void copy(byte[] buff) {
+ System.arraycopy(compressedGeometry, offset, buff, 0, size) ;
+ }
+
+ /**
+ * Returns a reference to the original compressed geometry byte array,
+ * which may have been copied even if by-reference semantics have been
+ * requested. It will be null if byCopy is in effect.
+ *
+ * @return reference to array of bytes containing the compressed geometry.
+ */
+ byte[] getReference() {
+ return originalCompressedGeometry ;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStream.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStream.java
new file mode 100644
index 0000000..77ab5f4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStream.java
@@ -0,0 +1,2321 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import com.sun.j3d.internal.BufferWrapper;
+import com.sun.j3d.internal.ByteBufferWrapper;
+import com.sun.j3d.internal.DoubleBufferWrapper;
+import com.sun.j3d.internal.FloatBufferWrapper;
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import javax.media.j3d.Appearance;
+import javax.media.j3d.Geometry;
+import javax.media.j3d.GeometryArray;
+import javax.media.j3d.GeometryStripArray;
+import javax.media.j3d.IndexedGeometryArray;
+import javax.media.j3d.IndexedGeometryStripArray;
+import javax.media.j3d.IndexedLineArray;
+import javax.media.j3d.IndexedLineStripArray;
+import javax.media.j3d.IndexedQuadArray;
+import javax.media.j3d.IndexedTriangleArray;
+import javax.media.j3d.IndexedTriangleFanArray;
+import javax.media.j3d.IndexedTriangleStripArray;
+import javax.media.j3d.J3DBuffer;
+import javax.media.j3d.LineArray;
+import javax.media.j3d.LineStripArray;
+import javax.media.j3d.Material;
+import javax.media.j3d.QuadArray;
+import javax.media.j3d.Shape3D;
+import javax.media.j3d.TriangleArray;
+import javax.media.j3d.TriangleFanArray;
+import javax.media.j3d.TriangleStripArray;
+import javax.vecmath.Color3f;
+import javax.vecmath.Color4f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Point3f;
+import javax.vecmath.Point3i;
+import javax.vecmath.Vector3f;
+
+/**
+ * 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.<p>
+ *
+ * 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.<p>
+ *
+ * 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.<p>
+ *
+ * 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.<p>
+ *
+ * @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.<p>
+ *
+ * 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).<p>
+ *
+ * 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.<p>
+ *
+ * @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.<p>
+ *
+ * @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 ;
+ }
+
+ 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)))) ;
+ }
+
+ 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 ;
+ }
+
+ void quantize(CompressionStream s, HuffmanTable t) {
+ normalQuant = value ;
+ normalQuantChanged = true ;
+ }
+
+ 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 ;
+ }
+
+ void quantize(CompressionStream s, HuffmanTable t) {
+ colorQuant = value ;
+ colorQuantChanged = true ;
+ }
+
+ 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++ ;
+ }
+
+ 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 ;
+ }
+ }
+
+ 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) ;
+ }
+
+ 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) ;
+ }
+ }
+
+ 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) ;
+ }
+
+ 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) ;
+
+ 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() ;
+ }
+
+ 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) ;
+ }
+
+ 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 {
+ FloatBufferWrapper fbw = null ;
+
+ InterleavedGeometryNIO(GeometryArray ga) {
+ super(ga) ;
+ J3DBuffer buffer = ga.getInterleavedVertexBuffer() ;
+ if (BufferWrapper.getBufferType(buffer) ==
+ BufferWrapper.TYPE_FLOAT) {
+ fbw = new FloatBufferWrapper(buffer) ;
+ }
+ else {
+ throw new IllegalArgumentException
+ ("\ninterleaved vertex buffer must be FloatBuffer") ;
+ }
+ }
+
+ 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) ;
+ }
+
+ 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 ;
+ }
+ }
+
+ 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) ;
+ }
+
+ 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() ;
+
+ ByteBufferWrapper colorsB = null ;
+ FloatBufferWrapper colorsF = null ;
+ FloatBufferWrapper normals = null ;
+ FloatBufferWrapper positionsF = null ;
+ DoubleBufferWrapper 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 = new FloatBufferWrapper(buffer) ;
+ if (debug) System.out.println("float positions buffer") ;
+ break ;
+ case BufferWrapper.TYPE_DOUBLE:
+ positionsD = new DoubleBufferWrapper(buffer) ;
+ 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 = new ByteBufferWrapper(buffer) ;
+ if (debug) System.out.println("byte colors buffer") ;
+ break ;
+ case BufferWrapper.TYPE_FLOAT:
+ colorsF = new FloatBufferWrapper(buffer) ;
+ 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 = new FloatBufferWrapper(buffer) ;
+ 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 ;
+ }
+ }
+
+ 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) ;
+ }
+
+ 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.<p>
+ *
+ * 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.<p>
+ *
+ * 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.<p>
+ *
+ * @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.<p>
+ *
+ * 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.<p>
+ *
+ * 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.<p>
+ *
+ * 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.<p>
+ *
+ * @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.<p>
+ *
+ * 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.<p>
+ *
+ * @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.<p>
+ *
+ * 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.<p>
+ *
+ * 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.<p>
+ *
+ * @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 ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamColor.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamColor.java
new file mode 100644
index 0000000..d8aef35
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamColor.java
@@ -0,0 +1,272 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Color4f;
+
+/**
+ * This class represents a color in a compression stream. It maintains both
+ * floating-point and quantized representations. This color may be bundled
+ * with a vertex or exist separately as a global color.
+ */
+class CompressionStreamColor extends CompressionStreamElement {
+ private int R, G, B, A ;
+ private boolean color3 ;
+ private boolean color4 ;
+ private float colorR, colorG, colorB, colorA ;
+
+ int rAbsolute, gAbsolute, bAbsolute, aAbsolute ;
+
+ /**
+ * Create a CompressionStreamColor.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param color3 floating-point representation to be encoded
+ */
+ CompressionStreamColor(CompressionStream stream, Color3f c3) {
+ this.color4 = false ;
+ this.color3 = true ;
+ colorR = c3.x ;
+ colorG = c3.y ;
+ colorB = c3.z ;
+ colorA = 0.0f ;
+ stream.byteCount += 12 ;
+ }
+
+ /**
+ * Create a CompressionStreamColor.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param color4 floating-point representation to be encoded
+ */
+ CompressionStreamColor(CompressionStream stream, Color4f c4) {
+ this.color3 = false ;
+ this.color4 = true ;
+ colorR = c4.x ;
+ colorG = c4.y ;
+ colorB = c4.z ;
+ colorA = c4.w ;
+ stream.byteCount += 16 ;
+ }
+
+ /**
+ * Quantize a floating point color to fixed point integer components of
+ * the specified number of bits. The bit length can range from a maximum
+ * of 16 to a minimum of 2 bits since negative colors are not defined.<p>
+ *
+ * The bit length is the total number of bits in the signed version of the
+ * fixed point representation of the input color, which is assumed to
+ * be normalized into the [0..1) range. With the maximum bit length of
+ * 16, 15 bits of positive colors can be represented; a bit length of 9 is
+ * needed to get the 8 bit positive color size in common use.<p>
+ *
+ * @param stream CompressionStream associated with this element
+ * @param table HuffmanTable for collecting data about the quantized
+ * representation of this element
+ */
+ void quantize(CompressionStream stream, HuffmanTable huffmanTable) {
+ // Clamp quantization.
+ int quant =
+ (stream.colorQuant < 2? 2 :
+ (stream.colorQuant > 16? 16 : stream.colorQuant)) ;
+
+ absolute = false ;
+ if (stream.firstColor || stream.colorQuantChanged) {
+ absolute = true ;
+ stream.lastColor[0] = 0 ;
+ stream.lastColor[1] = 0 ;
+ stream.lastColor[2] = 0 ;
+ stream.lastColor[3] = 0 ;
+ stream.firstColor = false ;
+ stream.colorQuantChanged = false ;
+ }
+
+ // Convert the floating point position to s.15 2's complement.
+ if (color3) {
+ R = (int)(colorR * 32768.0) ;
+ G = (int)(colorG * 32768.0) ;
+ B = (int)(colorB * 32768.0) ;
+ A = 0 ;
+ } else if (color4) {
+ R = (int)(colorR * 32768.0) ;
+ G = (int)(colorG * 32768.0) ;
+ B = (int)(colorB * 32768.0) ;
+ A = (int)(colorA * 32768.0) ;
+ }
+
+ // Clamp color components.
+ R = (R > 32767? 32767: (R < 0? 0: R)) ;
+ G = (G > 32767? 32767: (G < 0? 0: G)) ;
+ B = (B > 32767? 32767: (B < 0? 0: B)) ;
+ A = (A > 32767? 32767: (A < 0? 0: A)) ;
+
+ // Compute quantized values.
+ R &= quantizationMask[quant] ;
+ G &= quantizationMask[quant] ;
+ B &= quantizationMask[quant] ;
+ A &= quantizationMask[quant] ;
+
+ // Copy and retain absolute color for mesh buffer lookup.
+ rAbsolute = R ;
+ gAbsolute = G ;
+ bAbsolute = B ;
+ aAbsolute = A ;
+
+ // Compute deltas.
+ R -= stream.lastColor[0] ;
+ G -= stream.lastColor[1] ;
+ B -= stream.lastColor[2] ;
+ A -= stream.lastColor[3] ;
+
+ // Update last values.
+ stream.lastColor[0] += R ;
+ stream.lastColor[1] += G ;
+ stream.lastColor[2] += B ;
+ stream.lastColor[3] += A ;
+
+ // Compute length and shift common to all components.
+ if (color3)
+ computeLengthShift(R, G, B) ;
+
+ else if (color4)
+ computeLengthShift(R, G, B, A) ;
+
+ // 0-length components are allowed only for normals.
+ if (length == 0)
+ length = 1 ;
+
+ // Add this element to the Huffman table associated with this stream.
+ huffmanTable.addColorEntry(length, shift, absolute) ;
+ }
+
+ /**
+ * Output a setColor command.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputCommand(HuffmanTable table, CommandStream output) {
+ outputColor(table, output, CommandStream.SET_COLOR, 8) ;
+ }
+
+ /**
+ * Output a color subcommand.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputSubcommand(HuffmanTable table, CommandStream output) {
+
+ outputColor(table, output, 0, 6) ;
+ }
+
+ //
+ // Output the final compressed bits to the output command stream.
+ //
+ private void outputColor(HuffmanTable table, CommandStream output,
+ int header, int headerLength) {
+ HuffmanNode t ;
+
+ // Look up the Huffman token for this compression stream element.
+ t = table.getColorEntry(length, shift, absolute) ;
+
+ // Construct the color subcommand components. The maximum length of a
+ // color subcommand is 70 bits (a tag with a length of 6 followed by 4
+ // components of 16 bits each). The subcommand is therefore
+ // constructed initially using just the first 3 components, with the
+ // 4th component added later after the tag has been shifted into the
+ // subcommand header.
+ int componentLength = t.dataLength - t.shift ;
+ int subcommandLength = t.tagLength + (3 * componentLength) ;
+
+ R = (R >> t.shift) & (int)lengthMask[componentLength] ;
+ G = (G >> t.shift) & (int)lengthMask[componentLength] ;
+ B = (B >> t.shift) & (int)lengthMask[componentLength] ;
+
+ long colorSubcommand =
+ (((long)t.tag) << (3 * componentLength)) |
+ (((long)R) << (2 * componentLength)) |
+ (((long)G) << (1 * componentLength)) |
+ (((long)B) << (0 * componentLength)) ;
+
+ if (subcommandLength < 6) {
+ // The header will have some empty bits. The Huffman tag
+ // computation will prevent this if necessary.
+ header |= (int)(colorSubcommand << (6 - subcommandLength)) ;
+ subcommandLength = 0 ;
+ }
+ else {
+ // Move the 1st 6 bits of the subcommand into the header.
+ header |= (int)(colorSubcommand >>> (subcommandLength - 6)) ;
+ subcommandLength -= 6 ;
+ }
+
+ // Add alpha if present.
+ if (color4) {
+ A = (A >> t.shift) & (int)lengthMask[componentLength] ;
+ colorSubcommand = (colorSubcommand << componentLength) | A ;
+ subcommandLength += componentLength ;
+ }
+
+ // Add the header and body to the output buffer.
+ output.addCommand(header, headerLength,
+ colorSubcommand, subcommandLength) ;
+ }
+
+ public String toString() {
+ String d = absolute? "" : "delta " ;
+ String c = (colorR + " " + colorG + " " + colorB +
+ (color4? (" " + colorA): "")) ;
+
+ return
+ "color: " + c + "\n" +
+ " fixed point " + d + + R + " " + G + " " + B + "\n" +
+ " length " + length + " shift " + shift +
+ (absolute? " absolute" : " relative") ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamElement.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamElement.java
new file mode 100644
index 0000000..7b66765
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamElement.java
@@ -0,0 +1,359 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+/**
+ * Instances of this class are used as elements in a CompressionStream.
+ * @see CompressionStream
+ */
+abstract class CompressionStreamElement {
+ /**
+ * Bit length of quantized geometric components.
+ */
+ int length ;
+
+ /**
+ * Number of trailing zeros in quantized geometric components.
+ */
+ int shift ;
+
+ /**
+ * If false, geometric component values are represented as differences
+ * from those of the preceding element in the stream.
+ */
+ boolean absolute ;
+
+ /**
+ * Array with elements that can be used as masks to apply a quantization
+ * to the number of bits indicated by the referencing index [0..16].
+ */
+ static final int quantizationMask[] = {
+ 0xFFFF0000, 0xFFFF8000, 0xFFFFC000, 0xFFFFE000,
+ 0xFFFFF000, 0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00,
+ 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0,
+ 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE,
+ 0xFFFFFFFF
+ } ;
+
+ /**
+ * Array with elements that can be used as masks to retain the number of
+ * trailing bits of data indicated by the referencing index [0..64]. Used
+ * to clear the leading sign bits of fixed-point 2's complement numbers
+ * and in building the compressed output stream.
+ */
+ static final long lengthMask[] = {
+ 0x0000000000000000L, 0x0000000000000001L,
+ 0x0000000000000003L, 0x0000000000000007L,
+ 0x000000000000000FL, 0x000000000000001FL,
+ 0x000000000000003FL, 0x000000000000007FL,
+ 0x00000000000000FFL, 0x00000000000001FFL,
+ 0x00000000000003FFL, 0x00000000000007FFL,
+ 0x0000000000000FFFL, 0x0000000000001FFFL,
+ 0x0000000000003FFFL, 0x0000000000007FFFL,
+ 0x000000000000FFFFL, 0x000000000001FFFFL,
+ 0x000000000003FFFFL, 0x000000000007FFFFL,
+ 0x00000000000FFFFFL, 0x00000000001FFFFFL,
+ 0x00000000003FFFFFL, 0x00000000007FFFFFL,
+ 0x0000000000FFFFFFL, 0x0000000001FFFFFFL,
+ 0x0000000003FFFFFFL, 0x0000000007FFFFFFL,
+ 0x000000000FFFFFFFL, 0x000000001FFFFFFFL,
+ 0x000000003FFFFFFFL, 0x000000007FFFFFFFL,
+ 0x00000000FFFFFFFFL, 0x00000001FFFFFFFFL,
+ 0x00000003FFFFFFFFL, 0x00000007FFFFFFFFL,
+ 0x0000000FFFFFFFFFL, 0x0000001FFFFFFFFFL,
+ 0x0000003FFFFFFFFFL, 0x0000007FFFFFFFFFL,
+ 0x000000FFFFFFFFFFL, 0x000001FFFFFFFFFFL,
+ 0x000003FFFFFFFFFFL, 0x000007FFFFFFFFFFL,
+ 0x00000FFFFFFFFFFFL, 0x00001FFFFFFFFFFFL,
+ 0x00003FFFFFFFFFFFL, 0x00007FFFFFFFFFFFL,
+ 0x0000FFFFFFFFFFFFL, 0x0001FFFFFFFFFFFFL,
+ 0x0003FFFFFFFFFFFFL, 0x0007FFFFFFFFFFFFL,
+ 0x000FFFFFFFFFFFFFL, 0x001FFFFFFFFFFFFFL,
+ 0x003FFFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL,
+ 0x00FFFFFFFFFFFFFFL, 0x01FFFFFFFFFFFFFFL,
+ 0x03FFFFFFFFFFFFFFL, 0x07FFFFFFFFFFFFFFL,
+ 0x0FFFFFFFFFFFFFFFL, 0x1FFFFFFFFFFFFFFFL,
+ 0x3FFFFFFFFFFFFFFFL, 0x7FFFFFFFFFFFFFFFL,
+ 0xFFFFFFFFFFFFFFFFL
+ } ;
+
+
+ /**
+ * Computes the quantized representation of this stream element.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param table HuffmanTable for collecting data about the quantized
+ * representation of this element
+ */
+ abstract void quantize(CompressionStream stream, HuffmanTable table) ;
+
+ /**
+ * Outputs the compressed bits representing this stream element.
+ * Some instances of CompressionStreamElement don't require an
+ * implementation and will inherit the stub provided here.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputCommand(HuffmanTable table, CommandStream output) {
+ }
+
+ /**
+ * Finds the minimum bits needed to represent the given 16-bit signed 2's
+ * complement integer. For positive integers, this include the first
+ * 1 starting from the left, plus a 0 sign bit; for negative integers,
+ * this includes the first 0 starting from the left, plus a 1 sign bit.
+ * 0 is a special case returning 0; however, 0-length components are valid
+ * ONLY for normals.
+ *
+ * The decompressor uses the data length to determine how many bits of
+ * sign extension to add to the data coming in from the compressed stream
+ * in order to create a 16-bit signed 2's complement integer. E.g., a data
+ * length of 12 indicates that 16-12=4 bits of sign are to be extended.<p>
+ *
+ * @param number a signed 2's complement integer representable in 16 bits
+ * or less
+ * @return minimum number of bits to represent the number
+ */
+ private static final int getLength(int number) {
+ if (number == 0)
+ return 0 ;
+
+ else if ((number & 0x8000) > 0) {
+ // negative numbers
+ if ((number & 0x4000) == 0) return 16 ;
+ if ((number & 0x2000) == 0) return 15 ;
+ if ((number & 0x1000) == 0) return 14 ;
+ if ((number & 0x0800) == 0) return 13 ;
+ if ((number & 0x0400) == 0) return 12 ;
+ if ((number & 0x0200) == 0) return 11 ;
+ if ((number & 0x0100) == 0) return 10 ;
+ if ((number & 0x0080) == 0) return 9 ;
+ if ((number & 0x0040) == 0) return 8 ;
+ if ((number & 0x0020) == 0) return 7 ;
+ if ((number & 0x0010) == 0) return 6 ;
+ if ((number & 0x0008) == 0) return 5 ;
+ if ((number & 0x0004) == 0) return 4 ;
+ if ((number & 0x0002) == 0) return 3 ;
+ if ((number & 0x0001) == 0) return 2 ;
+
+ return 1 ;
+
+ } else {
+ // positive numbers
+ if ((number & 0x4000) > 0) return 16 ;
+ if ((number & 0x2000) > 0) return 15 ;
+ if ((number & 0x1000) > 0) return 14 ;
+ if ((number & 0x0800) > 0) return 13 ;
+ if ((number & 0x0400) > 0) return 12 ;
+ if ((number & 0x0200) > 0) return 11 ;
+ if ((number & 0x0100) > 0) return 10 ;
+ if ((number & 0x0080) > 0) return 9 ;
+ if ((number & 0x0040) > 0) return 8 ;
+ if ((number & 0x0020) > 0) return 7 ;
+ if ((number & 0x0010) > 0) return 6 ;
+ if ((number & 0x0008) > 0) return 5 ;
+ if ((number & 0x0004) > 0) return 4 ;
+ if ((number & 0x0002) > 0) return 3 ;
+
+ return 2 ;
+ }
+ }
+
+ /**
+ * Finds the rightmost 1 bit in the given 16-bit integer. This value is
+ * used by the decompressor to indicate the number of trailing zeros to be
+ * added to the end of the data coming in from the compressed stream,
+ * accomplished by left shifting the data by the indicated amount.
+ * 0 is a special case returning 0.<p>
+ *
+ * @param number an integer representable in 16 bits or less
+ * @return number of trailing zeros
+ */
+ private static final int getShift(int number) {
+ if (number == 0) return 0 ;
+
+ if ((number & 0x0001) > 0) return 0 ;
+ if ((number & 0x0002) > 0) return 1 ;
+ if ((number & 0x0004) > 0) return 2 ;
+ if ((number & 0x0008) > 0) return 3 ;
+ if ((number & 0x0010) > 0) return 4 ;
+ if ((number & 0x0020) > 0) return 5 ;
+ if ((number & 0x0040) > 0) return 6 ;
+ if ((number & 0x0080) > 0) return 7 ;
+ if ((number & 0x0100) > 0) return 8 ;
+ if ((number & 0x0200) > 0) return 9 ;
+ if ((number & 0x0400) > 0) return 10 ;
+ if ((number & 0x0800) > 0) return 11 ;
+ if ((number & 0x1000) > 0) return 12 ;
+ if ((number & 0x2000) > 0) return 13 ;
+ if ((number & 0x4000) > 0) return 14 ;
+
+ return 15 ;
+ }
+
+ /**
+ * Computes common length and shift of 2 numbers.
+ */
+ final void computeLengthShift(int n0, int n1) {
+ int s0 = n0 & 0x8000 ;
+ int s1 = n1 & 0x8000 ;
+
+ // equal sign optimization
+ if (s0 == s1)
+ if (s0 == 0)
+ this.length = getLength(n0 | n1) ;
+ else
+ this.length = getLength(n0 & n1) ;
+ else
+ this.length = getMaximum(getLength(n0), getLength(n1)) ;
+
+ this.shift = getShift(n0 | n1) ;
+ }
+
+
+ /**
+ * Computes common length and shift of 3 numbers.
+ */
+ final void computeLengthShift(int n0, int n1, int n2) {
+ int s0 = n0 & 0x8000 ;
+ int s1 = n1 & 0x8000 ;
+ int s2 = n2 & 0x8000 ;
+
+ // equal sign optimization
+ if (s0 == s1)
+ if (s1 == s2)
+ if (s2 == 0)
+ this.length = getLength(n0 | n1 | n2) ;
+ else
+ this.length = getLength(n0 & n1 & n2) ;
+ else
+ if (s1 == 0)
+ this.length = getMaximum(getLength(n0 | n1),
+ getLength(n2)) ;
+ else
+ this.length = getMaximum(getLength(n0 & n1),
+ getLength(n2)) ;
+ else
+ if (s1 == s2)
+ if (s2 == 0)
+ this.length = getMaximum(getLength(n1 | n2),
+ getLength(n0)) ;
+ else
+ this.length = getMaximum(getLength(n1 & n2),
+ getLength(n0)) ;
+ else
+ if (s0 == 0)
+ this.length = getMaximum(getLength(n0 | n2),
+ getLength(n1)) ;
+ else
+ this.length = getMaximum(getLength(n0 & n2),
+ getLength(n1)) ;
+
+ this.shift = getShift(n0 | n1 | n2) ;
+ }
+
+
+ /**
+ * Computes common length and shift of 4 numbers.
+ */
+ final void computeLengthShift(int n0, int n1, int n2, int n3) {
+ this.length = getMaximum(getLength(n0), getLength(n1),
+ getLength(n2), getLength(n3)) ;
+
+ this.shift = getShift(n0 | n1 | n2 | n3) ;
+ }
+
+
+ /**
+ * Finds the maximum of two integers.
+ */
+ private static final int getMaximum(int x, int y) {
+ if (x > y)
+ return x ;
+ else
+ return y ;
+ }
+
+ /**
+ * Finds the maximum of three integers.
+ */
+ private static final int getMaximum(int x, int y, int z) {
+ if (x > y)
+ if (x > z)
+ return x ;
+ else
+ return z ;
+ else
+ if (y > z)
+ return y ;
+ else
+ return z ;
+ }
+
+ /**
+ * Finds the maximum of four integers.
+ */
+ private static final int getMaximum(int x, int y, int z, int w) {
+ int n0, n1 ;
+
+ if (x > y)
+ n0 = x ;
+ else
+ n0 = y ;
+
+ if (z > w)
+ n1 = z ;
+ else
+ n1 = w ;
+
+ if (n0 > n1)
+ return n0 ;
+ else
+ return n1 ;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamNormal.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamNormal.java
new file mode 100644
index 0000000..3a44125
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamNormal.java
@@ -0,0 +1,674 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import javax.vecmath.Vector3f;
+
+/**
+ * This class represents a normal in a compression stream. It maintains both
+ * floating-point and quantized representations. This normal may be bundled
+ * with a vertex or exist separately as a global normal.
+ */
+class CompressionStreamNormal extends CompressionStreamElement {
+ private int u, v ;
+ private int specialOctant, specialSextant ;
+ private float normalX, normalY, normalZ ;
+
+ int octant, sextant ;
+ boolean specialNormal ;
+ int uAbsolute, vAbsolute ;
+
+ /**
+ * Create a CompressionStreamNormal.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param normal floating-point representation to be encoded
+ */
+ CompressionStreamNormal(CompressionStream stream, Vector3f normal) {
+ this.normalX = normal.x ;
+ this.normalY = normal.y ;
+ this.normalZ = normal.z ;
+ stream.byteCount += 12 ;
+ }
+
+ //
+ // Normal Encoding Parameterization
+ //
+ // A floating point normal is quantized to a desired number of bits by
+ // comparing it to candidate entries in a table of every possible normal
+ // at that quantization and finding the closest match. This table of
+ // normals is indexed by the following encoding:
+ //
+ // First, points on a unit radius sphere are parameterized by two angles,
+ // th and psi, using usual spherical coordinates. th is the angle about
+ // the y axis, psi is the inclination to the plane containing the point.
+ // The mapping between rectangular and spherical coordinates is:
+ //
+ // x = cos(th)*cos(psi)
+ // y = sin(psi)
+ // z = sin(th)*cos(psi)
+ //
+ // Points on sphere are folded first by octant, and then by sort order
+ // of xyz into one of six sextants. All the table encoding takes place in
+ // the positive octant, in the region bounded by the half spaces:
+ //
+ // x >= z
+ // z >= y
+ // y >= 0
+ //
+ // This triangular shaped patch runs from 0 to 45 degrees in th, and
+ // from 0 to as much as 0.615479709 (MAX_Y_ANG) in psi. The xyz bounds
+ // of the patch is:
+ //
+ // (1, 0, 0) (1/sqrt(2), 0, 1/sqrt(2)) (1/sqrt(3), 1/sqrt(3), 1/sqrt(3))
+ //
+ // When dicing this space up into discrete points, the choice for y is
+ // linear quantization in psi. This means that if the y range is to be
+ // divided up into n segments, the angle of segment j is:
+ //
+ // psi(j) = MAX_Y_ANG*(j/n)
+ //
+ // The y height of the patch (in arc length) is *not* the same as the xz
+ // dimension. However, the subdivision quantization needs to treat xz and
+ // y equally. To achieve this, the th angles are re-parameterized as
+ // reflected psi angles. That is, the i-th point's th is:
+ //
+ // th(i) = asin(tan(psi(i))) = asin(tan(MAX_Y_ANG*(i/n)))
+ //
+ // To go the other direction, the angle th corresponds to the real index r
+ // (in the same 0-n range as i):
+ //
+ // r(th) = n*atan(sin(th))/MAX_Y_ANG
+ //
+ // Rounded to the nearest integer, this gives the closest integer index i
+ // to the xz angle th. Because the triangle has a straight edge on the
+ // line x=z, it is more intuitive to index the xz angles in reverse
+ // order. Thus the two equations above are replaced by:
+ //
+ // th(i) = asin(tan(psi(i))) = asin(tan(MAX_Y_ANG*((n-i)/n)))
+ //
+ // r(th) = n*(1 - atan(sin(th))/MAX_Y_ANG)
+ //
+ // Each level of quantization subdivides the triangular patch twice as
+ // densely. The case in which only the three vertices of the triangle are
+ // present is the first logical stage of representation, but because of
+ // how the table is encoded the first usable case starts one level of
+ // sub-division later. This three point level has an n of 2 by the above
+ // conventions.
+ //
+ private static final int MAX_UV_BITS = 6 ;
+ private static final int MAX_UV_ENTRIES = 64 ;
+
+ private static final double cgNormals[][][][] =
+ new double[MAX_UV_BITS+1][MAX_UV_ENTRIES+1][MAX_UV_ENTRIES+1][3] ;
+
+ private static final double MAX_Y_ANG = 0.615479709 ;
+ private static final double UNITY_14 = 16384.0 ;
+
+ private static void computeNormals() {
+ int inx, iny, inz, n ;
+ double th, psi, qnx, qny, qnz ;
+
+ for (int quant = 0 ; quant <= MAX_UV_BITS ; quant++) {
+ n = 1 << quant ;
+
+ for (int j = 0 ; j <= n ; j++) {
+ for (int i = 0 ; i <= n ; i++) {
+ if (i+j > n) continue ;
+
+ psi = MAX_Y_ANG*(j/((double) n)) ;
+ th = Math.asin(Math.tan(MAX_Y_ANG*((n-i)/((double) n)))) ;
+
+ qnx = Math.cos(th)*Math.cos(psi) ;
+ qny = Math.sin(psi) ;
+ qnz = Math.sin(th)*Math.cos(psi) ;
+
+ // The normal table uses 16-bit components and must be
+ // able to represent both +1.0 and -1.0, so convert the
+ // floating point normal components to fixed point with 14
+ // fractional bits, a unity bit, and a sign bit (s1.14).
+ // Set them back to get the float equivalent.
+ qnx = qnx*UNITY_14 ; inx = (int)qnx ;
+ qnx = inx ; qnx = qnx/UNITY_14 ;
+
+ qny = qny*UNITY_14 ; iny = (int)qny ;
+ qny = iny ; qny = qny/UNITY_14 ;
+
+ qnz = qnz*UNITY_14 ; inz = (int)qnz ;
+ qnz = inz ; qnz = qnz/UNITY_14 ;
+
+ cgNormals[quant][j][i][0] = qnx ;
+ cgNormals[quant][j][i][1] = qny ;
+ cgNormals[quant][j][i][2] = qnz ;
+ }
+ }
+ }
+ }
+
+ //
+ // An inverse sine table is used for each quantization level to take the Y
+ // component of a normal (which is the sine of the inclination angle) and
+ // obtain the closest quantized Y angle.
+ //
+ // At any level of compression, there are a fixed number of different Y
+ // angles (between 0 and MAX_Y_ANG). The inverse table is built to have
+ // slightly more than twice as many entries as y angles at any particular
+ // level; this ensures that the inverse look-up will get within one angle
+ // of the right one. The size of the table should be as small as
+ // possible, but with its delta sine still smaller than the delta sine
+ // between the last two angles to be encoded.
+ //
+ // Example: the inverse sine table has a maximum angle of 0.615479709. At
+ // the maximum resolution of 6 bits there are 65 discrete angles used,
+ // but twice as many are needed for thresholding between angles, so the
+ // delta angle is 0.615479709/128. The difference then between the last
+ // two angles to be encoded is:
+ // sin(0.615479709*128.0/128.0) - sin(0.615479709*127.0/128.0) = 0.003932730
+ //
+ // Using 8 significent bits below the binary point, fixed point can
+ // represent sines in increments of 0.003906250, just slightly smaller.
+ // However, because the maximum Y angle sine is 0.577350269, only 148
+ // instead of 256 table entries are needed.
+ //
+ private static final short inverseSine[][] = new short[MAX_UV_BITS+1][] ;
+
+ // UNITY_14 * sin(MAX_Y_ANGLE)
+ private static final short MAX_SIN_14BIT = 9459 ;
+
+ private static void computeInverseSineTables() {
+ int intSin, deltaSin, intAngle ;
+ double floatSin, floatAngle ;
+ short sin14[] = new short[MAX_UV_ENTRIES+1] ;
+
+ // Build table of sines in s1.14 fixed point for each of the
+ // discrete angles used at maximum resolution.
+ for (int i = 0 ; i <= MAX_UV_ENTRIES ; i++) {
+ sin14[i] = (short)(UNITY_14*Math.sin(i*MAX_Y_ANG/MAX_UV_ENTRIES)) ;
+ }
+
+ for (int quant = 0 ; quant <= MAX_UV_BITS ; quant++) {
+ switch (quant) {
+ default:
+ case 6:
+ // Delta angle: MAX_Y_ANGLE/128.0
+ // Bits below binary point for fixed point delta sine: 8
+ // Integer delta sine: 64
+ // Inverse sine table size: 148 entries
+ deltaSin = 1 << (14 - 8) ;
+ break ;
+ case 5:
+ // Delta angle: MAX_Y_ANGLE/64.0
+ // Bits below binary point for fixed point delta sine: 7
+ // Integer delta sine: 128
+ // Inverse sine table size: 74 entries
+ deltaSin = 1 << (14 - 7) ;
+ break ;
+ case 4:
+ // Delta angle: MAX_Y_ANGLE/32.0
+ // Bits below binary point for fixed point delta sine: 6
+ // Integer delta sine: 256
+ // Inverse sine table size: 37 entries
+ deltaSin = 1 << (14 - 6) ;
+ break ;
+ case 3:
+ // Delta angle: MAX_Y_ANGLE/16.0
+ // Bits below binary point for fixed point delta sine: 5
+ // Integer delta sine: 512
+ // Inverse sine table size: 19 entries
+ deltaSin = 1 << (14 - 5) ;
+ break ;
+ case 2:
+ // Delta angle: MAX_Y_ANGLE/8.0
+ // Bits below binary point for fixed point delta sine: 4
+ // Integer delta sine: 1024
+ // Inverse sine table size: 10 entries
+ deltaSin = 1 << (14 - 4) ;
+ break ;
+ case 1:
+ // Delta angle: MAX_Y_ANGLE/4.0
+ // Bits below binary point for fixed point delta sine: 3
+ // Integer delta sine: 2048
+ // Inverse sine table size: 5 entries
+ deltaSin = 1 << (14 - 3) ;
+ break ;
+ case 0:
+ // Delta angle: MAX_Y_ANGLE/2.0
+ // Bits below binary point for fixed point delta sine: 2
+ // Integer delta sine: 4096
+ // Inverse sine table size: 3 entries
+ deltaSin = 1 << (14 - 2) ;
+ break ;
+ }
+
+ inverseSine[quant] = new short[(MAX_SIN_14BIT/deltaSin) + 1] ;
+
+ intSin = 0 ;
+ for (int i = 0 ; i < inverseSine[quant].length ; i++) {
+ // Compute float representation of integer sine with desired
+ // number of fractional bits by effectively right shifting 14.
+ floatSin = intSin/UNITY_14 ;
+
+ // Compute the angle with this sine value and quantize it.
+ floatAngle = Math.asin(floatSin) ;
+ intAngle = (int)((floatAngle/MAX_Y_ANG) * (1 << quant)) ;
+
+ // Choose the closest of the three nearest quantized values
+ // intAngle-1, intAngle, and intAngle+1.
+ if (intAngle > 0) {
+ if (Math.abs(sin14[intAngle << (6-quant)] - intSin) >
+ Math.abs(sin14[(intAngle-1) << (6-quant)] - intSin))
+ intAngle = intAngle-1 ;
+ }
+
+ if (intAngle < (1 << quant)) {
+ if (Math.abs(sin14[intAngle << (6-quant)] - intSin) >
+ Math.abs(sin14[(intAngle+1) << (6-quant)] - intSin))
+ intAngle = intAngle+1 ;
+ }
+
+ inverseSine[quant][i] = (short)intAngle ;
+ intSin += deltaSin ;
+ }
+ }
+ }
+
+ /**
+ * Compute static tables needed for normal quantization.
+ */
+ static {
+ computeNormals() ;
+ computeInverseSineTables() ;
+ }
+
+ /**
+ * Quantize the floating point normal to a 6-bit octant/sextant plus u,v
+ * components of [0..6] bits. Full resolution is 18 bits and the minimum
+ * is 6 bits.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param table HuffmanTable for collecting data about the quantized
+ * representation of this element
+ */
+ void quantize(CompressionStream stream, HuffmanTable huffmanTable) {
+ double nx, ny, nz, t ;
+
+ // Clamp UV quantization.
+ int quant =
+ (stream.normalQuant < 0? 0 :
+ (stream.normalQuant > 6? 6 : stream.normalQuant)) ;
+
+ nx = normalX ;
+ ny = normalY ;
+ nz = normalZ ;
+
+ octant = 0 ;
+ sextant = 0 ;
+ u = 0 ;
+ v = 0 ;
+
+ // Normalize the fixed point normal to the positive signed octant.
+ if (nx < 0.0) {
+ octant |= 4 ;
+ nx = -nx ;
+ }
+ if (ny < 0.0) {
+ octant |= 2 ;
+ ny = -ny ;
+ }
+ if (nz < 0.0) {
+ octant |= 1 ;
+ nz = -nz ;
+ }
+
+ // Normalize the fixed point normal to the proper sextant of the octant.
+ if (nx < ny) {
+ sextant |= 1 ;
+ t = nx ;
+ nx = ny ;
+ ny = t ;
+ }
+ if (nz < ny) {
+ sextant |= 2 ;
+ t = ny ;
+ ny = nz ;
+ nz = t ;
+ }
+ if (nx < nz) {
+ sextant |= 4 ;
+ t = nx ;
+ nx = nz ;
+ nz = t ;
+ }
+
+ // Convert the floating point y component to s1.14 fixed point.
+ int yInt = (int)(ny * UNITY_14) ;
+
+ // The y component of the normal is the sine of the y angle. Quantize
+ // the y angle by using the fixed point y component as an index into
+ // the inverse sine table of the correct size for the quantization
+ // level. (12 - quant) bits of the s1.14 y normal component are
+ // rolled off with a right shift; the remaining bits then match the
+ // number of bits used to represent the delta sine of the table.
+ int yIndex = inverseSine[quant][yInt >> (12-quant)] ;
+
+ // Search the two xz rows near y for the best match.
+ int ii = 0 ;
+ int jj = 0 ;
+ int n = 1 << quant ;
+ double dot, bestDot = -1 ;
+
+ for (int j = yIndex-1 ; j < yIndex+1 && j <= n ; j++) {
+ if (j < 0)
+ continue ;
+
+ for (int i = 0 ; i <= n ; i++) {
+ if (i+j > n)
+ continue ;
+
+ dot = nx * cgNormals[quant][j][i][0] +
+ ny * cgNormals[quant][j][i][1] +
+ nz * cgNormals[quant][j][i][2] ;
+
+ if (dot > bestDot) {
+ bestDot = dot ;
+ ii = i ;
+ jj = j ;
+ }
+ }
+ }
+
+ // Convert u and v to standard grid form.
+ u = ii << (6 - quant) ;
+ v = jj << (6 - quant) ;
+
+ // Check for special normals and specially encode them.
+ specialNormal = false ;
+ if (u == 64 && v == 0) {
+ // six coordinate axes case
+ if (sextant == 0 || sextant == 2) {
+ // +/- x-axis
+ specialSextant = 0x6 ;
+ specialOctant = ((octant & 4) != 0)? 0x2 : 0 ;
+
+ } else if (sextant == 3 || sextant == 1) {
+ // +/- y-axis
+ specialSextant = 0x6 ;
+ specialOctant = 4 | (((octant & 2) != 0)? 0x2 : 0) ;
+
+ } else if (sextant == 5 || sextant == 4) {
+ // +/- z-axis
+ specialSextant = 0x7 ;
+ specialOctant = ((octant & 1) != 0)? 0x2 : 0 ;
+ }
+ specialNormal = true ;
+ u = v = 0 ;
+
+ } else if (u == 0 && v == 64) {
+ // eight mid point case
+ specialSextant = 6 | (octant >> 2) ;
+ specialOctant = ((octant & 0x3) << 1) | 1 ;
+ specialNormal = true ;
+ u = v = 0 ;
+ }
+
+ // Compute deltas if possible.
+ // Use the non-normalized ii and jj indices.
+ int du = 0 ;
+ int dv = 0 ;
+ int uv64 = 64 >> (6 - quant) ;
+
+ absolute = false ;
+ if (stream.firstNormal || stream.normalQuantChanged ||
+ stream.lastSpecialNormal || specialNormal) {
+ // The first normal by definition is absolute, and normals cannot
+ // be represented as deltas to or from special normals, nor from
+ // normals with a different quantization.
+ absolute = true ;
+ stream.firstNormal = false ;
+ stream.normalQuantChanged = false ;
+
+ } else if (stream.lastOctant == octant &&
+ stream.lastSextant == sextant) {
+ // Deltas are always allowed within the same sextant/octant.
+ du = ii - stream.lastU ;
+ dv = jj - stream.lastV ;
+
+ } else if (stream.lastOctant != octant &&
+ stream.lastSextant == sextant &&
+ (((sextant == 1 || sextant == 5) &&
+ (stream.lastOctant & 3) == (octant & 3)) ||
+ ((sextant == 0 || sextant == 4) &&
+ (stream.lastOctant & 5) == (octant & 5)) ||
+ ((sextant == 2 || sextant == 3) &&
+ (stream.lastOctant & 6) == (octant & 6)))) {
+ // If the sextants are the same, the octants can differ only when
+ // they are bordering each other on the same edge that the
+ // sextant has.
+ du = ii - stream.lastU ;
+ dv = -jj - stream.lastV ;
+
+ // Can't delta by less than -64.
+ if (dv < -uv64) absolute = true ;
+
+ // Can't delta doubly defined points.
+ if (jj == 0) absolute = true ;
+
+ } else if (stream.lastOctant == octant &&
+ stream.lastSextant != sextant &&
+ ((sextant == 0 && stream.lastSextant == 4) ||
+ (sextant == 4 && stream.lastSextant == 0) ||
+ (sextant == 1 && stream.lastSextant == 5) ||
+ (sextant == 5 && stream.lastSextant == 1) ||
+ (sextant == 2 && stream.lastSextant == 3) ||
+ (sextant == 3 && stream.lastSextant == 2))) {
+ // If the octants are the same, the sextants must border on
+ // the i side (this case) or the j side (next case).
+ du = -ii - stream.lastU ;
+ dv = jj - stream.lastV ;
+
+ // Can't delta by less than -64.
+ if (du < -uv64) absolute = true ;
+
+ // Can't delta doubly defined points.
+ if (ii == 0) absolute = true ;
+
+ } else if (stream.lastOctant == octant &&
+ stream.lastSextant != sextant &&
+ ((sextant == 0 && stream.lastSextant == 2) ||
+ (sextant == 2 && stream.lastSextant == 0) ||
+ (sextant == 1 && stream.lastSextant == 3) ||
+ (sextant == 3 && stream.lastSextant == 1) ||
+ (sextant == 4 && stream.lastSextant == 5) ||
+ (sextant == 5 && stream.lastSextant == 4))) {
+ // If the octants are the same, the sextants must border on
+ // the j side (this case) or the i side (previous case).
+ if (((ii + jj ) != uv64) && (ii != 0) && (jj != 0)) {
+ du = uv64 - ii - stream.lastU ;
+ dv = uv64 - jj - stream.lastV ;
+
+ // Can't delta by greater than +63.
+ if ((du >= uv64) || (dv >= uv64))
+ absolute = true ;
+ } else
+ // Can't delta doubly defined points.
+ absolute = true ;
+
+ } else
+ // Can't delta this normal.
+ absolute = true ;
+
+ if (absolute == false) {
+ // Convert du and dv to standard grid form.
+ u = du << (6 - quant) ;
+ v = dv << (6 - quant) ;
+ }
+
+ // Compute length and shift common to all components.
+ computeLengthShift(u, v) ;
+
+ if (absolute && length > 6) {
+ // Absolute normal u, v components are unsigned 6-bit integers, so
+ // truncate the 0 sign bit for values > 0x001f.
+ length = 6 ;
+ }
+
+ // Add this element to the Huffman table associated with this stream.
+ huffmanTable.addNormalEntry(length, shift, absolute) ;
+
+ // Save current normal as last.
+ stream.lastSextant = sextant ;
+ stream.lastOctant = octant ;
+ stream.lastU = ii ;
+ stream.lastV = jj ;
+ stream.lastSpecialNormal = specialNormal ;
+
+ // Copy and retain absolute normal for mesh buffer lookup.
+ uAbsolute = ii ;
+ vAbsolute = jj ;
+ }
+
+ /**
+ * Output a setNormal command.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputCommand(HuffmanTable table, CommandStream output) {
+ outputNormal(table, output, CommandStream.SET_NORM, 8) ;
+ }
+
+ /**
+ * Output a normal subcommand.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputSubcommand(HuffmanTable table, CommandStream output) {
+ outputNormal(table, output, 0, 6) ;
+ }
+
+ //
+ // Output the final compressed bits to the output command stream.
+ //
+ private void outputNormal(HuffmanTable table, CommandStream output,
+ int header, int headerLength) {
+
+ HuffmanNode t ;
+
+ // Look up the Huffman token for this compression stream element.
+ t = table.getNormalEntry(length, shift, absolute) ;
+
+ // Construct the normal subcommand.
+ int componentLength = t.dataLength - t.shift ;
+ int subcommandLength = 0 ;
+ long normalSubcommand = 0 ;
+
+ if (absolute) {
+ // A 3-bit sextant and a 3-bit octant are always present.
+ subcommandLength = t.tagLength + 6 ;
+
+ if (specialNormal)
+ // Use the specially-encoded sextant and octant.
+ normalSubcommand =
+ (t.tag << 6) | (specialSextant << 3) | specialOctant ;
+ else
+ // Use the general encoding rule.
+ normalSubcommand =
+ (t.tag << 6) | (sextant << 3) | octant ;
+ } else {
+ // The tag is immediately followed by the u and v delta components.
+ subcommandLength = t.tagLength ;
+ normalSubcommand = t.tag ;
+ }
+
+ // Add the u and v values to the subcommand.
+ subcommandLength += (2 * componentLength) ;
+
+ u = (u >> t.shift) & (int)lengthMask[componentLength] ;
+ v = (v >> t.shift) & (int)lengthMask[componentLength] ;
+
+ normalSubcommand =
+ (normalSubcommand << (2 * componentLength)) |
+ (u << (1 * componentLength)) |
+ (v << (0 * componentLength)) ;
+
+ if (subcommandLength < 6) {
+ // The header will have some empty bits. The Huffman tag
+ // computation will prevent this if necessary.
+ header |= (int)(normalSubcommand << (6 - subcommandLength)) ;
+ subcommandLength = 0 ;
+ }
+ else {
+ // Move the 1st 6 bits of the subcommand into the header.
+ header |= (int)(normalSubcommand >>> (subcommandLength - 6)) ;
+ subcommandLength -= 6 ;
+ }
+
+ // Add the header and body to the output buffer.
+ output.addCommand(header, headerLength,
+ normalSubcommand, subcommandLength) ;
+ }
+
+ public String toString() {
+ String fixed ;
+
+ if (specialNormal)
+ fixed = " special normal, sextant " + specialSextant +
+ " octant " + specialOctant ;
+
+ else if (absolute)
+ fixed = " sextant " + sextant + " octant " + octant +
+ " u " + u + " v " + v ;
+ else
+ fixed = " du " + u + " dv " + v ;
+
+ return
+ "normal: " + normalX + " " + normalY + " " + normalZ + "\n"
+ + fixed + "\n" + " length " + length + " shift " + shift +
+ (absolute? " absolute" : " relative") ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamVertex.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamVertex.java
new file mode 100644
index 0000000..77e396d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamVertex.java
@@ -0,0 +1,322 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Color4f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+
+/**
+ * This class represents a vertex in a compression stream. It maintains both
+ * floating-point and quantized representations of the vertex position along
+ * with meshing and vertex replacement flags for line and surface
+ * primitives. If normals or colors are bundled with geometry vertices then
+ * instances of this class will also contain references to normal or color
+ * stream elements.
+ */
+class CompressionStreamVertex extends CompressionStreamElement {
+ private int X, Y, Z ;
+ private int meshFlag ;
+ private int stripFlag ;
+ private float floatX, floatY, floatZ ;
+
+ int xAbsolute, yAbsolute, zAbsolute ;
+ CompressionStreamColor color = null ;
+ CompressionStreamNormal normal = null ;
+
+ /**
+ * Create a CompressionStreamVertex with the given parameters.
+ *
+ * @param stream CompressionStream associated with this vertex
+ * @param p position
+ * @param n normal bundled with this vertex or null if not bundled
+ * @param c color bundled with this vertex or null if not bundled
+ * @param stripFlag CompressionStream.RESTART,
+ * CompressionStream.REPLACE_OLDEST, or CompressionStream.REPLACE_MIDDLE
+ * @param meshFlag CompressionStream.MESH_PUSH or
+ * CompressionStream.NO_MESH_PUSH
+ */
+ CompressionStreamVertex(CompressionStream stream,
+ Point3f p, Vector3f n, Color3f c,
+ int stripFlag, int meshFlag) {
+
+ this(stream, p, n, stripFlag, meshFlag) ;
+
+ if (stream.vertexColor3)
+ color = new CompressionStreamColor(stream, c) ;
+ }
+
+ /**
+ * Create a CompressionStreamVertex with the given parameters.
+ *
+ * @param stream CompressionStream associated with this vertex
+ * @param p position
+ * @param n normal bundled with this vertex or null if not bundled
+ * @param c color bundled with this vertex or null if not bundled
+ * @param stripFlag CompressionStream.RESTART,
+ * CompressionStream.REPLACE_OLDEST, or CompressionStream.REPLACE_MIDDLE
+ * @param meshFlag CompressionStream.MESH_PUSH or
+ * CompressionStream.NO_MESH_PUSH
+ */
+ CompressionStreamVertex(CompressionStream stream,
+ Point3f p, Vector3f n, Color4f c,
+ int stripFlag, int meshFlag) {
+
+ this(stream, p, n, stripFlag, meshFlag) ;
+
+ if (stream.vertexColor4)
+ color = new CompressionStreamColor(stream, c) ;
+ }
+
+ /**
+ * Create a CompressionStreamVertex with the given parameters.
+ *
+ * @param stream CompressionStream associated with this vertex
+ * @param p position
+ * @param n normal bundled with this vertex or null if not bundled
+ * @param stripFlag CompressionStream.RESTART,
+ * CompressionStream.REPLACE_OLDEST, or CompressionStream.REPLACE_MIDDLE
+ * @param meshFlag CompressionStream.MESH_PUSH or
+ * CompressionStream.NO_MESH_PUSH
+ */
+ CompressionStreamVertex(CompressionStream stream, Point3f p, Vector3f n,
+ int stripFlag, int meshFlag) {
+
+ this.stripFlag = stripFlag ;
+ this.meshFlag = meshFlag ;
+ this.floatX = p.x ;
+ this.floatY = p.y ;
+ this.floatZ = p.z ;
+
+ stream.byteCount += 12 ;
+ stream.vertexCount++ ;
+
+ if (p.x < stream.mcBounds[0].x) stream.mcBounds[0].x = p.x ;
+ if (p.y < stream.mcBounds[0].y) stream.mcBounds[0].y = p.y ;
+ if (p.z < stream.mcBounds[0].z) stream.mcBounds[0].z = p.z ;
+
+ if (p.x > stream.mcBounds[1].x) stream.mcBounds[1].x = p.x ;
+ if (p.y > stream.mcBounds[1].y) stream.mcBounds[1].y = p.y ;
+ if (p.z > stream.mcBounds[1].z) stream.mcBounds[1].z = p.z ;
+
+ if (stream.vertexNormals)
+ normal = new CompressionStreamNormal(stream, n) ;
+ }
+
+ /**
+ * Quantize the floating point position to fixed point integer components
+ * of the specified number of bits. The bit length can range from 1 to 16.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param table HuffmanTable for collecting data about the quantized
+ * representation of this element
+ */
+ void quantize(CompressionStream stream, HuffmanTable huffmanTable) {
+ double px, py, pz ;
+
+ // Clamp quantization.
+ int quant =
+ (stream.positionQuant < 1? 1 :
+ (stream.positionQuant > 16? 16 : stream.positionQuant)) ;
+
+ absolute = false ;
+ if (stream.firstPosition || stream.positionQuantChanged) {
+ absolute = true ;
+ stream.lastPosition[0] = 0 ;
+ stream.lastPosition[1] = 0 ;
+ stream.lastPosition[2] = 0 ;
+ stream.firstPosition = false ;
+ stream.positionQuantChanged = false ;
+ }
+
+ // Normalize position to the unit cube. This is bounded by the open
+ // intervals (-1..1) on each axis.
+ px = (floatX - stream.center[0]) * stream.scale ;
+ py = (floatY - stream.center[1]) * stream.scale ;
+ pz = (floatZ - stream.center[2]) * stream.scale ;
+
+ // Convert the floating point position to s.15 2's complement.
+ // ~1.0 -> 32767 (0x00007fff) [ ~1.0 = 32767.0/32768.0]
+ // ~-1.0 -> -32767 (0xffff8001) [~-1.0 = -32767.0/32768.0]
+ X = (int)(px * 32768.0) ;
+ Y = (int)(py * 32768.0) ;
+ Z = (int)(pz * 32768.0) ;
+
+ // Compute quantized values.
+ X &= quantizationMask[quant] ;
+ Y &= quantizationMask[quant] ;
+ Z &= quantizationMask[quant] ;
+
+ // Update quantized bounds.
+ if (X < stream.qcBounds[0].x) stream.qcBounds[0].x = X ;
+ if (Y < stream.qcBounds[0].y) stream.qcBounds[0].y = Y ;
+ if (Z < stream.qcBounds[0].z) stream.qcBounds[0].z = Z ;
+
+ if (X > stream.qcBounds[1].x) stream.qcBounds[1].x = X ;
+ if (Y > stream.qcBounds[1].y) stream.qcBounds[1].y = Y ;
+ if (Z > stream.qcBounds[1].z) stream.qcBounds[1].z = Z ;
+
+ // Copy and retain absolute position for mesh buffer lookup.
+ xAbsolute = X ;
+ yAbsolute = Y ;
+ zAbsolute = Z ;
+
+ // Compute deltas.
+ X -= stream.lastPosition[0] ;
+ Y -= stream.lastPosition[1] ;
+ Z -= stream.lastPosition[2] ;
+
+ // Update last values.
+ stream.lastPosition[0] += X ;
+ stream.lastPosition[1] += Y ;
+ stream.lastPosition[2] += Z ;
+
+ // Deltas which exceed the range of 16-bit signed 2's complement
+ // numbers are handled by sign-extension of the 16th bit in order to
+ // effect a 16-bit wrap-around.
+ X = (X << 16) >> 16 ;
+ Y = (Y << 16) >> 16 ;
+ Z = (Z << 16) >> 16 ;
+
+ // Compute length and shift common to all components.
+ computeLengthShift(X, Y, Z) ;
+
+ // 0-length components are allowed only for normals.
+ if (length == 0)
+ length = 1 ;
+
+ // Add this element to the Huffman table associated with this stream.
+ huffmanTable.addPositionEntry(length, shift, absolute) ;
+
+ // Quantize any bundled color or normal.
+ if (color != null)
+ color.quantize(stream, huffmanTable) ;
+
+ if (normal != null)
+ normal.quantize(stream, huffmanTable) ;
+
+ // Push this vertex into the mesh buffer mirror, if necessary, so it
+ // can be retrieved for computing deltas when mesh buffer references
+ // are subsequently encountered during the quantization pass.
+ if (meshFlag == stream.MESH_PUSH)
+ stream.meshBuffer.push(this) ;
+ }
+
+ /**
+ * Output the final compressed bits to the compression command stream.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputCommand(HuffmanTable huffmanTable, CommandStream outputBuffer) {
+
+ HuffmanNode t ;
+ int command = CommandStream.VERTEX ;
+
+ // Look up the Huffman token for this compression stream element. The
+ // values of length and shift found there will override the
+ // corresponding fields in this element, which represent best-case
+ // compression without regard to tag length.
+ t = huffmanTable.getPositionEntry(length, shift, absolute) ;
+
+ // Construct the position subcommand.
+ int componentLength = t.dataLength - t.shift ;
+ int subcommandLength = t.tagLength + (3 * componentLength) ;
+
+ X = (X >> t.shift) & (int)lengthMask[componentLength] ;
+ Y = (Y >> t.shift) & (int)lengthMask[componentLength] ;
+ Z = (Z >> t.shift) & (int)lengthMask[componentLength] ;
+
+ long positionSubcommand =
+ (((long)t.tag) << (3 * componentLength)) |
+ (((long)X) << (2 * componentLength)) |
+ (((long)Y) << (1 * componentLength)) |
+ (((long)Z) << (0 * componentLength)) ;
+
+ if (subcommandLength < 6) {
+ // The header will have some empty bits. The Huffman tag
+ // computation will prevent this if necessary.
+ command |= (int)(positionSubcommand << (6 - subcommandLength)) ;
+ subcommandLength = 0 ;
+ }
+ else {
+ // Move the 1st 6 bits of the subcommand into the header.
+ command |= (int)(positionSubcommand >>> (subcommandLength - 6)) ;
+ subcommandLength -= 6 ;
+ }
+
+ // Construct the vertex command body.
+ long body =
+ (((long)stripFlag) << (subcommandLength + 1)) |
+ (((long)meshFlag) << (subcommandLength + 0)) |
+ (positionSubcommand & lengthMask[subcommandLength]) ;
+
+ // Add the vertex command to the output buffer.
+ outputBuffer.addCommand(command, 8, body, subcommandLength + 3) ;
+
+ // Output any normal and color subcommands.
+ if (normal != null)
+ normal.outputSubcommand(huffmanTable, outputBuffer) ;
+
+ if (color != null)
+ color.outputSubcommand(huffmanTable, outputBuffer) ;
+ }
+
+ public String toString() {
+ String d = absolute? "" : "delta " ;
+ String c = (color == null? "": "\n\n " + color.toString()) ;
+ String n = (normal == null? "": "\n\n " + normal.toString()) ;
+
+ return
+ "position: " + floatX + " " + floatY + " " + floatZ + "\n" +
+ "fixed point " + d + + X + " " + Y + " " + Z + "\n" +
+ "length " + length + " shift " + shift +
+ (absolute? " absolute" : " relative") + "\n" +
+ "strip flag " + stripFlag + " mesh flag " + meshFlag +
+ c + n ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedStrip.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedStrip.java
new file mode 100644
index 0000000..194c9c9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedStrip.java
@@ -0,0 +1,908 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+
+/**
+ * This class provides static methods to support topological
+ * transformations on generalized strips. This is used by the
+ * GeometryDecompressor. These methods only need to look at the
+ * vertex replacement flags to determine how the vertices in the strip
+ * are connected. The connections are rearranged in different ways to
+ * transform generalized strips to GeometryArray representations.
+ *
+ * @see GeneralizedStripFlags
+ * @see GeneralizedVertexList
+ * @see GeometryDecompressor
+ */
+class GeneralizedStrip {
+ private static final boolean debug = false ;
+
+ // Private convenience copies of various constants.
+ private static final int CW =
+ GeneralizedStripFlags.FRONTFACE_CW ;
+ private static final int CCW =
+ GeneralizedStripFlags.FRONTFACE_CCW ;
+ private static final int RESTART_CW =
+ GeneralizedStripFlags.RESTART_CW ;
+ private static final int RESTART_CCW =
+ GeneralizedStripFlags.RESTART_CCW ;
+ private static final int REPLACE_MIDDLE =
+ GeneralizedStripFlags.REPLACE_MIDDLE ;
+ private static final int REPLACE_OLDEST =
+ GeneralizedStripFlags.REPLACE_OLDEST ;
+
+ /**
+ * The IntList is like an ArrayList, but avoids the Integer
+ * object wrapper and accessor overhead for simple lists of ints.
+ */
+ static class IntList {
+ /**
+ * The array of ints.
+ */
+ int ints[] ;
+
+ /**
+ * The number of ints in this instance.
+ */
+ int count ;
+
+ /**
+ * Construct a new empty IntList of the given initial size.
+ * @param initialSize initial size of the backing array
+ */
+ IntList(int initialSize) {
+ ints = new int[initialSize] ;
+ count = 0 ;
+ }
+
+ /**
+ * Constructs an IntList with the given contents.
+ * @param ints the array of ints to use as the contents
+ */
+ IntList(int ints[]) {
+ this.ints = ints ;
+ this.count = ints.length ;
+ }
+
+ /**
+ * Add a new int to the end of this list.
+ * @param i the int to be appended to this list
+ */
+ void add(int i) {
+ if (count == ints.length) {
+ int newints[] = new int[2*count] ;
+ System.arraycopy(ints, 0, newints, 0, count) ;
+ ints = newints ;
+ if (debug)
+ System.out.println
+ ("GeneralizedStrip.IntList: reallocated " +
+ (2*count) + " ints") ;
+ }
+ ints[count++] = i ;
+ }
+
+ /**
+ * Trim the backing array to the current count and return the
+ * resulting backing array.
+ */
+ int[] trim() {
+ if (count != ints.length) {
+ int newints[] = new int[count] ;
+ System.arraycopy(ints, 0, newints, 0, count) ;
+ ints = newints ;
+ }
+ return ints ;
+ }
+
+ /**
+ * Fill the list with consecutive integers starting from 0.
+ */
+ void fillAscending() {
+ for (int i = 0 ; i < ints.length ; i++)
+ ints[i] = i ;
+
+ count = ints.length ;
+ }
+
+ public String toString() {
+ String s = new String("[") ;
+ for (int i = 0 ; i < count-1 ; i++)
+ s = s + Integer.toString(ints[i]) + ", " ;
+ return s + Integer.toString(ints[count-1]) + "]" ;
+ }
+ }
+
+ /**
+ * The StripArray class is used as the output of some conversion methods
+ * in the GeneralizedStrip class.
+ */
+ static class StripArray {
+ /**
+ * A list of indices into the vertices of the original generalized
+ * strip. It specifies the order in which vertices in the original
+ * strip should be followed to build GeometryArray objects.
+ */
+ IntList vertices ;
+
+ /**
+ * A list of strip counts.
+ */
+ IntList stripCounts ;
+
+ /**
+ * Creates a StripArray with the specified vertices and stripCounts.
+ * @param vertices IntList containing vertex indicies.
+ * @param stripCounts IntList containing strip lengths.
+ */
+ StripArray(IntList vertices, IntList stripCounts) {
+ this.vertices = vertices ;
+ this.stripCounts = stripCounts ;
+ }
+ }
+
+ /**
+ * Interprets the vertex flags associated with a class implementing
+ * GeneralizedStripFlags, constructing and returning a 2-element array of
+ * StripArray objects. The first StripArray will contain triangle strips
+ * and the second will contain triangle fans.
+ *
+ * @param vertices an object implementing GeneralizedStripFlags
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @return a 2-element array containing strips in 0 and fans in 1
+ */
+ static StripArray[] toStripsAndFans(GeneralizedStripFlags vertices,
+ int frontFace) {
+
+ int size = vertices.getFlagCount() ;
+
+ // Initialize IntLists to worst-case sizes.
+ IntList stripVerts = new IntList(size*3) ;
+ IntList fanVerts = new IntList(size*3) ;
+ IntList stripCounts = new IntList(size) ;
+ IntList fanCounts = new IntList(size) ;
+
+ toStripsAndFans(vertices, frontFace,
+ stripVerts, stripCounts, fanVerts, fanCounts) ;
+
+ // Construct the StripArray output.
+ StripArray sa[] = new StripArray[2] ;
+
+ if (stripCounts.count > 0)
+ sa[0] = new StripArray(stripVerts, stripCounts) ;
+
+ if (fanCounts.count > 0)
+ sa[1] = new StripArray(fanVerts, fanCounts) ;
+
+ return sa ;
+ }
+
+ private static void toStripsAndFans(GeneralizedStripFlags vertices,
+ int frontFace,
+ IntList stripVerts,
+ IntList stripCounts,
+ IntList fanVerts,
+ IntList fanCounts) {
+ int newFlag, curFlag, winding ;
+ int v, size, stripStart, stripLength ;
+ boolean transition = false ;
+
+ stripStart = 0 ;
+ stripLength = 3 ;
+ curFlag = vertices.getFlag(0) ;
+ winding = (curFlag == RESTART_CW ? CW : CCW) ;
+ size = vertices.getFlagCount() ;
+
+ // Vertex replace flags for the first 3 vertices are irrelevant since
+ // they can only define a single triangle. The first meaningful
+ // replace flag starts at the 4th vertex.
+ v = 3 ;
+ if (v < size)
+ curFlag = vertices.getFlag(v) ;
+
+ while (v < size) {
+ newFlag = vertices.getFlag(v) ;
+
+ if ((newFlag == curFlag) &&
+ (newFlag != RESTART_CW) && (newFlag != RESTART_CCW)) {
+ // The last flag was the same as this one, and it wasn't a
+ // restart: proceed to the next vertex.
+ stripLength++ ;
+ v++ ;
+
+ } else {
+ // Either this vertex flag changed from the last one, or
+ // the flag explicitly specifies a restart: process the
+ // last strip and start up a new one.
+ if (curFlag == REPLACE_MIDDLE)
+ addFan(fanVerts, fanCounts, stripStart, stripLength,
+ frontFace, winding, transition) ;
+ else
+ addStrip(stripVerts, stripCounts, stripStart, stripLength,
+ frontFace, winding) ;
+
+ // Restart: skip to the 4th vertex of the new strip.
+ if ((newFlag == RESTART_CW) || (newFlag == RESTART_CCW)) {
+ winding = (newFlag == RESTART_CW ? CW : CCW) ;
+ stripStart = v ;
+ stripLength = 3 ;
+ v += 3 ;
+ transition = false ;
+ if (v < size)
+ curFlag = vertices.getFlag(v) ;
+ }
+ // Strip/fan transition: decrement start of strip.
+ else {
+ if (newFlag == REPLACE_OLDEST) {
+ // Flip winding order when transitioning from fans
+ // to strips.
+ winding = (winding == CW ? CCW : CW) ;
+ stripStart = v-2 ;
+ stripLength = 3 ;
+ } else {
+ // Flip winding order when transitioning from
+ // strips to fans only if the preceding strip has
+ // an even number of vertices.
+ if ((stripLength & 0x01) == 0)
+ winding = (winding == CW ? CCW : CW) ;
+ stripStart = v-3 ;
+ stripLength = 4 ;
+ }
+ v++ ;
+ transition = true ;
+ curFlag = newFlag ;
+ }
+ }
+ }
+
+ // Finish off the last strip or fan.
+ // If v > size then the strip is degenerate.
+ if (v == size)
+ if (curFlag == REPLACE_MIDDLE)
+ addFan(fanVerts, fanCounts, stripStart, stripLength,
+ frontFace, winding, transition) ;
+ else
+ addStrip(stripVerts, stripCounts, stripStart, stripLength,
+ frontFace, winding) ;
+ else
+ throw new IllegalArgumentException
+ (J3dUtilsI18N.getString("GeneralizedStrip0")) ;
+
+ if (debug) {
+ System.out.println("GeneralizedStrip.toStripsAndFans") ;
+ if (v > size)
+ System.out.println(" ended with a degenerate triangle:" +
+ " number of vertices: " + (v-size)) ;
+
+ System.out.println("\n number of strips: " + stripCounts.count) ;
+ if (stripCounts.count > 0) {
+ System.out.println(" number of vertices: " + stripVerts.count) ;
+ System.out.println(" vertices/strip: " +
+ (float)stripVerts.count/stripCounts.count) ;
+ System.out.println(" strip counts: " + stripCounts.toString()) ;
+ // System.out.println(" indices: " + stripVerts.toString()) ;
+ }
+
+ System.out.println("\n number of fans: " + fanCounts.count) ;
+ if (fanCounts.count > 0) {
+ System.out.println(" number of vertices: " + fanVerts.count) ;
+ System.out.println(" vertices/strip: " +
+ (float)fanVerts.count/fanCounts.count) ;
+ System.out.println(" fan counts: " + fanCounts.toString()) ;
+ // System.out.println(" indices: " + fanVerts.toString()) ;
+ }
+ System.out.println("\n total vertices: " +
+ (stripVerts.count + fanVerts.count) +
+ "\n original number of vertices: " + size +
+ "\n") ;
+ }
+ }
+
+ //
+ // Java 3D specifies that the vertices of front-facing polygons
+ // have counter-clockwise (CCW) winding order when projected to
+ // the view surface. Polygons with clockwise (CW) vertex winding
+ // will be culled as back-facing by default.
+ //
+ // Generalized triangle strips can flip the orientation of their
+ // triangles with the RESTART_CW and RESTART_CCW vertex flags.
+ // Strips flagged with an orientation opposite to what has been
+ // specified as front-facing must have their windings reversed in
+ // order to have the correct face orientation when represented as
+ // GeometryArray objects.
+ //
+ private static void addStrip(IntList stripVerts,
+ IntList stripCounts,
+ int start, int length,
+ int frontFace, int winding) {
+ int vindex = start ;
+
+ if (winding == frontFace) {
+ // Maintain original order.
+ stripCounts.add(length) ;
+ while (vindex < start + length) {
+ stripVerts.add(vindex++) ;
+ }
+ } else if ((length & 0x1) == 1) {
+ // Reverse winding order if number of vertices is odd.
+ stripCounts.add(length) ;
+ vindex += length-1 ;
+ while (vindex >= start) {
+ stripVerts.add(vindex--) ;
+ }
+ } else if (length == 4) {
+ // Swap middle vertices.
+ stripCounts.add(4) ;
+ stripVerts.add(vindex) ;
+ stripVerts.add(vindex+2) ;
+ stripVerts.add(vindex+1) ;
+ stripVerts.add(vindex+3) ;
+ } else {
+ // Make the 1st triangle a singleton with reverse winding.
+ stripCounts.add(3) ;
+ stripVerts.add(vindex) ;
+ stripVerts.add(vindex+2) ;
+ stripVerts.add(vindex+1) ;
+ if (length > 3) {
+ // Copy the rest of the vertices in original order.
+ vindex++ ;
+ stripCounts.add(length-1) ;
+ while (vindex < start + length) {
+ stripVerts.add(vindex++) ;
+ }
+ }
+ }
+ }
+
+ private static void addFan(IntList fanVerts,
+ IntList fanCounts,
+ int start, int length,
+ int frontFace, int winding,
+ boolean transition) {
+ int vindex = start ;
+ fanVerts.add(vindex++) ;
+
+ if (winding == frontFace) {
+ if (transition) {
+ // Skip 1st triangle if this is the result of a transition.
+ fanCounts.add(length-1) ;
+ vindex++ ;
+ } else {
+ fanCounts.add(length) ;
+ fanVerts.add(vindex++) ;
+ }
+ while (vindex < start + length) {
+ fanVerts.add(vindex++) ;
+ }
+ } else {
+ // Reverse winding order.
+ vindex += length-2 ;
+ while (vindex > start+1) {
+ fanVerts.add(vindex--) ;
+ }
+ if (transition) {
+ // Skip 1st triangle if this is the result of a transition.
+ fanCounts.add(length-1) ;
+ } else {
+ fanCounts.add(length) ;
+ fanVerts.add(vindex) ;
+ }
+ }
+ }
+
+ /**
+ * Interprets the vertex flags associated with a class implementing
+ * GeneralizedStripFlags, constructing and returning a StripArray containing
+ * exclusively strips.
+ *
+ * @param vertices an object implementing GeneralizedStripFlags
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @return a StripArray containing the converted strips
+ */
+ static StripArray toTriangleStrips(GeneralizedStripFlags vertices,
+ int frontFace) {
+
+ int size = vertices.getFlagCount() ;
+
+ // initialize lists to worst-case sizes.
+ IntList stripVerts = new IntList(size*3) ;
+ IntList fanVerts = new IntList(size*3) ;
+ IntList stripCounts = new IntList(size) ;
+ IntList fanCounts = new IntList(size) ;
+
+ toStripsAndFans(vertices, frontFace,
+ stripVerts, stripCounts, fanVerts, fanCounts) ;
+
+ if (fanCounts.count == 0)
+ if (stripCounts.count > 0)
+ return new StripArray(stripVerts, stripCounts) ;
+ else
+ return null ;
+
+ // convert each fan to one or more strips
+ int i, v = 0 ;
+ for (i = 0 ; i < fanCounts.count ; i++) {
+ fanToStrips(v, fanCounts.ints[i], fanVerts.ints,
+ stripVerts, stripCounts, false) ;
+ v += fanCounts.ints[i] ;
+ }
+
+ // create the StripArray output
+ StripArray sa = new StripArray(stripVerts, stripCounts) ;
+
+ if (debug) {
+ System.out.println("GeneralizedStrip.toTriangleStrips" +
+ "\n number of strips: " +
+ sa.stripCounts.count) ;
+ if (sa.stripCounts.count > 0) {
+ System.out.println(" number of vertices: " +
+ sa.vertices.count +
+ "\n vertices/strip: " +
+ ((float)sa.vertices.count /
+ (float)sa.stripCounts.count)) ;
+ System.out.print(" strip counts: [") ;
+ for (i = 0 ; i < sa.stripCounts.count-1 ; i++)
+ System.out.print(sa.stripCounts.ints[i] + ", ") ;
+ System.out.println(sa.stripCounts.ints[i] + "]") ;
+ }
+ System.out.println() ;
+ }
+ return sa ;
+ }
+
+ private static void fanToStrips(int v, int length, int fans[],
+ IntList stripVerts,
+ IntList stripCounts,
+ boolean convexPlanar) {
+ if (convexPlanar) {
+ // Construct a strip by criss-crossing across the interior.
+ stripCounts.add(length) ;
+ stripVerts.add(fans[v]) ;
+
+ int j = v + 1 ;
+ int k = v + (length - 1) ;
+ while (j <= k) {
+ stripVerts.add(fans[j++]) ;
+ if (j > k) break ;
+ stripVerts.add(fans[k--]) ;
+ }
+ } else {
+ // Traverse non-convex or non-planar fan, biting off 3-triangle
+ // strips or less. First 5 vertices produce 1 strip of 3
+ // triangles, and every 4 vertices after that produce another
+ // strip of 3 triangles. Each remaining strip adds 2 vertices.
+ int fanStart = v ;
+ v++ ;
+ while (v+4 <= fanStart + length) {
+ stripVerts.add(fans[v]) ;
+ stripVerts.add(fans[v+1]) ;
+ stripVerts.add(fans[fanStart]) ;
+ stripVerts.add(fans[v+2]) ;
+ stripVerts.add(fans[v+3]) ;
+ stripCounts.add(5) ;
+ v += 3 ;
+ }
+
+ // Finish off the fan.
+ if (v+1 < fanStart + length) {
+ stripVerts.add(fans[v]) ;
+ stripVerts.add(fans[v+1]) ;
+ stripVerts.add(fans[fanStart]) ;
+ v++ ;
+
+ if (v+1 < fanStart + length) {
+ stripVerts.add(fans[v+1]) ;
+ stripCounts.add(4) ;
+ }
+ else
+ stripCounts.add(3) ;
+ }
+ }
+ }
+
+ /**
+ * Interprets the vertex flags associated with a class implementing
+ * GeneralizedStripFlags, constructing and returning an array of vertex
+ * references representing the original generalized strip as individual
+ * triangles. Each sequence of three consecutive vertex references in the
+ * output defines a single triangle.
+ *
+ * @param vertices an object implementing GeneralizedStripFlags
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @return an array of indices into the original vertex array
+ */
+ static int[] toTriangles(GeneralizedStripFlags vertices, int frontFace) {
+
+ int vertexCount = 0 ;
+ StripArray sa[] = toStripsAndFans(vertices, frontFace) ;
+
+ if (sa[0] != null)
+ vertexCount = 3 * getTriangleCount(sa[0].stripCounts) ;
+ if (sa[1] != null)
+ vertexCount += 3 * getTriangleCount(sa[1].stripCounts) ;
+
+ if (debug)
+ System.out.println("GeneralizedStrip.toTriangles\n" +
+ " number of triangles: " + vertexCount/3 + "\n" +
+ " number of vertices: " + vertexCount + "\n") ;
+ int t = 0 ;
+ int triangles[] = new int[vertexCount] ;
+
+ if (sa[0] != null)
+ t = stripsToTriangles(t, triangles,
+ 0, sa[0].vertices.ints,
+ 0, sa[0].stripCounts.ints,
+ sa[0].stripCounts.count) ;
+ if (sa[1] != null)
+ t = fansToTriangles(t, triangles,
+ 0, sa[1].vertices.ints,
+ 0, sa[1].stripCounts.ints,
+ sa[1].stripCounts.count) ;
+ return triangles ;
+ }
+
+ private static int stripsToTriangles(int tstart, int tbuff[],
+ int vstart, int vertices[],
+ int stripStart, int stripCounts[],
+ int stripCount) {
+ int t = tstart ;
+ int v = vstart ;
+ for (int i = 0 ; i < stripCount ; i++) {
+ for (int j = 0 ; j < stripCounts[i+stripStart] - 2 ; j++) {
+ if ((j & 0x01) == 0) {
+ // even-numbered triangles
+ tbuff[t*3 +0] = vertices[v+0] ;
+ tbuff[t*3 +1] = vertices[v+1] ;
+ tbuff[t*3 +2] = vertices[v+2] ;
+ } else {
+ // odd-numbered triangles
+ tbuff[t*3 +0] = vertices[v+1] ;
+ tbuff[t*3 +1] = vertices[v+0] ;
+ tbuff[t*3 +2] = vertices[v+2] ;
+ }
+ t++ ; v++ ;
+ }
+ v += 2 ;
+ }
+ return t ;
+ }
+
+ private static int fansToTriangles(int tstart, int tbuff[],
+ int vstart, int vertices[],
+ int stripStart, int stripCounts[],
+ int stripCount) {
+ int t = tstart ;
+ int v = vstart ;
+ for (int i = 0 ; i < stripCount ; i++) {
+ for (int j = 0 ; j < stripCounts[i+stripStart] - 2 ; j++) {
+ tbuff[t*3 +0] = vertices[v] ;
+ tbuff[t*3 +1] = vertices[v+j+1] ;
+ tbuff[t*3 +2] = vertices[v+j+2] ;
+ t++ ;
+ }
+ v += stripCounts[i+stripStart] ;
+ }
+ return t ;
+ }
+
+ /**
+ * Interprets the vertex flags associated with a class implementing
+ * GeneralizedStripFlags, constructing and returning a 2-element array of
+ * StripArray objects. The first StripArray will contain triangle strips
+ * and the second will contain individual triangles in the vertices
+ * field. Short strips will be converted to individual triangles.
+ *
+ * @param vertices an object implementing GeneralizedStripFlags
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @param shortStripSize strips this size or less will be converted to
+ * individual triangles if there are more than maxShortStrips of them
+ * @param maxShortStrips maximum number of short strips allowed before
+ * creating individual triangles
+ * @return a 2-element array containing strips in 0 and triangles in 1
+ */
+ static StripArray[] toStripsAndTriangles(GeneralizedStripFlags vertices,
+ int frontFace, int shortStripSize,
+ int maxShortStrips) {
+ int longStripCount = 0 ;
+ int longStripVertexCount = 0 ;
+ int shortStripCount = 0 ;
+ int triangleCount = 0 ;
+
+ StripArray sa[] = new StripArray[2] ;
+ StripArray ts = toTriangleStrips(vertices, frontFace) ;
+
+ for (int i = 0 ; i < ts.stripCounts.count ; i++)
+ if (ts.stripCounts.ints[i] <= shortStripSize) {
+ shortStripCount++ ;
+ triangleCount += ts.stripCounts.ints[i] - 2 ;
+ } else {
+ longStripCount++ ;
+ longStripVertexCount += ts.stripCounts.ints[i] ;
+ }
+
+ if (debug)
+ System.out.print("GeneralizedStrip.toStripsAndTriangles\n" +
+ " short strip size: " + shortStripSize +
+ " short strips tolerated: " + maxShortStrips +
+ " number of short strips: " + shortStripCount +
+ "\n\n") ;
+
+ if (shortStripCount <= maxShortStrips) {
+ sa[0] = ts ;
+ sa[1] = null ;
+ } else {
+ int si = 0 ; int newStripVerts[] = new int[longStripVertexCount] ;
+ int ci = 0 ; int newStripCounts[] = new int[longStripCount] ;
+ int ti = 0 ; int triangles[] = new int[3*triangleCount] ;
+ int vi = 0 ;
+
+ for (int i = 0 ; i < ts.stripCounts.count ; i++) {
+ if (ts.stripCounts.ints[i] <= shortStripSize) {
+ ti = stripsToTriangles(ti, triangles,
+ vi, ts.vertices.ints,
+ i, ts.stripCounts.ints, 1) ;
+ vi += ts.stripCounts.ints[i] ;
+ } else {
+ newStripCounts[ci++] = ts.stripCounts.ints[i] ;
+ for (int j = 0 ; j < ts.stripCounts.ints[i] ; j++)
+ newStripVerts[si++] = ts.vertices.ints[vi++] ;
+ }
+ }
+
+ if (longStripCount > 0)
+ sa[0] = new StripArray(new IntList(newStripVerts),
+ new IntList(newStripCounts)) ;
+ else
+ sa[0] = null ;
+
+ sa[1] = new StripArray(new IntList(triangles), null) ;
+
+ if (debug) {
+ System.out.println(" triangles separated: " + triangleCount) ;
+ if (longStripCount > 0) {
+ System.out.println
+ (" new vertices/strip: " +
+ ((float)longStripVertexCount/(float)longStripCount)) ;
+
+ System.out.print(" long strip counts: [") ;
+ for (int i = 0 ; i < longStripCount-1 ; i++)
+ System.out.print(newStripCounts[i++] + ", ") ;
+
+ System.out.println
+ (newStripCounts[longStripCount-1] + "]\n") ;
+ }
+ }
+ }
+ return sa ;
+ }
+
+ /**
+ * Interprets the vertex flags associated with a class implementing
+ * GeneralizedStripFlags, constructing and returning a StripArray.
+ *
+ * RESTART_CW and RESTART_CCW are treated as equivalent, as are
+ * REPLACE_MIDDLE and REPLACE_OLDEST.
+ *
+ * @param vertices an object implementing GeneralizedStripFlags
+ * @return a StripArray representing an array of line strips
+ */
+ static StripArray toLineStrips(GeneralizedStripFlags vertices) {
+ int v, size, stripStart, stripLength, flag ;
+
+ stripStart = 0 ;
+ stripLength = 2 ;
+ size = vertices.getFlagCount() ;
+
+ // Initialize IntLists to worst-case sizes.
+ IntList stripVerts = new IntList(size*2) ;
+ IntList stripCounts = new IntList(size) ;
+
+ // Vertex replace flags for the first two vertices are irrelevant.
+ v = 2 ;
+ while (v < size) {
+ flag = vertices.getFlag(v) ;
+
+ if ((flag != RESTART_CW) && (flag != RESTART_CCW)) {
+ // proceed to the next vertex.
+ stripLength++ ;
+ v++ ;
+
+ } else {
+ // Record the last strip.
+ stripCounts.add(stripLength) ;
+ for (int i = stripStart ; i < stripStart+stripLength ; i++)
+ stripVerts.add(i) ;
+
+ // Start a new strip and skip to its 3rd vertex.
+ stripStart = v ;
+ stripLength = 2 ;
+ v += 2 ;
+ }
+ }
+
+ // Finish off the last strip.
+ // If v > size then the strip is degenerate.
+ if (v == size) {
+ stripCounts.add(stripLength) ;
+ for (int i = stripStart ; i < stripStart+stripLength ; i++)
+ stripVerts.add(i) ;
+ } else
+ throw new IllegalArgumentException
+ (J3dUtilsI18N.getString("GeneralizedStrip0")) ;
+
+ if (debug) {
+ System.out.println("GeneralizedStrip.toLineStrips\n") ;
+ if (v > size)
+ System.out.println(" ended with a degenerate line") ;
+
+ System.out.println(" number of strips: " + stripCounts.count) ;
+ if (stripCounts.count > 0) {
+ System.out.println(" number of vertices: " + stripVerts.count) ;
+ System.out.println(" vertices/strip: " +
+ (float)stripVerts.count/stripCounts.count) ;
+ System.out.println(" strip counts: " + stripCounts.toString()) ;
+ // System.out.println(" indices: " + stripVerts.toString()) ;
+ }
+ System.out.println() ;
+ }
+
+ if (stripCounts.count > 0)
+ return new StripArray(stripVerts, stripCounts) ;
+ else
+ return null ;
+ }
+
+ /**
+ * Counts the number of lines defined by arrays of line strips.
+ *
+ * @param stripCounts array of strip counts, as used by the
+ * GeometryStripArray object
+ * @return number of lines in the strips
+ */
+ static int getLineCount(int stripCounts[]) {
+ int count = 0 ;
+ for (int i = 0 ; i < stripCounts.length ; i++)
+ count += (stripCounts[i] - 1) ;
+ return count ;
+ }
+
+ /**
+ * Counts the number of triangles defined by arrays of
+ * triangle strips or fans.
+ *
+ * @param stripCounts array of strip counts, as used by the
+ * GeometryStripArray object
+ * @return number of triangles in the strips or fans
+ */
+ static int getTriangleCount(int stripCounts[]) {
+ int count = 0 ;
+ for (int i = 0 ; i < stripCounts.length ; i++)
+ count += (stripCounts[i] - 2) ;
+ return count ;
+ }
+
+ /**
+ * Counts the number of triangles defined by arrays of
+ * triangle strips or fans.
+ *
+ * @param stripCounts IntList of strip counts
+ * @return number of triangles in the strips or fans
+ */
+ static int getTriangleCount(IntList stripCounts) {
+ int count = 0 ;
+ for (int i = 0 ; i < stripCounts.count ; i++)
+ count += (stripCounts.ints[i] - 2) ;
+ return count ;
+ }
+
+ /**
+ * Breaks up triangle strips into separate triangles.
+ *
+ * @param stripCounts array of strip counts, as used by the
+ * GeometryStripArray object
+ * @return array of ints which index into the original vertex array; each
+ * set of three consecutive vertex indices defines a single triangle
+ */
+ static int[] stripsToTriangles(int stripCounts[]) {
+ int triangleCount = getTriangleCount(stripCounts) ;
+ int tbuff[] = new int[3*triangleCount] ;
+ IntList vertices = new IntList(triangleCount + 2*stripCounts.length) ;
+
+ vertices.fillAscending() ;
+ stripsToTriangles(0, tbuff,
+ 0, vertices.ints,
+ 0, stripCounts,
+ stripCounts.length) ;
+ return tbuff ;
+ }
+
+ /**
+ * Breaks up triangle fans into separate triangles.
+ *
+ * @param stripCounts array of strip counts, as used by the
+ * GeometryStripArray object
+ * @return array of ints which index into the original vertex array; each
+ * set of three consecutive vertex indices defines a single triangle
+ */
+ static int[] fansToTriangles(int stripCounts[]) {
+ int triangleCount = getTriangleCount(stripCounts) ;
+ int tbuff[] = new int[3*triangleCount] ;
+ IntList vertices = new IntList(triangleCount + 2*stripCounts.length) ;
+
+ vertices.fillAscending() ;
+ fansToTriangles(0, tbuff,
+ 0, vertices.ints,
+ 0, stripCounts,
+ stripCounts.length) ;
+ return tbuff ;
+ }
+
+ /**
+ * Takes a fan and converts it to one or more strips.
+ *
+ * @param v index into the fans array of the first vertex in the fan
+ * @param length number of vertices in the fan
+ * @param fans array of vertex indices representing one or more fans
+ * @param convexPlanar if true indicates that the fan is convex and
+ * planar; such fans will always be converted into a single strip
+ * @return a StripArray containing the converted strips
+ */
+ static StripArray fanToStrips(int v, int length, int fans[],
+ boolean convexPlanar) {
+
+ // Initialize IntLists to worst-case sizes.
+ IntList stripVerts = new IntList(length*3) ;
+ IntList stripCounts = new IntList(length) ;
+
+ fanToStrips(v, length, fans, stripVerts, stripCounts, convexPlanar) ;
+ return new StripArray(stripVerts, stripCounts) ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedStripFlags.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedStripFlags.java
new file mode 100644
index 0000000..5d7fa08
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedStripFlags.java
@@ -0,0 +1,105 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+/**
+ * A class which implements GeneralizedStripFlags provides the means to access
+ * the vertex replace code flags associated with each vertex of a generalized
+ * strip. This allows a flexible representation of generalized strips for
+ * various classes and makes it possible to provide a common subset of static
+ * methods which operate only on their topology.
+ *
+ * @see GeneralizedStrip
+ * @see GeneralizedVertexList
+ */
+interface GeneralizedStripFlags {
+
+ /**
+ * This flag indicates that a vertex starts a new strip with clockwise
+ * winding.
+ */
+ static final int RESTART_CW = 0 ;
+
+ /**
+ * This flag indicates that a vertex starts a new strip with
+ * counter-clockwise winding.
+ */
+ static final int RESTART_CCW = 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.
+ */
+ 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.
+ */
+ static final int REPLACE_OLDEST = 3 ;
+
+ /**
+ * This constant is used to indicate that triangles with clockwise vertex
+ * winding are front facing.
+ */
+ static final int FRONTFACE_CW = 0 ;
+
+ /**
+ * This constant is used to indicate that triangles with counter-clockwise
+ * vertex winding are front facing.
+ */
+ static final int FRONTFACE_CCW = 1 ;
+
+ /**
+ * Return the number of flags. This should be the same as the number of
+ * vertices in the generalized strip.
+ */
+ int getFlagCount() ;
+
+ /**
+ * Return the flag associated with the vertex at the specified index.
+ */
+ int getFlag(int index) ;
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedVertexList.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedVertexList.java
new file mode 100644
index 0000000..f5baf04
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedVertexList.java
@@ -0,0 +1,429 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import java.util.ArrayList;
+import javax.media.j3d.GeometryArray;
+import javax.media.j3d.GeometryStripArray;
+import javax.media.j3d.LineStripArray;
+import javax.media.j3d.PointArray;
+import javax.media.j3d.TriangleArray;
+import javax.media.j3d.TriangleFanArray;
+import javax.media.j3d.TriangleStripArray;
+import javax.vecmath.Color3f;
+import javax.vecmath.Color4f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+
+/**
+ * The GeneralizedVertexList class is a variable-size list used to
+ * collect the vertices for a generalized strip of points, lines, or
+ * triangles. This is used by the GeometryDecompressor. This class
+ * implements the GeneralizedStripFlags interface and provides methods
+ * for copying instance vertex data into various fixed-size
+ * GeometryArray representations.
+ *
+ * @see GeneralizedStrip
+ * @see GeometryDecompressor
+ */
+class GeneralizedVertexList implements GeneralizedStripFlags {
+
+ // The ArrayList containing the vertices.
+ private ArrayList vertices ;
+
+ // Booleans for individual vertex components.
+ private boolean hasColor3 = false ;
+ private boolean hasColor4 = false ;
+ private boolean hasNormals = false ;
+
+ // Indicates the vertex winding of front-facing triangles in this strip.
+ private int frontFace ;
+
+ /**
+ * Count of number of strips generated after conversion to GeometryArray.
+ */
+ int stripCount ;
+
+ /**
+ * Count of number of vertices generated after conversion to GeometryArray.
+ */
+ int vertexCount ;
+
+ /**
+ * Count of number of triangles generated after conversion to GeometryArray.
+ */
+ int triangleCount ;
+
+ /**
+ * Bits describing the data bundled with each vertex. This is specified
+ * using the GeometryArray mask components.
+ */
+ int vertexFormat ;
+
+ /**
+ * Creates a new GeneralizedVertexList for the specified vertex format.
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex, as used by GeometryArray.
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @param initSize initial number of elements
+ * @see GeometryArray
+ */
+ GeneralizedVertexList(int vertexFormat, int frontFace, int initSize) {
+ this.frontFace = frontFace ;
+ setVertexFormat(vertexFormat) ;
+
+ if (initSize == 0)
+ vertices = new ArrayList() ;
+ else
+ vertices = new ArrayList(initSize) ;
+
+ stripCount = 0 ;
+ vertexCount = 0 ;
+ triangleCount = 0 ;
+ }
+
+ /**
+ * Creates a new GeneralizedVertexList for the specified vertex format.
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex, as used by GeometryArray.
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @see GeometryArray
+ */
+ GeneralizedVertexList(int vertexFormat, int frontFace) {
+ this(vertexFormat, frontFace, 0) ;
+ }
+
+ /**
+ * Sets the vertex format for this vertex list.
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex, as used by GeometryArray.
+ */
+ void setVertexFormat(int vertexFormat) {
+ this.vertexFormat = vertexFormat ;
+
+ if ((vertexFormat & GeometryArray.NORMALS) != 0)
+ hasNormals = true ;
+
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4)
+ hasColor4 = true ;
+ else if ((vertexFormat & GeometryArray.COLOR_3) == GeometryArray.COLOR_3)
+ hasColor3 = true ;
+ }
+
+ /**
+ * A class with fields corresponding to all the data that can be bundled
+ * with the vertices of generalized strips.
+ */
+ class Vertex {
+ int flag ;
+ Point3f coord ;
+ Color3f color3 ;
+ Color4f color4 ;
+ Vector3f normal ;
+
+ Vertex(Point3f p, Vector3f n, Color4f c, int flag) {
+ this.flag = flag ;
+ coord = new Point3f(p) ;
+
+ if (hasNormals)
+ normal = new Vector3f(n) ;
+
+ if (hasColor3)
+ color3 = new Color3f(c.x, c.y, c.z) ;
+
+ else if (hasColor4)
+ color4 = new Color4f(c) ;
+ }
+ }
+
+ /**
+ * Copy vertex data to a new Vertex object and add it to this list.
+ */
+ void addVertex(Point3f pos, Vector3f norm, Color4f color, int flag) {
+ vertices.add(new Vertex(pos, norm, color, flag)) ;
+ }
+
+ /**
+ * Return the number of vertices in this list.
+ */
+ int size() {
+ return vertices.size() ;
+ }
+
+ // GeneralizedStripFlags interface implementation
+ public int getFlagCount() {
+ return vertices.size() ;
+ }
+
+ // GeneralizedStripFlags interface implementation
+ public int getFlag(int index) {
+ return ((Vertex)vertices.get(index)).flag ;
+ }
+
+ // Copy vertices in the given order to a fixed-length GeometryArray.
+ // Using the array versions of the GeometryArray set() methods results in
+ // a significant performance improvement despite needing to create
+ // fixed-length arrays to hold the vertex elements.
+ private void copyVertexData(GeometryArray ga,
+ GeneralizedStrip.IntList indices) {
+ Vertex v ;
+ Point3f p3f[] = new Point3f[indices.count] ;
+
+ if (hasNormals) {
+ Vector3f v3f[] = new Vector3f[indices.count] ;
+ if (hasColor3) {
+ Color3f c3f[] = new Color3f[indices.count] ;
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ v3f[i] = v.normal ;
+ c3f[i] = v.color3 ;
+ }
+ ga.setColors(0, c3f) ;
+
+ } else if (hasColor4) {
+ Color4f c4f[] = new Color4f[indices.count] ;
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ v3f[i] = v.normal ;
+ c4f[i] = v.color4 ;
+ }
+ ga.setColors(0, c4f) ;
+
+ } else {
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ v3f[i] = v.normal ;
+ }
+ }
+ ga.setNormals(0, v3f) ;
+
+ } else {
+ if (hasColor3) {
+ Color3f c3f[] = new Color3f[indices.count] ;
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ c3f[i] = v.color3 ;
+ }
+ ga.setColors(0, c3f) ;
+
+ } else if (hasColor4) {
+ Color4f c4f[] = new Color4f[indices.count] ;
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ c4f[i] = v.color4 ;
+ }
+ ga.setColors(0, c4f) ;
+
+ } else {
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ }
+ }
+ }
+ ga.setCoordinates(0, p3f) ;
+ }
+
+ /**
+ * Output a PointArray.
+ */
+ PointArray toPointArray() {
+ int size = vertices.size() ;
+
+ if (size > 0) {
+ PointArray pa = new PointArray(size, vertexFormat) ;
+ GeneralizedStrip.IntList il = new GeneralizedStrip.IntList(size) ;
+
+ il.fillAscending() ;
+ copyVertexData(pa, il) ;
+
+ vertexCount += size ;
+ return pa ;
+ }
+ else
+ return null ;
+ }
+
+ /**
+ * Output a TriangleArray.
+ */
+ TriangleArray toTriangleArray() {
+ int vertices[] = GeneralizedStrip.toTriangles(this, frontFace) ;
+
+ if (vertices != null) {
+ TriangleArray ta ;
+ GeneralizedStrip.IntList il ;
+
+ ta = new TriangleArray(vertices.length, vertexFormat) ;
+ il = new GeneralizedStrip.IntList(vertices) ;
+ copyVertexData(ta, il) ;
+
+ vertexCount += vertices.length ;
+ triangleCount += vertices.length/3 ;
+ return ta ;
+ } else
+ return null ;
+ }
+
+ /**
+ * Output a LineStripArray.
+ */
+ LineStripArray toLineStripArray() {
+ GeneralizedStrip.StripArray stripArray =
+ GeneralizedStrip.toLineStrips(this) ;
+
+ if (stripArray != null) {
+ LineStripArray lsa ;
+ lsa = new LineStripArray(stripArray.vertices.count,
+ vertexFormat,
+ stripArray.stripCounts.trim()) ;
+
+ copyVertexData(lsa, stripArray.vertices) ;
+
+ vertexCount += stripArray.vertices.count ;
+ stripCount += stripArray.stripCounts.count ;
+ return lsa ;
+ } else
+ return null ;
+ }
+
+ /**
+ * Output a TriangleStripArray.
+ */
+ TriangleStripArray toTriangleStripArray() {
+ GeneralizedStrip.StripArray stripArray =
+ GeneralizedStrip.toTriangleStrips(this, frontFace) ;
+
+ if (stripArray != null) {
+ TriangleStripArray tsa ;
+ tsa = new TriangleStripArray(stripArray.vertices.count,
+ vertexFormat,
+ stripArray.stripCounts.trim()) ;
+
+ copyVertexData(tsa, stripArray.vertices) ;
+
+ vertexCount += stripArray.vertices.count ;
+ stripCount += stripArray.stripCounts.count ;
+ return tsa ;
+ } else
+ return null ;
+ }
+
+ /**
+ * Output triangle strip and triangle fan arrays.
+ * @return a 2-element array of GeometryStripArray; element 0 if non-null
+ * will contain a TriangleStripArray, and element 1 if non-null will
+ * contain a TriangleFanArray.
+ */
+ GeometryStripArray[] toStripAndFanArrays() {
+ GeneralizedStrip.StripArray stripArray[] =
+ GeneralizedStrip.toStripsAndFans(this, frontFace) ;
+
+ GeometryStripArray gsa[] = new GeometryStripArray[2] ;
+
+ if (stripArray[0] != null) {
+ gsa[0] = new TriangleStripArray(stripArray[0].vertices.count,
+ vertexFormat,
+ stripArray[0].stripCounts.trim()) ;
+
+ copyVertexData(gsa[0], stripArray[0].vertices) ;
+
+ vertexCount += stripArray[0].vertices.count ;
+ stripCount += stripArray[0].stripCounts.count ;
+ }
+
+ if (stripArray[1] != null) {
+ gsa[1] = new TriangleFanArray(stripArray[1].vertices.count,
+ vertexFormat,
+ stripArray[1].stripCounts.trim()) ;
+
+ copyVertexData(gsa[1], stripArray[1].vertices) ;
+
+ vertexCount += stripArray[1].vertices.count ;
+ stripCount += stripArray[1].stripCounts.count ;
+ }
+ return gsa ;
+ }
+
+ /**
+ * Output triangle strip and and triangle arrays.
+ * @return a 2-element array of GeometryArray; element 0 if non-null
+ * will contain a TriangleStripArray, and element 1 if non-null will
+ * contain a TriangleArray.
+ */
+ GeometryArray[] toStripAndTriangleArrays() {
+ GeneralizedStrip.StripArray stripArray[] =
+ GeneralizedStrip.toStripsAndTriangles(this, frontFace, 4, 12) ;
+
+ GeometryArray ga[] = new GeometryArray[2] ;
+
+ if (stripArray[0] != null) {
+ ga[0] = new TriangleStripArray(stripArray[0].vertices.count,
+ vertexFormat,
+ stripArray[0].stripCounts.trim()) ;
+
+ copyVertexData(ga[0], stripArray[0].vertices) ;
+
+ vertexCount += stripArray[0].vertices.count ;
+ stripCount += stripArray[0].stripCounts.count ;
+ }
+
+ if (stripArray[1] != null) {
+ ga[1] = new TriangleArray(stripArray[1].vertices.count,
+ vertexFormat) ;
+
+ copyVertexData(ga[1], stripArray[1].vertices) ;
+ triangleCount += stripArray[1].vertices.count/3 ;
+ }
+ return ga ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryCompressor.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryCompressor.java
new file mode 100644
index 0000000..90b0e89
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryCompressor.java
@@ -0,0 +1,204 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import java.io.IOException;
+import javax.vecmath.Point3d;
+
+/**
+ * A GeometryCompressor takes a stream of geometric elements and
+ * quantization parameters (the CompressionStream object) and
+ * compresses it into a stream of commands as defined by appendix B
+ * of the Java 3D specification. The resulting data may be output
+ * in the form of a CompressedGeometryData node component or appended
+ * to a CompressedGeometryFile.
+ *
+ * @see CompressionStream
+ * @see CompressedGeometryData
+ * @see CompressedGeometryFile
+ *
+ * @since Java 3D 1.5
+ */
+public class GeometryCompressor {
+ private static final boolean benchmark = false ;
+ private static final boolean printStream = false ;
+ private static final boolean printHuffman = false ;
+
+ private HuffmanTable huffmanTable ;
+ private CommandStream outputBuffer ;
+ private CompressedGeometryData.Header cgHeader ;
+ private long startTime ;
+
+ public GeometryCompressor() {
+ // Create a compressed geometry header.
+ cgHeader = new CompressedGeometryData.Header() ;
+
+ // v1.0.0 - pre-FCS
+ // v1.0.1 - fixed winding order, FCS version (J3D 1.1.2)
+ // v1.0.2 - normal component length maximum 6, LII hardware (J3D 1.2)
+ cgHeader.majorVersionNumber = 1 ;
+ cgHeader.minorVersionNumber = 0 ;
+ cgHeader.minorMinorVersionNumber = 2 ;
+ }
+
+ /**
+ * Compress a stream into a CompressedGeometryData node component.
+ *
+ *
+ * @param stream CompressionStream containing the geometry to be compressed
+ * @return a CompressedGeometryData node component
+ */
+ public CompressedGeometryData compress(CompressionStream stream) {
+ CompressedGeometryData cg ;
+
+ compressStream(stream) ;
+ cg = new CompressedGeometryData(cgHeader, outputBuffer.getBytes()) ;
+
+ outputBuffer.clear() ;
+ return cg ;
+ }
+
+ /**
+ * Compress a stream and append the output to a CompressedGeometryFile.
+ * The resource remains open for subsequent updates; its close() method
+ * must be called to create a valid compressed geometry resource file.
+ *
+ * @param stream CompressionStream containing the geometry to be compressed
+ * @param f a currently open CompressedGeometryFile with write access
+ * @exception IOException if write fails
+ */
+ public void compress(CompressionStream stream, CompressedGeometryFile f)
+ throws IOException {
+
+ compressStream(stream) ;
+ f.write(cgHeader, outputBuffer.getBytes()) ;
+
+ outputBuffer.clear() ;
+ }
+
+ //
+ // Compress the stream and put the results in the output buffer.
+ // Set up the CompressedGeometryData.Header object.
+ //
+ private void compressStream(CompressionStream stream) {
+ if (benchmark) startTime = System.currentTimeMillis() ;
+
+ // Create the Huffman table.
+ huffmanTable = new HuffmanTable() ;
+
+ // Quantize the stream, compute deltas between consecutive elements if
+ // possible, and histogram the data length distribution.
+ stream.quantize(huffmanTable) ;
+
+ // Compute tags for stream tokens.
+ huffmanTable.computeTags() ;
+
+ // Create the output buffer and assemble the compressed output.
+ outputBuffer = new CommandStream(stream.getByteCount() / 3) ;
+ stream.outputCommands(huffmanTable, outputBuffer) ;
+
+ // Print any desired info.
+ if (benchmark) printBench(stream) ;
+ if (printStream) stream.print() ;
+ if (printHuffman) huffmanTable.print() ;
+
+ // Set up the compressed geometry header object.
+ cgHeader.bufferType = stream.streamType ;
+ cgHeader.bufferDataPresent = 0 ;
+ cgHeader.lowerBound = new Point3d(stream.ncBounds[0]) ;
+ cgHeader.upperBound = new Point3d(stream.ncBounds[1]) ;
+
+ if (stream.vertexNormals)
+ cgHeader.bufferDataPresent |=
+ CompressedGeometryData.Header.NORMAL_IN_BUFFER ;
+
+ if (stream.vertexColor3 || stream.vertexColor4)
+ cgHeader.bufferDataPresent |=
+ CompressedGeometryData.Header.COLOR_IN_BUFFER ;
+
+ if (stream.vertexColor4)
+ cgHeader.bufferDataPresent |=
+ CompressedGeometryData.Header.ALPHA_IN_BUFFER ;
+
+ cgHeader.start = 0 ;
+ cgHeader.size = outputBuffer.getByteCount() ;
+
+ // Clear the huffman table for next use.
+ huffmanTable.clear() ;
+ }
+
+ private void printBench(CompressionStream stream) {
+ long t = System.currentTimeMillis() - startTime ;
+ int vertexCount = stream.getVertexCount() ;
+ int meshReferenceCount = stream.getMeshReferenceCount() ;
+ int totalVertices = meshReferenceCount + vertexCount ;
+ float meshPercent = 100f * meshReferenceCount/(float)totalVertices ;
+
+ float compressionRatio =
+ stream.getByteCount() / ((float)outputBuffer.getByteCount()) ;
+
+ int vertexBytes =
+ 12 + (stream.vertexColor3 ? 12 : 0) +
+ (stream.vertexColor4 ? 16 : 0) + (stream.vertexNormals ? 12 : 0) ;
+
+ float compressedVertexBytes =
+ outputBuffer.getByteCount() / (float)totalVertices ;
+
+ System.out.println
+ ("\nGeometryCompressor:\n" + totalVertices + " total vertices\n" +
+ vertexCount + " streamed vertices\n" + meshReferenceCount +
+ " mesh buffer references (" + meshPercent + "%)\n" +
+ stream.getByteCount() + " bytes streamed geometry compressed to " +
+ outputBuffer.getByteCount() + " in " + (t/1000f) + " sec\n" +
+ (stream.getByteCount()/(float)t) + " kbytes/sec, " +
+ "stream compression ratio " + compressionRatio + "\n\n" +
+ vertexBytes + " original bytes per vertex, " +
+ compressedVertexBytes + " compressed bytes per vertex\n" +
+ "total vertex compression ratio " +
+ (vertexBytes / (float)compressedVertexBytes) + "\n\n" +
+ "lower bound " + stream.ncBounds[0].toString() +"\n" +
+ "upper bound " + stream.ncBounds[1].toString()) ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDecompressor.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDecompressor.java
new file mode 100644
index 0000000..62a5646
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDecompressor.java
@@ -0,0 +1,1236 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import com.sun.j3d.internal.J3dUtilsI18N;
+import javax.vecmath.Color4f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+
+/**
+ * This abstract class provides the base methods needed to create a geometry
+ * decompressor. Subclasses must implement a backend to handle the output,
+ * consisting of a generalized triangle strip, line strip, or point array,
+ * along with possible global color and normal changes.
+ */
+abstract class GeometryDecompressor {
+ private static final boolean debug = false ;
+ private static final boolean benchmark = false ;
+
+ /**
+ * Compressed geometry format version supported.
+ */
+ static final int majorVersionNumber = 1 ;
+ static final int minorVersionNumber = 0 ;
+ static final int minorMinorVersionNumber = 2 ;
+
+ /**
+ * This method is called when a SetState command is encountered in the
+ * decompression stream.
+ *
+ * @param bundlingNorm true indicates normals are bundled with vertices
+ * @param bundlingColor true indicates colors are bundled with vertices
+ * @param doingAlpha true indicates alpha values are bundled with vertices
+ */
+ abstract void outputVertexFormat(boolean bundlingNorm,
+ boolean bundlingColor,
+ boolean doingAlpha) ;
+
+ /**
+ * This method captures the vertex output of the decompressor. The normal
+ * or color references may be null if the corresponding data is not
+ * bundled with the vertices in the compressed geometry buffer. Alpha
+ * values may be included in the color.
+ *
+ * @param position The coordinates of the vertex.
+ * @param normal The normal bundled with the vertex. May be null.
+ * @param color The color bundled with the vertex. May be null.
+ * Alpha may be present.
+ * @param vertexReplaceCode Specifies the generalized strip flag
+ * that is bundled with each vertex.
+ * @see GeneralizedStripFlags
+ * @see CompressedGeometryHeader
+ */
+ abstract void outputVertex(Point3f position, Vector3f normal,
+ Color4f color, int vertexReplaceCode) ;
+
+ /**
+ * This method captures the global color output of the decompressor. It
+ * is only invoked if colors are not bundled with the vertex data. The
+ * global color applies to all succeeding vertices until the next time the
+ * method is invoked.
+ *
+ * @param color The current global color.
+ */
+ abstract void outputColor(Color4f color) ;
+
+ /**
+ * This method captures the global normal output of the decompressor. It
+ * is only invoked if normals are not bundled with the vertex data. The
+ * global normal applies to all succeeding vertices until the next time the
+ * method is invoked.
+ *
+ * @param normal The current global normal.
+ */
+ abstract void outputNormal(Vector3f normal) ;
+
+ // Geometry compression opcodes.
+ private static final int GC_VERTEX = 0x40 ;
+ private static final int GC_SET_NORM = 0xC0 ;
+ private static final int GC_SET_COLOR = 0x80 ;
+ private static final int GC_MESH_B_R = 0x20 ;
+ private static final int GC_SET_STATE = 0x18 ;
+ private static final int GC_SET_TABLE = 0x10 ;
+ private static final int GC_PASS_THROUGH = 0x08 ;
+ private static final int GC_EOS = 0x00 ;
+ private static final int GC_V_NO_OP = 0x01 ;
+ private static final int GC_SKIP_8 = 0x07 ;
+
+ // Three 64-entry decompression tables are used: gctables[0] for
+ // positions, gctables[1] for colors, and gctables[2] for normals.
+ private HuffmanTableEntry gctables[][] ;
+
+ /**
+ * Decompression table entry.
+ */
+ static class HuffmanTableEntry {
+ int tagLength, dataLength ;
+ int rightShift, absolute ;
+
+ public String toString() {
+ return
+ " tag length: " + tagLength +
+ " data length: " + dataLength +
+ " shift: " + rightShift +
+ " abs/rel: " + absolute ;
+ }
+ }
+
+ // A 16-entry mesh buffer is used.
+ private MeshBufferEntry meshBuffer[] ;
+ private int meshIndex = 15 ;
+ private int meshState ;
+
+ // meshState values. These are needed to determine if colors and/or
+ // normals should come from meshBuffer or from SetColor or SetNormal.
+ private static final int USE_MESH_NORMAL = 0x1 ;
+ private static final int USE_MESH_COLOR = 0x2 ;
+
+ /**
+ * Mesh buffer entry containing position, normal, and color.
+ */
+ static class MeshBufferEntry {
+ short x, y, z ;
+ short octant, sextant, u, v ;
+ short r, g, b, a ;
+ }
+
+ // Geometry compression state variables.
+ private short curX, curY, curZ ;
+ private short curR, curG, curB, curA ;
+ private int curSex, curOct, curU, curV ;
+
+ // Current vertex data.
+ private Point3f curPos ;
+ private Vector3f curNorm ;
+ private Color4f curColor ;
+ private int repCode ;
+
+ // Flags indicating what data is bundled with the vertex.
+ private boolean bundlingNorm ;
+ private boolean bundlingColor ;
+ private boolean doingAlpha ;
+
+ // Internal decompression buffering variables.
+ private int currentHeader = 0 ;
+ private int nextHeader = 0 ;
+ private int bitBuffer = 0 ;
+ private int bitBufferCount = 32 ;
+
+ // Used for benchmarking if so configured.
+ private long startTime ;
+ private int vertexCount ;
+
+ // Bit-field masks: BMASK[i] = (1<<i)-1
+ private static final int BMASK[] = {
+ 0x0, 0x1, 0x3, 0x7,
+ 0xF, 0x1F, 0x3F, 0x7F,
+ 0xFF, 0x1FF, 0x3FF, 0x7FF,
+ 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF,
+ 0xFFFF, 0x1FFFF, 0x3FFFF, 0x7FFFF,
+ 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF,
+ 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF,
+ 0xFFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
+ 0xFFFFFFFF,
+ } ;
+
+ // A reference to the compressed data and the current offset.
+ private byte gcData[] ;
+ private int gcIndex ;
+
+ // The normals table for decoding 6-bit [u,v] spherical sextant coordinates.
+ private static final double gcNormals[][][] ;
+ private static final double NORMAL_MAX_Y_ANG = 0.615479709 ;
+ private static final boolean printNormalTable = false ;
+
+ /**
+ * Initialize the normals table.
+ */
+ static {
+ int i, j, inx, iny, inz ;
+ double th, psi, qnx, qny, qnz ;
+
+ gcNormals = new double[65][65][3] ;
+
+ for (i = 0 ; i < 65 ; i++) {
+ for (j = 0 ; j < 65 ; j++) {
+ if (i+j > 64) continue ;
+
+ psi = NORMAL_MAX_Y_ANG * (i / 64.0) ;
+ th = Math.asin(Math.tan(NORMAL_MAX_Y_ANG * ((64-j)/64.0))) ;
+
+ qnx = Math.cos(th) * Math.cos(psi) ;
+ qny = Math.sin(psi) ;
+ qnz = Math.sin(th) * Math.cos(psi) ;
+
+ // Convert the floating point normal to s1.14 bit notation,
+ // then back again.
+ qnx = qnx*16384.0 ; inx = (int)qnx ;
+ qnx = (double)inx ; qnx = qnx/16384.0 ;
+
+ qny = qny*16384.0 ; iny = (int)qny ;
+ qny = (double)iny ; qny = qny/16384.0 ;
+
+ qnz = qnz*16384.0 ; inz = (int)qnz ;
+ qnz = (double)inz ; qnz = qnz/16384.0 ;
+
+ gcNormals[i][j][0] = qnx ;
+ gcNormals[i][j][1] = qny ;
+ gcNormals[i][j][2] = qnz ;
+ }
+ }
+
+ if (printNormalTable) {
+ System.out.println("struct {") ;
+ System.out.println(" double nx, ny, nz ;") ;
+ System.out.println("} gcNormals[65][65] = {");
+ for (i = 0 ; i <= 64 ; i++) {
+ System.out.println("{") ;
+ for (j = 0 ; j <= 64 ; j++) {
+ if (j+i > 64) continue ;
+ System.out.println("{ " + gcNormals[i][j][0] +
+ ", " + gcNormals[i][j][1] +
+ ", " + gcNormals[i][j][2] + " }") ;
+ }
+ System.out.println("},") ;
+ }
+ System.out.println("}") ;
+ }
+ }
+
+ //
+ // The constructor.
+ //
+ GeometryDecompressor() {
+ curPos = new Point3f() ;
+ curNorm = new Vector3f() ;
+ curColor = new Color4f() ;
+ gctables = new HuffmanTableEntry[3][64] ;
+
+ for (int i = 0 ; i < 64 ; i++) {
+ gctables[0][i] = new HuffmanTableEntry() ;
+ gctables[1][i] = new HuffmanTableEntry() ;
+ gctables[2][i] = new HuffmanTableEntry() ;
+ }
+
+ meshBuffer = new MeshBufferEntry[16] ;
+ for (int i = 0 ; i < 16 ; i++)
+ meshBuffer[i] = new MeshBufferEntry() ;
+ }
+
+ /**
+ * Check version numbers and return true if compatible.
+ */
+ boolean checkVersion(int majorVersionNumber, int minorVersionNumber) {
+ return ((majorVersionNumber < this.majorVersionNumber) ||
+ ((majorVersionNumber == this.majorVersionNumber) &&
+ (minorVersionNumber <= this.minorVersionNumber))) ;
+ }
+
+ /**
+ * Decompress data and invoke abstract output methods.
+ *
+ * @param start byte offset to start of compressed geometry in data array
+ * @param length size of compressed geometry in bytes
+ * @param data array containing compressed geometry buffer of the
+ * specified length at the given offset from the start of the array
+ * @exception ArrayIndexOutOfBoundsException if start+length > data size
+ */
+ void decompress(int start, int length, byte data[]) {
+ if (debug)
+ System.out.println("GeometryDecompressor.decompress\n" +
+ " start: " + start +
+ " length: " + length +
+ " data array size: " + data.length) ;
+ if (benchmark)
+ benchmarkStart(length) ;
+
+ if (start+length > data.length)
+ throw new ArrayIndexOutOfBoundsException
+ (J3dUtilsI18N.getString("GeometryDecompressor0")) ;
+
+ // Set reference to compressed data and skip to start of data.
+ gcData = data ;
+ gcIndex = start ;
+
+ // Initialize state.
+ bitBufferCount = 0 ;
+ meshState = 0 ;
+ bundlingNorm = false ;
+ bundlingColor = false ;
+ doingAlpha = false ;
+ repCode = 0 ;
+
+ // Headers are interleaved for hardware implementations, so the
+ // first is always a nullop.
+ nextHeader = GC_V_NO_OP ;
+
+ // Enter decompression loop.
+ while (gcIndex < start+length)
+ processDecompression() ;
+
+ // Finish out any bits left in bitBuffer.
+ while (bitBufferCount > 0)
+ processDecompression() ;
+
+ if (benchmark)
+ benchmarkPrint(length) ;
+ }
+
+ //
+ // Return the next bitCount bits of compressed data.
+ //
+ private int getBits(int bitCount, String d) {
+ int bits ;
+
+ if (debug)
+ System.out.print(" getBits(" + bitCount + ") " + d + ", " +
+ bitBufferCount + " available at gcIndex " +
+ gcIndex) ;
+
+ if (bitCount == 0) {
+ if (debug) System.out.println(": got 0x0") ;
+ return 0 ;
+ }
+
+ if (bitBufferCount == 0) {
+ bitBuffer = (((gcData[gcIndex++] & 0xff) << 24) |
+ ((gcData[gcIndex++] & 0xff) << 16) |
+ ((gcData[gcIndex++] & 0xff) << 8) |
+ ((gcData[gcIndex++] & 0xff))) ;
+
+ bitBufferCount = 32 ;
+ }
+
+ if (bitBufferCount >= bitCount) {
+ bits = (bitBuffer >>> (32 - bitCount)) & BMASK[bitCount] ;
+ bitBuffer = bitBuffer << bitCount ;
+ bitBufferCount -= bitCount ;
+ } else {
+ bits = (bitBuffer >>> (32 - bitCount)) & BMASK[bitCount] ;
+ bits = bits >>> (bitCount - bitBufferCount) ;
+ bits = bits << (bitCount - bitBufferCount) ;
+
+ bitBuffer = (((gcData[gcIndex++] & 0xff) << 24) |
+ ((gcData[gcIndex++] & 0xff) << 16) |
+ ((gcData[gcIndex++] & 0xff) << 8) |
+ ((gcData[gcIndex++] & 0xff))) ;
+
+ bits = bits |
+ ((bitBuffer >>> (32 - (bitCount - bitBufferCount))) &
+ BMASK[bitCount - bitBufferCount]) ;
+
+ bitBuffer = bitBuffer << (bitCount - bitBufferCount) ;
+ bitBufferCount = 32 - (bitCount - bitBufferCount) ;
+ }
+
+ if (debug)
+ System.out.println(": got 0x" + Integer.toHexString(bits)) ;
+
+ return bits ;
+ }
+
+ //
+ // Shuffle interleaved headers and opcodes.
+ //
+ private void processDecompression() {
+ int mbp ;
+ currentHeader = nextHeader ;
+
+ if ((currentHeader & 0xC0) == GC_VERTEX) {
+ // Process a vertex.
+ if (!bundlingNorm && !bundlingColor) {
+ // get next opcode, process current position opcode
+ nextHeader = getBits(8, "header") ;
+ mbp = processDecompressionOpcode(0) ;
+
+ } else if (bundlingNorm && !bundlingColor) {
+ // get normal header, process current position opcode
+ nextHeader = getBits(6, "normal") ;
+ mbp = processDecompressionOpcode(0) ;
+ currentHeader = nextHeader | GC_SET_NORM ;
+
+ // get next opcode, process current normal opcode
+ nextHeader = getBits(8, "header") ;
+ processDecompressionOpcode(mbp) ;
+
+ } else if (!bundlingNorm && bundlingColor) {
+ // get color header, process current position opcode
+ nextHeader = getBits(6, "color") ;
+ mbp = processDecompressionOpcode(0) ;
+ currentHeader = nextHeader | GC_SET_COLOR ;
+
+ // get next opcode, process current color opcode
+ nextHeader = getBits(8, "header") ;
+ processDecompressionOpcode(mbp) ;
+
+ } else {
+ // get normal header, process current position opcode
+ nextHeader = getBits(6, "normal") ;
+ mbp = processDecompressionOpcode(0) ;
+ currentHeader = nextHeader | GC_SET_NORM ;
+
+ // get color header, process current normal opcode
+ nextHeader = getBits(6, "color") ;
+ processDecompressionOpcode(mbp) ;
+ currentHeader = nextHeader | GC_SET_COLOR ;
+
+ // get next opcode, process current color opcode
+ nextHeader = getBits(8, "header") ;
+ processDecompressionOpcode(mbp) ;
+ }
+
+ // Send out the complete vertex.
+ outputVertex(curPos, curNorm, curColor, repCode) ;
+ if (benchmark) vertexCount++ ;
+
+ // meshState bits get turned off in the setColor and setNormal
+ // routines in order to keep track of what data a mesh buffer
+ // reference should use.
+ meshState |= USE_MESH_NORMAL ;
+ meshState |= USE_MESH_COLOR ;
+
+ } else {
+ // Non-vertex case: get next opcode, then process current opcode.
+ nextHeader = getBits(8, "header") ;
+ processDecompressionOpcode(0) ;
+ }
+ }
+
+ //
+ // Decode the opcode in currentHeader, and dispatch to the appropriate
+ // processing method.
+ //
+ private int processDecompressionOpcode(int mbp) {
+ if ((currentHeader & 0xC0) == GC_SET_NORM)
+ processSetNormal(mbp) ;
+ else if ((currentHeader & 0xC0) == GC_SET_COLOR)
+ processSetColor(mbp) ;
+ else if ((currentHeader & 0xC0) == GC_VERTEX)
+ // Return the state of the mesh buffer push bit
+ // when processing a vertex.
+ return processVertex() ;
+ else if ((currentHeader & 0xE0) == GC_MESH_B_R) {
+ processMeshBR() ;
+
+ // Send out the complete vertex.
+ outputVertex(curPos, curNorm, curColor, repCode) ;
+ if (benchmark) vertexCount++ ;
+
+ // meshState bits get turned off in the setColor and setNormal
+ // routines in order to keep track of what data a mesh buffer
+ // reference should use.
+ meshState |= USE_MESH_NORMAL ;
+ meshState |= USE_MESH_COLOR ;
+ }
+ else if ((currentHeader & 0xF8) == GC_SET_STATE)
+ processSetState() ;
+ else if ((currentHeader & 0xF8) == GC_SET_TABLE)
+ processSetTable() ;
+ else if ((currentHeader & 0xFF) == GC_EOS)
+ processEos() ;
+ else if ((currentHeader & 0xFF) == GC_V_NO_OP)
+ processVNoop() ;
+ else if ((currentHeader & 0xFF) == GC_PASS_THROUGH)
+ processPassThrough() ;
+ else if ((currentHeader & 0xFF) == GC_SKIP_8)
+ processSkip8() ;
+
+ return 0 ;
+ }
+
+ //
+ // Process a set state opcode.
+ //
+ private void processSetState() {
+ int ii ;
+ if (debug)
+ System.out.println("GeometryDecompressor.processSetState") ;
+
+ ii = getBits(3, "bundling") ;
+
+ bundlingNorm = ((currentHeader & 0x1) != 0) ;
+ bundlingColor = (((ii >>> 2) & 0x1) != 0) ;
+ doingAlpha = (((ii >>> 1) & 0x1) != 0) ;
+
+ if (debug)
+ System.out.println(" bundling normal: " + bundlingNorm +
+ " bundling color: " + bundlingColor +
+ " alpha present: " + doingAlpha) ;
+
+ // Call the abstract output implementation.
+ outputVertexFormat(bundlingNorm, bundlingColor, doingAlpha) ;
+ }
+
+ //
+ // Process a set decompression table opcode.
+ //
+ // Extract the parameters of the table set command,
+ // and set the approprate table entries.
+ //
+ private void processSetTable() {
+ HuffmanTableEntry gct[] ;
+ int i, adr, tagLength, dataLength, rightShift, absolute ;
+ int ii, index ;
+
+ if (debug)
+ System.out.println("GeometryDecompressor.processSetTable") ;
+
+ // Get reference to approprate 64 entry table.
+ index = (currentHeader & 0x6) >>> 1 ;
+ gct = gctables[index] ;
+
+ // Get the remaining bits of the set table command.
+ ii = getBits(15, "set table") ;
+
+ // Extract the individual fields from the two bit strings.
+ adr = ((currentHeader & 0x1) << 6) | ((ii >>> 9) & 0x3F) ;
+
+ // Get data length. For positions and colors, 0 really means 16, as 0
+ // lengths are meaningless for them. Normal components are allowed to
+ // have lengths of 0.
+ dataLength = (ii >>> 5) & 0x0F ;
+ if (dataLength == 0 && index != 2)
+ dataLength = 16 ;
+
+ rightShift = ii & 0x0F ;
+ absolute = (ii >>> 4) & 0x1 ;
+
+ //
+ // Decode the tag length from the address field by finding the
+ // first set 1 from the left in the bitfield.
+ //
+ for (tagLength = 6 ; tagLength > 0 ; tagLength--) {
+ if ((adr >> tagLength) != 0) break ;
+ }
+
+ // Shift the address bits up into place, and off the leading 1.
+ adr = (adr << (6 - tagLength)) & 0x3F ;
+
+ if (debug)
+ System.out.println(" table " + ((currentHeader & 0x6) >>> 1) +
+ " address " + adr +
+ " tag length " + tagLength +
+ " data length " + dataLength +
+ " shift " + rightShift +
+ " absolute " + absolute) ;
+
+ // Fill in the table fields with the specified values.
+ for (i = 0 ; i < (1 << (6 - tagLength)) ; i++) {
+ gct[adr+i].tagLength = tagLength ;
+ gct[adr+i].dataLength = dataLength ;
+ gct[adr+i].rightShift = rightShift ;
+ gct[adr+i].absolute = absolute ;
+ }
+ }
+
+
+ //
+ // Process a vertex opcode. Any bundled normal and/or color will be
+ // processed by separate methods. Return the mesh buffer push indicator.
+ //
+ private int processVertex() {
+ HuffmanTableEntry gct ;
+ float fX, fY, fZ ;
+ short dx, dy, dz ;
+ int mbp, x, y, z, dataLen ;
+ int ii ;
+
+ // If the next command is a mesh buffer reference
+ // then use colors and normals from the mesh buffer.
+ meshState = 0 ;
+
+ // Get a reference to the approprate tag table entry.
+ gct = gctables[0][currentHeader & 0x3F] ;
+
+ if (debug) System.out.println("GeometryDecompressor.processVertex\n" +
+ gct.toString()) ;
+
+ // Get the true length of the data.
+ dataLen = gct.dataLength - gct.rightShift ;
+
+ // Read in the replace code and mesh buffer push bits,
+ // if they're not in the current header.
+ if (6 - (3 * dataLen) - gct.tagLength > 0) {
+ int numBits = 6 - (3 * dataLen) - gct.tagLength ;
+ int jj ;
+
+ jj = currentHeader & BMASK[numBits] ;
+ ii = getBits(3 - numBits, "repcode/mbp") ;
+ ii |= (jj << (3 - numBits)) ;
+ }
+ else
+ ii = getBits(3, "repcode/mbp") ;
+
+ repCode = ii >>> 1 ;
+ mbp = ii & 0x1 ;
+
+ // Read in x, y, and z components.
+ x = currentHeader & BMASK[6-gct.tagLength] ;
+
+ if (gct.tagLength + dataLen == 6) {
+ y = getBits(dataLen, "y") ;
+ z = getBits(dataLen, "z") ;
+ } else if (gct.tagLength + dataLen < 6) {
+ x = x >> (6 - gct.tagLength - dataLen) ;
+
+ y = currentHeader & BMASK[6 - gct.tagLength - dataLen] ;
+ if (gct.tagLength + 2*dataLen == 6) {
+ z = getBits(dataLen, "z") ;
+ } else if (gct.tagLength + 2*dataLen < 6) {
+ y = y >> (6 - gct.tagLength - 2*dataLen) ;
+
+ z = currentHeader & BMASK[6 - gct.tagLength - 2*dataLen] ;
+ if (gct.tagLength + 3*dataLen < 6) {
+ z = z >> (6 - gct.tagLength - 3*dataLen) ;
+ } else if (gct.tagLength + 3*dataLen > 6) {
+ ii = getBits(dataLen - (6 - gct.tagLength - 2*dataLen),
+ "z") ;
+ z = (z << (dataLen - (6 - gct.tagLength - 2*dataLen)))
+ | ii ;
+ }
+ } else {
+ ii = getBits(dataLen - (6 - gct.tagLength - dataLen), "y") ;
+ y = (y << (dataLen - (6 - gct.tagLength - dataLen))) | ii ;
+ z = getBits(dataLen, "z") ;
+ }
+ } else {
+ ii = getBits(dataLen - (6 - gct.tagLength), "x") ;
+ x = (x << (dataLen - (6 - gct.tagLength))) | ii ;
+ y = getBits(dataLen, "y") ;
+ z = getBits(dataLen, "z") ;
+ }
+
+ // Sign extend delta x y z components.
+ x = x << (32 - dataLen) ; x = x >> (32 - dataLen) ;
+ y = y << (32 - dataLen) ; y = y >> (32 - dataLen) ;
+ z = z << (32 - dataLen) ; z = z >> (32 - dataLen) ;
+
+ // Normalize values.
+ dx = (short)(x << gct.rightShift) ;
+ dy = (short)(y << gct.rightShift) ;
+ dz = (short)(z << gct.rightShift) ;
+
+ // Update current position, first adding deltas if in relative mode.
+ if (gct.absolute != 0) {
+ curX = dx ; curY = dy ; curZ = dz ;
+ if (debug) System.out.println(" absolute position: " +
+ curX + " " + curY + " " + curZ) ;
+ } else {
+ curX += dx ; curY += dy ; curZ += dz ;
+ if (debug) System.out.println(" delta position: " +
+ dx + " " + dy + " " + dz) ;
+ }
+
+ // Do optional mesh buffer push.
+ if (mbp != 0) {
+ // Increment to next position (meshIndex is initialized to 15).
+ meshIndex = (meshIndex + 1) & 0xF ;
+ meshBuffer[meshIndex].x = curX ;
+ meshBuffer[meshIndex].y = curY ;
+ meshBuffer[meshIndex].z = curZ ;
+ if (debug)
+ System.out.println(" pushed position into mesh buffer at " +
+ meshIndex) ;
+ }
+
+ // Convert point back to [-1..1] floating point.
+ fX = curX ; fX /= 32768.0 ;
+ fY = curY ; fY /= 32768.0 ;
+ fZ = curZ ; fZ /= 32768.0 ;
+ if (debug)
+ System.out.println(" result position " + fX + " " + fY + " " + fZ) ;
+
+ curPos.set(fX, fY, fZ) ;
+ return mbp ;
+ }
+
+
+ //
+ // Process a set current normal opcode.
+ //
+ private void processSetNormal(int mbp) {
+ HuffmanTableEntry gct ;
+ int index, du, dv, n, dataLength ;
+ int ii ;
+
+ // if next command is a mesh buffer reference, use this normal
+ meshState &= ~USE_MESH_NORMAL ;
+
+ // use table 2 for normals
+ gct = gctables[2][currentHeader & 0x3F] ;
+
+ if (debug)
+ System.out.println("GeometryDecompressor.processSetNormal\n" +
+ gct.toString()) ;
+
+ // subtract up-shift amount to get true data (u, v) length
+ dataLength = gct.dataLength - gct.rightShift ;
+
+ if (gct.absolute != 0) {
+ //
+ // Absolute normal case. Extract index from 6-bit tag.
+ //
+ index = currentHeader & BMASK[6-gct.tagLength] ;
+
+ if (gct.tagLength != 0) {
+ // read in the rest of the 6-bit sex/oct pair (index)
+ ii = getBits(6 - (6 - gct.tagLength), "sex/oct") ;
+ index = (index << (6 - (6 - gct.tagLength))) | ii ;
+ }
+
+ // read in u and v data
+ curU = getBits(dataLength, "u") ;
+ curV = getBits(dataLength, "v") ;
+
+ // normalize u, v, sextant, and octant
+ curU = curU << gct.rightShift ;
+ curV = curV << gct.rightShift ;
+
+ curSex = (index >> 3) & 0x7 ;
+ curOct = index & 0x7 ;
+
+ if (debug) {
+ if (curSex < 6)
+ System.out.println(" absolute normal: sex " + curSex +
+ " oct " + curOct +
+ " u " + curU + " v " + curV) ;
+ else
+ System.out.println(" special normal: sex " + curSex +
+ " oct " + curOct) ;
+ }
+ } else {
+ //
+ // Relative normal case. Extract du from 6-bit tag.
+ //
+ du = currentHeader & BMASK[6-gct.tagLength] ;
+
+ if (gct.tagLength + dataLength < 6) {
+ // normalize du, get dv
+ du = du >> (6 - gct.tagLength - dataLength) ;
+ dv = currentHeader & BMASK[6 - gct.tagLength - dataLength] ;
+
+ if (gct.tagLength + 2*dataLength < 6) {
+ // normalize dv
+ dv = dv >> (6 - gct.tagLength - 2*dataLength) ;
+ } else if (gct.tagLength + 2*dataLength > 6) {
+ // read in rest of dv and normalize it
+ ii = getBits(dataLength -
+ (6 - gct.tagLength - dataLength), "dv") ;
+ dv = (dv << (dataLength -
+ (6 - gct.tagLength - dataLength))) | ii ;
+ }
+ } else if (gct.tagLength + dataLength > 6) {
+ // read in rest of du and normalize it
+ ii = getBits(dataLength - (6 - gct.tagLength), "du") ;
+ du = (du << (dataLength - (6 - gct.tagLength))) | ii ;
+ // read in dv
+ dv = getBits(dataLength, "dv") ;
+ } else {
+ // read in dv
+ dv = getBits(dataLength, "dv") ;
+ }
+
+ // Sign extend delta uv components.
+ du = du << (32 - dataLength) ; du = du >> (32 - dataLength) ;
+ dv = dv << (32 - dataLength) ; dv = dv >> (32 - dataLength) ;
+
+ // normalize values
+ du = du << gct.rightShift ;
+ dv = dv << gct.rightShift ;
+
+ // un-delta
+ curU += du ;
+ curV += dv ;
+
+ if (debug)
+ System.out.println(" delta normal: du " + du + " dv " + dv) ;
+
+ //
+ // Check for normal wrap.
+ //
+ if (! ((curU >= 0) && (curV >= 0) && (curU + curV <= 64)))
+ if ((curU < 0) && (curV >= 0)) {
+ // wrap on u, same octant, different sextant
+ curU = -curU ;
+ switch (curSex) {
+ case 0: curSex = 4 ; break ;
+ case 1: curSex = 5 ; break ;
+ case 2: curSex = 3 ; break ;
+ case 3: curSex = 2 ; break ;
+ case 4: curSex = 0 ; break ;
+ case 5: curSex = 1 ; break ;
+ }
+ } else if ((curU >= 0) && (curV < 0)) {
+ // wrap on v, same sextant, different octant
+ curV = -curV ;
+ switch (curSex) {
+ case 1: case 5:
+ curOct = curOct ^ 4 ; // invert x axis
+ break ;
+ case 0: case 4:
+ curOct = curOct ^ 2 ; // invert y axis
+ break ;
+ case 2: case 3:
+ curOct = curOct ^ 1 ; // invert z axis
+ break ;
+ }
+ } else if (curU + curV > 64) {
+ // wrap on uv, same octant, different sextant
+ curU = 64 - curU ;
+ curV = 64 - curV ;
+ switch (curSex) {
+ case 0: curSex = 2 ; break ;
+ case 1: curSex = 3 ; break ;
+ case 2: curSex = 0 ; break ;
+ case 3: curSex = 1 ; break ;
+ case 4: curSex = 5 ; break ;
+ case 5: curSex = 4 ; break ;
+ }
+ } else {
+ throw new IllegalArgumentException
+ (J3dUtilsI18N.getString("GeometryDecompressor1")) ;
+ }
+ }
+
+ // do optional mesh buffer push
+ if (mbp != 0) {
+ if (debug)
+ System.out.println(" pushing normal into mesh buffer at " +
+ meshIndex) ;
+
+ meshBuffer[meshIndex].sextant = (short)curSex ;
+ meshBuffer[meshIndex].octant = (short)curOct ;
+ meshBuffer[meshIndex].u = (short)curU ;
+ meshBuffer[meshIndex].v = (short)curV ;
+ }
+
+ // convert normal back to [-1..1] floating point
+ indexNormal(curSex, curOct, curU, curV, curNorm) ;
+
+ // a set normal opcode when normals aren't bundled with the vertices
+ // is a global normal change.
+ if (! bundlingNorm) outputNormal(curNorm) ;
+ }
+
+
+ //
+ // Get the floating point normal from its sextant, octant, u, and v.
+ //
+ private void indexNormal(int sex, int oct, int u, int v, Vector3f n) {
+ float nx, ny, nz, t ;
+
+ if (debug) System.out.println(" sextant " + sex + " octant " + oct +
+ " u " + u + " v " + v) ;
+ if (sex > 5) {
+ // special normals
+ switch (oct & 0x1) {
+ case 0: // six coordinate axes
+ switch (((sex & 0x1) << 1) | ((oct & 0x4) >> 2)) {
+ case 0: nx = 1.0f ; ny = nz = 0.0f ; break ;
+ case 1: ny = 1.0f ; nx = nz = 0.0f ; break ;
+ default:
+ case 2: nz = 1.0f ; nx = ny = 0.0f ; break ;
+ }
+ sex = 0 ; oct = (oct & 0x2) >> 1 ;
+ oct = (oct << 2) | (oct << 1) | oct ;
+ break ;
+ case 1: // eight mid
+ default:
+ oct = ((sex & 0x1) << 2) | (oct >> 1) ;
+ sex = 0 ;
+ nx = ny = nz = (float)(1.0/Math.sqrt(3.0)) ;
+ break ;
+ }
+ if ((oct & 0x1) != 0) nz = -nz ;
+ if ((oct & 0x2) != 0) ny = -ny ;
+ if ((oct & 0x4) != 0) nx = -nx ;
+
+ } else {
+ // regular normals
+ nx = (float)gcNormals[v][u][0] ;
+ ny = (float)gcNormals[v][u][1] ;
+ nz = (float)gcNormals[v][u][2] ;
+
+ // reverse the swap
+ if ((sex & 0x4) != 0) { t = nx ; nx = nz ; nz = t ; }
+ if ((sex & 0x2) != 0) { t = ny ; ny = nz ; nz = t ; }
+ if ((sex & 0x1) != 0) { t = nx ; nx = ny ; ny = t ; }
+
+ // reverse the sign flip
+ if ((oct & 0x1) != 0) nz = -nz ;
+ if ((oct & 0x2) != 0) ny = -ny ;
+ if ((oct & 0x4) != 0) nx = -nx ;
+ }
+
+ // return resulting normal
+ n.set(nx, ny, nz) ;
+ if (debug)
+ System.out.println(" result normal: " + nx + " " + ny + " " + nz) ;
+ }
+
+
+ //
+ // Process a set current color command.
+ //
+ private void processSetColor(int mbp) {
+ HuffmanTableEntry gct ;
+ short dr, dg, db, da ;
+ float fR, fG, fB, fA ;
+ int r, g, b, a, index, dataLength ;
+ int ii ;
+
+ // If the next command is a mesh buffer reference, use this color.
+ meshState &= ~USE_MESH_COLOR ;
+
+ // Get the huffman table entry.
+ gct = gctables[1][currentHeader & 0x3F] ;
+
+ if (debug)
+ System.out.println("GeometryDecompressor.processSetColor\n" +
+ gct.toString()) ;
+
+ // Get the true length of the data.
+ dataLength = gct.dataLength - gct.rightShift ;
+
+ // Read in red, green, blue, and possibly alpha.
+ r = currentHeader & BMASK[6 - gct.tagLength] ;
+ a = 0 ;
+
+ if (gct.tagLength + dataLength == 6) {
+ g = getBits(dataLength, "g") ;
+ b = getBits(dataLength, "b") ;
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+ else if (gct.tagLength + dataLength < 6) {
+ r = r >> (6 - gct.tagLength - dataLength) ;
+
+ g = currentHeader & BMASK[6-gct.tagLength-dataLength] ;
+ if (gct.tagLength + 2*dataLength == 6) {
+ b = getBits(dataLength, "b") ;
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+ else if (gct.tagLength + 2*dataLength < 6) {
+ g = g >> (6 - gct.tagLength - 2*dataLength) ;
+
+ b = currentHeader & BMASK[6-gct.tagLength-2*dataLength] ;
+ if (gct.tagLength + 3*dataLength == 6) {
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+ else if (gct.tagLength + 3*dataLength < 6) {
+ b = b >> (6 - gct.tagLength - 3*dataLength) ;
+
+ if (doingAlpha) {
+ a = currentHeader &
+ BMASK[6 - gct.tagLength - 4*dataLength] ;
+ if (gct.tagLength + 4 * dataLength < 6) {
+ a = a >> (6 - gct.tagLength - 3*dataLength) ;
+ }
+ else if (gct.tagLength + 4 * dataLength > 6) {
+ ii = getBits(dataLength -
+ (6-gct.tagLength - 3*dataLength), "a") ;
+ a = (a << (dataLength -
+ (6-gct.tagLength - 3*dataLength))) | ii ;
+ }
+ }
+ } else {
+ ii = getBits(dataLength -
+ (6 - gct.tagLength - 2*dataLength), "b") ;
+ b = (b << (dataLength -
+ (6 - gct.tagLength - 2*dataLength))) | ii ;
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+ } else {
+ ii = getBits(dataLength - (6 - gct.tagLength - dataLength),
+ "g") ;
+ g = (g << (dataLength -
+ (6 - gct.tagLength - dataLength))) | ii ;
+ b = getBits(dataLength, "b") ;
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+ } else {
+ ii = getBits(dataLength - (6 - gct.tagLength), "r") ;
+ r = (r << (dataLength - (6 - gct.tagLength))) | ii ;
+ g = getBits(dataLength, "g") ;
+ b = getBits(dataLength, "b") ;
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+
+ // Sign extend delta x y z components.
+ r <<= (32 - dataLength) ; r >>= (32 - dataLength) ;
+ g <<= (32 - dataLength) ; g >>= (32 - dataLength) ;
+ b <<= (32 - dataLength) ; b >>= (32 - dataLength) ;
+ a <<= (32 - dataLength) ; a >>= (32 - dataLength) ;
+
+ // Normalize values.
+ dr = (short)(r << gct.rightShift) ;
+ dg = (short)(g << gct.rightShift) ;
+ db = (short)(b << gct.rightShift) ;
+ da = (short)(a << gct.rightShift) ;
+
+ // Update current position, first adding deltas if in relative mode.
+ if (gct.absolute != 0) {
+ curR = dr ; curG = dg ; curB = db ;
+ if (doingAlpha) curA = da ;
+ if (debug) System.out.println(" absolute color: r " + curR +
+ " g " + curG + " b " + curB +
+ " a " + curA) ;
+ } else {
+ curR += dr ; curG += dg ; curB += db ;
+ if (doingAlpha) curA += da ;
+ if (debug) System.out.println(" delta color: dr " + dr +
+ " dg " + dg + " db " + db +
+ " da " + da) ;
+ }
+
+ // Do optional mesh buffer push.
+ if (mbp != 0) {
+ if (debug)
+ System.out.println(" pushing color into mesh buffer at " +
+ meshIndex) ;
+
+ meshBuffer[meshIndex].r = curR ;
+ meshBuffer[meshIndex].g = curG ;
+ meshBuffer[meshIndex].b = curB ;
+ meshBuffer[meshIndex].a = curA ;
+ }
+
+ // Convert point back to [-1..1] floating point.
+ fR = curR ; fR /= 32768.0 ;
+ fG = curG ; fG /= 32768.0 ;
+ fB = curB ; fB /= 32768.0 ;
+ fA = curA ; fA /= 32768.0 ;
+
+ curColor.set(fR, fG, fB, fA) ;
+ if (debug) System.out.println(" result color: " + fR +
+ " " + fG + " " + fB + " " + fA) ;
+
+ // A set color opcode when colors aren't bundled with the vertices
+ // is a global color change.
+ if (! bundlingColor) outputColor(curColor) ;
+ }
+
+
+ //
+ // Process a mesh buffer reference command.
+ //
+ private void processMeshBR() {
+ MeshBufferEntry entry ;
+ int index, normal ;
+ int ii ;
+
+ if (debug)
+ System.out.println("GeometryDecompressor.processMeshBR") ;
+
+ ii = getBits(1, "mbr") ;
+
+ index = (currentHeader >>> 1) & 0xF ;
+ repCode = ((currentHeader & 0x1) << 1) | ii ;
+
+ // Adjust index to proper place in fifo.
+ index = (meshIndex - index) & 0xf ;
+ if (debug)
+ System.out.println(" using index " + index) ;
+
+ // Get reference to mesh buffer entry.
+ entry = meshBuffer[index] ;
+ curX = entry.x ;
+ curY = entry.y ;
+ curZ = entry.z ;
+
+ // Convert point back to [-1..1] floating point.
+ curPos.set(((float)curX)/32768.0f,
+ ((float)curY)/32768.0f,
+ ((float)curZ)/32768.0f) ;
+
+ if (debug) System.out.println(" retrieved position " + curPos.x +
+ " " + curPos.y + " " + curPos.z +
+ " replace code " + repCode) ;
+
+ // Get mesh buffer normal if previous opcode was not a setNormal.
+ if (bundlingNorm && ((meshState & USE_MESH_NORMAL) != 0)) {
+ curSex = entry.sextant ;
+ curOct = entry.octant ;
+ curU = entry.u ;
+ curV = entry.v ;
+
+ // Convert normal back to -1.0 - 1.0 floating point from index.
+ normal = (curSex<<15) | (curOct<<12) | (curU<<6) | curV ;
+
+ if (debug) System.out.println(" retrieving normal") ;
+ indexNormal(curSex, curOct, curU, curV, curNorm) ;
+ }
+
+ // Get mesh buffer color if previous opcode was not a setColor.
+ if (bundlingColor && ((meshState & USE_MESH_COLOR) != 0)) {
+ curR = entry.r ;
+ curG = entry.g ;
+ curB = entry.b ;
+
+ // Convert point back to -1.0 - 1.0 floating point.
+ curColor.x = curR ; curColor.x /= 32768.0 ;
+ curColor.y = curG ; curColor.y /= 32768.0 ;
+ curColor.z = curB ; curColor.z /= 32768.0 ;
+
+ if (doingAlpha) {
+ curA = entry.a ;
+ curColor.w = curA ; curColor.w /= 32768.0 ;
+ }
+ if (debug)
+ System.out.println(" retrieved color " + curColor.x +
+ " " + curColor.y + " " + curColor.z +
+ " " + curColor.w) ;
+ }
+
+ // Reset meshState.
+ meshState = 0 ;
+ }
+
+
+ // Process a end-of-stream opcode.
+ private void processEos() {
+ if (debug) System.out.println("GeometryDecompressor.processEos") ;
+ }
+
+ // Process a variable length no-op opcode.
+ private void processVNoop() {
+ int ii, ct ;
+ if (debug) System.out.println("GeometryDecompressor.processVNoop") ;
+
+ ct = getBits(5, "noop count") ;
+ ii = getBits(ct, "noop bits") ;
+ }
+
+ // Process a pass-through opcode.
+ private void processPassThrough() {
+ int ignore ;
+ if (debug)
+ System.out.println("GeometryDecompressor.processPassThrough") ;
+
+ ignore = getBits(24, "passthrough") ;
+ ignore = getBits(32, "passthrough") ;
+ }
+
+ // Process a skip-8 opcode.
+ private void processSkip8() {
+ int skip ;
+ if (debug) System.out.println("GeometryDecompressor.processSkip8") ;
+
+ skip = getBits(8, "skip8") ;
+ }
+
+ private void benchmarkStart(int length) {
+ vertexCount = 0 ;
+ System.out.println(" GeometryDecompressor: decompressing " +
+ length + " bytes...") ;
+ startTime = System.currentTimeMillis() ;
+ }
+
+ private void benchmarkPrint(int length) {
+ float t = (System.currentTimeMillis() - startTime) / 1000.0f ;
+ System.out.println
+ (" done in " + t + " sec." + "\n" +
+ " decompressed " + vertexCount + " vertices at " +
+ (vertexCount/t) + " vertices/sec\n") ;
+
+ System.out.print(" vertex data present: coords") ;
+ int floatVertexSize = 12 ;
+ if (bundlingNorm) {
+ System.out.print(" normals") ;
+ floatVertexSize += 12 ;
+ }
+ if (bundlingColor) {
+ System.out.println(" colors") ;
+ floatVertexSize += 12 ;
+ }
+ if (doingAlpha) {
+ System.out.println(" alpha") ;
+ floatVertexSize += 4 ;
+ }
+ System.out.println() ;
+
+ System.out.println
+ (" bytes of data in generalized strip output: " +
+ (vertexCount * floatVertexSize) + "\n" +
+ " compression ratio: " +
+ (length / (float)(vertexCount * floatVertexSize)) + "\n") ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDecompressorShape3D.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDecompressorShape3D.java
new file mode 100644
index 0000000..3af98a5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDecompressorShape3D.java
@@ -0,0 +1,523 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import com.sun.j3d.internal.J3dUtilsI18N;
+import java.util.ArrayList;
+import javax.media.j3d.Appearance;
+import javax.media.j3d.GeometryArray;
+import javax.media.j3d.GeometryStripArray;
+import javax.media.j3d.LineStripArray;
+import javax.media.j3d.Material;
+import javax.media.j3d.PointArray;
+import javax.media.j3d.Shape3D;
+import javax.media.j3d.TriangleArray;
+import javax.media.j3d.TriangleStripArray;
+import javax.vecmath.Color4f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+
+/**
+ * This class implements a Shape3D backend for the abstract
+ * GeometryDecompressor.
+ */
+class GeometryDecompressorShape3D extends GeometryDecompressor {
+ private static final boolean debug = false ;
+ private static final boolean benchmark = false ;
+ private static final boolean statistics = false ;
+ private static final boolean printInfo = debug || benchmark || statistics ;
+
+ // Type of connections in the compressed data:
+ // TYPE_POINT (1), TYPE_LINE (2), or TYPE_TRIANGLE (4).
+ private int bufferDataType ;
+
+ // Data bundled with each vertex: bitwise combination of
+ // NORMAL_IN_BUFFER (1), COLOR_IN_BUFFER (2), ALPHA_IN_BUFFER (4).
+ private int dataPresent ;
+
+ // List for accumulating the output of the decompressor and converting to
+ // GeometryArray representations.
+ private GeneralizedVertexList vlist ;
+
+ // Accumulates Shape3D objects constructed from decompressor output.
+ private ArrayList shapes ;
+
+ // Decompressor output state variables.
+ private Color4f curColor ;
+ private Vector3f curNormal ;
+
+ // Variables for gathering statistics.
+ private int origVertexCount ;
+ private int stripCount ;
+ private int vertexCount ;
+ private int triangleCount ;
+ private long startTime ;
+ private long endTime ;
+
+ // Triangle array type to construct.
+ private int triOutputType ;
+
+ // Types of triangle output available.
+ private static final int TRI_SET = 0 ;
+ private static final int TRI_STRIP_SET = 1 ;
+ private static final int TRI_STRIP_AND_FAN_SET = 2 ;
+ private static final int TRI_STRIP_AND_TRI_SET = 3 ;
+
+ // Private convenience copies of various constants.
+ private static final int TYPE_POINT =
+ CompressedGeometryRetained.TYPE_POINT ;
+ private static final int TYPE_LINE =
+ CompressedGeometryRetained.TYPE_LINE ;
+ private static final int TYPE_TRIANGLE =
+ CompressedGeometryRetained.TYPE_TRIANGLE ;
+ private static final int FRONTFACE_CCW =
+ GeneralizedStripFlags.FRONTFACE_CCW ;
+
+ /**
+ * Decompress the given compressed geometry.
+ * @param cgr CompressedGeometryRetained object with compressed geometry
+ * @return an array of Shape3D with TriangleArray geometry if compressed
+ * data contains triangles; otherwise, Shape3D array containing PointArray
+ * or LineStripArray geometry
+ * @see CompressedGeometry
+ * @see GeometryDecompressor
+ */
+ Shape3D[] toTriangleArrays(CompressedGeometryRetained cgr) {
+ return decompress(cgr, TRI_SET) ;
+ }
+
+
+ /**
+ * Decompress the given compressed geometry.
+ * @param cgr CompressedGeometryRetained object with compressed geometry
+ * @return an array of Shape3D with TriangleStripArray geometry if
+ * compressed data contains triangles; otherwise, Shape3D array containing
+ * PointArray or LineStripArray geometry
+ * @see CompressedGeometry
+ * @see GeometryDecompressor
+ */
+ Shape3D[] toTriangleStripArrays(CompressedGeometryRetained cgr) {
+ return decompress(cgr, TRI_STRIP_SET) ;
+ }
+
+
+ /**
+ * Decompress the given compressed geometry.
+ * @param cgr CompressedGeometryRetained object with compressed geometry
+ * @return an array of Shape3D with TriangleStripArray and
+ * TriangleFanArray geometry if compressed data contains triangles;
+ * otherwise, Shape3D array containing PointArray or LineStripArray
+ * geometry
+ * @see CompressedGeometry
+ * @see GeometryDecompressor
+ */
+ Shape3D[] toStripAndFanArrays(CompressedGeometryRetained cgr) {
+ return decompress(cgr, TRI_STRIP_AND_FAN_SET) ;
+ }
+
+
+ /**
+ * Decompress the given compressed geometry.
+ * @param cgr CompressedGeometryRetained object with compressed geometry
+ * @return an array of Shape3D with TriangleStripArray and
+ * TriangleArray geometry if compressed data contains triangles;
+ * otherwise, Shape3D array containing PointArray or LineStripArray
+ * geometry
+ * @see CompressedGeometry
+ * @see GeometryDecompressor
+ */
+ Shape3D[] toStripAndTriangleArrays(CompressedGeometryRetained cgr) {
+ return decompress(cgr, TRI_STRIP_AND_TRI_SET) ;
+ }
+
+ /**
+ * Decompress the data contained in a CompressedGeometryRetained and
+ * return an array of Shape3D objects using the specified triangle output
+ * type. The triangle output type is ignored if the compressed data
+ * contains points or lines.
+ */
+ private Shape3D[] decompress(CompressedGeometryRetained cgr,
+ int triOutputType) {
+
+ if (! checkVersion(cgr.majorVersionNumber, cgr.minorVersionNumber)) {
+ return null ;
+ }
+
+ vlist = null ;
+ curColor = null ;
+ curNormal = null ;
+
+ // Get descriptors for compressed data.
+ bufferDataType = cgr.bufferType ;
+ dataPresent = cgr.bufferContents ;
+ if (printInfo) beginPrint() ;
+
+ // Initialize the decompressor backend.
+ this.triOutputType = triOutputType ;
+ shapes = new ArrayList() ;
+
+ // Call the superclass decompress() method which calls the output
+ // methods of this subclass. The results are stored in vlist.
+ super.decompress(cgr.offset, cgr.size, cgr.compressedGeometry) ;
+
+ // Convert the decompressor output to Shape3D objects.
+ addShape3D() ;
+ if (printInfo) endPrint() ;
+
+ // Return the fixed-length output array.
+ Shape3D shapeArray[] = new Shape3D[shapes.size()] ;
+ return (Shape3D[])shapes.toArray(shapeArray) ;
+ }
+
+ /**
+ * Initialize the vertex output list based on the vertex format provided
+ * by the SetState decompression command.
+ */
+ void outputVertexFormat(boolean bundlingNorm, boolean bundlingColor,
+ boolean doingAlpha) {
+
+ if (vlist != null)
+ // Construct shapes using the current vertex format.
+ addShape3D() ;
+
+ int vertexFormat = GeometryArray.COORDINATES ;
+
+ if (bundlingNorm) {
+ vertexFormat |= GeometryArray.NORMALS ;
+ }
+
+ if (bundlingColor) {
+ if (doingAlpha) {
+ vertexFormat |= GeometryArray.COLOR_4;
+ } else {
+ vertexFormat |= GeometryArray.COLOR_3;
+ }
+ }
+
+ vlist = new GeneralizedVertexList(vertexFormat, FRONTFACE_CCW) ;
+ }
+
+ /**
+ * Add a new decompressed vertex to the current list.
+ */
+ void outputVertex(Point3f position, Vector3f normal,
+ Color4f color, int vertexReplaceCode) {
+
+ if (curNormal != null) normal = curNormal ;
+ vlist.addVertex(position, normal, color, vertexReplaceCode) ;
+
+ if (debug) {
+ System.out.println(" outputVertex: flag " + vertexReplaceCode) ;
+ System.out.println(" position " + position.toString()) ;
+ if (normal != null)
+ System.out.println(" normal " + normal.toString()) ;
+ if (color != null)
+ System.out.println(" color " + color.toString()) ;
+ }
+ }
+
+ /**
+ * Create a Shape3D using the current color for both the ambient and
+ * diffuse material colors, then start a new vertex list for the new
+ * color. The outputColor() method is never called if colors are bundled
+ * with each vertex in the compressed buffer.
+ */
+ void outputColor(Color4f color) {
+ if (debug) System.out.println(" outputColor: " + color.toString()) ;
+
+ if (vlist.size() > 0) {
+ // Construct Shape3D using the current color.
+ addShape3D() ;
+
+ // Start a new vertex list for the new color.
+ vlist = new GeneralizedVertexList(vlist.vertexFormat,
+ FRONTFACE_CCW) ;
+ }
+ if (curColor == null) curColor = new Color4f() ;
+ curColor.set(color) ;
+ }
+
+ /**
+ * Set the current normal that will be copied to each succeeding vertex
+ * output by the decompressor. The per-vertex copy is needed since in
+ * Java 3D a normal is always associated with a vertex. This method is
+ * never called if normals are bundled with each vertex in the compressed
+ * buffer.
+ */
+ void outputNormal(Vector3f normal) {
+ if (debug) System.out.println(" outputNormal: " + normal.toString()) ;
+
+ if ((vlist.vertexFormat & GeometryArray.NORMALS) == 0) {
+ if (vlist.size() > 0)
+ // Construct Shape3D using the current vertex format.
+ addShape3D() ;
+
+ // Start a new vertex list with the new format.
+ vlist = new GeneralizedVertexList
+ (vlist.vertexFormat|GeometryArray.NORMALS, FRONTFACE_CCW) ;
+ }
+ if (curNormal == null) curNormal = new Vector3f() ;
+ curNormal.set(normal) ;
+ }
+
+ /**
+ * Create a Shape3D object of the desired type from the current vertex
+ * list. Apply the current color, if non-null, as a Material attribute.
+ */
+ private void addShape3D() {
+ Material m = new Material() ;
+
+ if (curColor != null) {
+ if ((vlist.vertexFormat & GeometryArray.COLOR_4) != GeometryArray.COLOR_4) {
+ m.setAmbientColor(curColor.x, curColor.y, curColor.z) ;
+ m.setDiffuseColor(curColor.x, curColor.y, curColor.z) ;
+ }
+ else {
+ m.setAmbientColor(curColor.x, curColor.y, curColor.z) ;
+ m.setDiffuseColor(curColor.x, curColor.y, curColor.z,
+ curColor.w) ;
+ }
+ }
+
+ if ((vlist.vertexFormat & GeometryArray.NORMALS) == 0)
+ m.setLightingEnable(false) ;
+ else
+ m.setLightingEnable(true) ;
+
+ Appearance a = new Appearance() ;
+ a.setMaterial(m) ;
+
+ switch(bufferDataType) {
+ case TYPE_TRIANGLE:
+ switch(triOutputType) {
+ case TRI_SET:
+ TriangleArray ta = vlist.toTriangleArray() ;
+ if (ta != null)
+ shapes.add(new Shape3D(ta, a)) ;
+ break ;
+ case TRI_STRIP_SET:
+ TriangleStripArray tsa = vlist.toTriangleStripArray() ;
+ if (tsa != null)
+ shapes.add(new Shape3D(tsa, a)) ;
+ break ;
+ case TRI_STRIP_AND_FAN_SET:
+ GeometryStripArray gsa[] = vlist.toStripAndFanArrays() ;
+ if (gsa[0] != null)
+ shapes.add(new Shape3D(gsa[0], a)) ;
+ if (gsa[1] != null)
+ shapes.add(new Shape3D(gsa[1], a)) ;
+ break ;
+ case TRI_STRIP_AND_TRI_SET:
+ GeometryArray ga[] = vlist.toStripAndTriangleArrays() ;
+ if (ga[0] != null)
+ shapes.add(new Shape3D(ga[0], a)) ;
+ if (ga[1] != null)
+ shapes.add(new Shape3D(ga[1], a)) ;
+ break ;
+ default:
+ throw new IllegalArgumentException
+ (J3dUtilsI18N.getString("GeometryDecompressorShape3D0")) ;
+ }
+ break ;
+
+ case TYPE_LINE:
+ LineStripArray lsa = vlist.toLineStripArray() ;
+ if (lsa != null)
+ shapes.add(new Shape3D(lsa, a)) ;
+ break ;
+
+ case TYPE_POINT:
+ PointArray pa = vlist.toPointArray() ;
+ if (pa != null)
+ shapes.add(new Shape3D(pa, a)) ;
+ break ;
+
+ default:
+ throw new IllegalArgumentException
+ (J3dUtilsI18N.getString("GeometryDecompressorShape3D1")) ;
+ }
+
+ if (benchmark || statistics) {
+ origVertexCount += vlist.size() ;
+ vertexCount += vlist.vertexCount ;
+ stripCount += vlist.stripCount ;
+ triangleCount += vlist.triangleCount ;
+ }
+ }
+
+ private void beginPrint() {
+ System.out.println("\nGeometryDecompressorShape3D") ;
+
+ switch(bufferDataType) {
+ case TYPE_TRIANGLE:
+ System.out.println(" buffer TYPE_TRIANGLE") ;
+ break ;
+ case TYPE_LINE:
+ System.out.println(" buffer TYPE_LINE") ;
+ break ;
+ case TYPE_POINT:
+ System.out.println(" buffer TYPE_POINT") ;
+ break ;
+ default:
+ throw new IllegalArgumentException
+ (J3dUtilsI18N.getString("GeometryDecompressorShape3D1")) ;
+ }
+
+ System.out.print(" buffer data present: coords") ;
+
+ if ((dataPresent & CompressedGeometryData.Header.NORMAL_IN_BUFFER) != 0)
+ System.out.print(" normals") ;
+ if ((dataPresent & CompressedGeometryData.Header.COLOR_IN_BUFFER) != 0)
+ System.out.print(" colors") ;
+ if ((dataPresent & CompressedGeometryData.Header.ALPHA_IN_BUFFER) != 0)
+ System.out.print(" alpha") ;
+
+ System.out.println() ;
+
+ stripCount = 0 ;
+ vertexCount = 0 ;
+ triangleCount = 0 ;
+ origVertexCount = 0 ;
+
+ startTime = System.currentTimeMillis() ;
+ }
+
+ private void endPrint() {
+ endTime = System.currentTimeMillis() ;
+
+ if (benchmark || statistics)
+ printBench() ;
+
+ if (statistics)
+ printStats() ;
+ }
+
+ private void printBench() {
+ float t = (endTime - startTime) / 1000.0f ;
+ System.out.println
+ (" decompression + strip conversion took " + t + " sec.") ;
+
+ switch(bufferDataType) {
+ case TYPE_POINT:
+ System.out.println
+ (" points decompressed: " + vertexCount + "\n" +
+ " net decompression rate: " + (vertexCount/t) +
+ " points/sec.\n") ;
+ break ;
+ case TYPE_LINE:
+ System.out.println
+ (" lines decompressed: " + (vertexCount - stripCount) + "\n" +
+ " net decompression rate: " + ((vertexCount - stripCount)/t) +
+ " lines/sec.\n") ;
+ break ;
+ case TYPE_TRIANGLE:
+ System.out.println
+ (" triangles decompressed: " +
+ (vertexCount - 2*stripCount) + "\n" +
+ " net decompression rate: " +
+ ((vertexCount - 2*stripCount)/t) + " triangles/sec.\n") ;
+ break ;
+ }
+ }
+
+ private void printStats() {
+ switch(triOutputType) {
+ case TRI_SET:
+ System.out.println(" using individual triangle output") ;
+ break ;
+ case TRI_STRIP_SET:
+ System.out.println(" using strip output") ;
+ break ;
+ case TRI_STRIP_AND_FAN_SET:
+ System.out.println(" using strips and fans for output") ;
+ break ;
+ case TRI_STRIP_AND_TRI_SET:
+ System.out.println(" using strips and triangles for output") ;
+ break ;
+ }
+
+ System.out.print
+ (" number of Shape3D objects: " + shapes.size() +
+ "\n number of Shape3D decompressed vertices: ") ;
+
+ if (triOutputType == TRI_SET || bufferDataType == TYPE_POINT) {
+ System.out.println(vertexCount) ;
+ }
+ else if (triOutputType == TRI_STRIP_AND_TRI_SET) {
+ System.out.println((vertexCount + triangleCount*3) +
+ "\n number of strips: " + stripCount +
+ "\n number of individual triangles: " +
+ triangleCount) ;
+ if (stripCount > 0)
+ System.out.println
+ (" vertices/strip: " + (float)vertexCount/stripCount +
+ "\n triangles represented in strips: " +
+ (vertexCount - 2*stripCount)) ;
+ }
+ else {
+ System.out.println(vertexCount +
+ "\n number of strips: " + stripCount) ;
+ if (stripCount > 0)
+ System.out.println
+ (" vertices/strip: " + (float)vertexCount/stripCount) ;
+ }
+
+ System.out.print(" vertex data present in last Shape3D: coords") ;
+ if ((vlist.vertexFormat & GeometryArray.NORMALS) != 0)
+ System.out.print(" normals") ;
+
+ boolean color4 =
+ (vlist.vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4;
+ boolean color3 = !color4 &&
+ (vlist.vertexFormat & GeometryArray.COLOR_3) == GeometryArray.COLOR_3;
+ if (color3 || color4) {
+ System.out.print(" colors") ;
+ if (color4)
+ System.out.print(" alpha") ;
+ }
+ System.out.println() ;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/HuffmanNode.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/HuffmanNode.java
new file mode 100644
index 0000000..c04fa7e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/HuffmanNode.java
@@ -0,0 +1,225 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import java.util.Collection;
+import java.util.Comparator;
+
+/**
+ * Instances of this class are used as the nodes of binary trees representing
+ * mappings of tags to compression stream elements. Tags are descriptors
+ * inserted into the compression command stream that specify the encoding of
+ * immediately succeeding data elements.<p>
+ *
+ * The tag assignments in such a tree are computed from the paths taken from
+ * the root to the leaf nodes. Each leaf node represents the particular way
+ * one or more compression stream elements wound up being encoded with respect
+ * to various combinations of data lengths, shifts, and absolute/relative
+ * status.<p>
+ *
+ * Huffman's algorithm for constructing binary trees with minimal weighted
+ * path lengths can be used to optimize the bit lengths of the tags with
+ * respect to the frequency of occurrence of their associated data encodings
+ * in the compression stream. The weighted path length is the sum of the
+ * frequencies of all the leaf nodes times their path lengths to the root of
+ * the tree.<p>
+ *
+ * The length of the longest tag determines the size of the table mapping tags
+ * to data representations. The geometry compression specification limits the
+ * size of the table to 64 entries, so tags cannot be longer than 6 bits. The
+ * depth of the tree is reduced through a process of increasing the data
+ * lengths of less frequently occuring nodes so they can be merged with other
+ * more frequent nodes.
+ */
+class HuffmanNode {
+ int tag, tagLength ;
+ int shift, dataLength ;
+ boolean absolute ;
+
+ private int frequency ;
+ private HuffmanNode child0, child1, mergeNode ;
+ private boolean merged, unmergeable, cleared ;
+
+ void clear() {
+ tag = -1 ;
+ tagLength = -1 ;
+
+ shift = -1 ;
+ dataLength = -1 ;
+ absolute = false ;
+
+ child0 = null ;
+ child1 = null ;
+ mergeNode = null ;
+
+ frequency = 0 ;
+ merged = false ;
+ unmergeable = false ;
+ cleared = true ;
+ }
+
+ HuffmanNode() {
+ clear() ;
+ }
+
+ HuffmanNode(int length, int shift, boolean absolute) {
+ this() ;
+ set(length, shift, absolute) ;
+ }
+
+ final void set(int length, int shift, boolean absolute) {
+ this.dataLength = length ;
+ this.shift = shift ;
+ this.absolute = absolute ;
+ this.cleared = false ;
+ }
+
+ final boolean cleared() {
+ return cleared ;
+ }
+
+ final void addCount() {
+ frequency++ ;
+ }
+
+ final boolean hasCount() {
+ return frequency > 0 ;
+ }
+
+ final boolean tokenEquals(HuffmanNode node) {
+ return
+ this.absolute == node.absolute &&
+ this.dataLength == node.dataLength &&
+ this.shift == node.shift ;
+ }
+
+ void addChildren(HuffmanNode child0, HuffmanNode child1) {
+ this.child0 = child0 ;
+ this.child1 = child1 ;
+ this.frequency = child0.frequency + child1.frequency ;
+ }
+
+ void collectLeaves(int tag, int tagLength, Collection collection) {
+ if (child0 == null) {
+ this.tag = tag ;
+ this.tagLength = tagLength ;
+ collection.add(this) ;
+ } else {
+ child0.collectLeaves((tag << 1) | 0, tagLength + 1, collection) ;
+ child1.collectLeaves((tag << 1) | 1, tagLength + 1, collection) ;
+ }
+ }
+
+ boolean mergeInto(HuffmanNode node) {
+ if (this.absolute == node.absolute) {
+ if (this.dataLength > node.dataLength)
+ node.dataLength = this.dataLength ;
+
+ if (this.shift < node.shift)
+ node.shift = this.shift ;
+
+ node.frequency += this.frequency ;
+ this.mergeNode = node ;
+ this.merged = true ;
+ return true ;
+
+ } else
+ return false ;
+ }
+
+ int incrementLength() {
+ if (shift > 0)
+ shift-- ;
+ else
+ dataLength++ ;
+
+ return dataLength - shift ;
+ }
+
+ final boolean merged() {
+ return merged ;
+ }
+
+ final HuffmanNode getMergeNode() {
+ return mergeNode ;
+ }
+
+ void setUnmergeable() {
+ unmergeable = true ;
+ }
+
+ final boolean unmergeable() {
+ return unmergeable ;
+ }
+
+ public String toString() {
+ return
+ "shift " + shift + " data length " + dataLength +
+ (absolute? " absolute " : " relative ") +
+ "\ntag 0x" + Integer.toHexString(tag) + " tag length " + tagLength +
+ "\nfrequency: " + frequency ;
+ }
+
+ /**
+ * Sorts nodes in ascending order by frequency.
+ */
+ static class FrequencyComparator implements Comparator {
+ public final int compare(Object o1, Object o2) {
+ return ((HuffmanNode)o1).frequency - ((HuffmanNode)o2).frequency ;
+ }
+ }
+
+ /**
+ * Sorts nodes in descending order by tag bit length.
+ */
+ static class TagLengthComparator implements Comparator {
+ public final int compare(Object o1, Object o2) {
+ return ((HuffmanNode)o2).tagLength - ((HuffmanNode)o1).tagLength ;
+ }
+ }
+
+ static FrequencyComparator frequencyComparator = new FrequencyComparator() ;
+ static TagLengthComparator tagLengthComparator = new TagLengthComparator() ;
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/HuffmanTable.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/HuffmanTable.java
new file mode 100644
index 0000000..595a1ea
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/HuffmanTable.java
@@ -0,0 +1,483 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+/**
+ * This class maintains a map from compression stream elements (tokens) onto
+ * HuffmanNode objects. A HuffmanNode contains a tag describing the
+ * associated token's data length, right shift value, and absolute/relative
+ * status.<p>
+ *
+ * The tags are computed using Huffman's algorithm to build a binary tree with
+ * a minimal total weighted path length. The frequency of each token is
+ * used as its node's weight when building the tree. The path length from the
+ * root to the token's node then indicates the bit length that should be used
+ * for that token's tag in order to minimize the total size of the compressed
+ * stream.
+ */
+class HuffmanTable {
+ private static final int MAX_TAG_LENGTH = 6 ;
+
+ private HuffmanNode positions[] ;
+ private HuffmanNode normals[] ;
+ private HuffmanNode colors[] ;
+
+ /**
+ * Create a new HuffmanTable with entries for all possible position,
+ * normal, and color tokens.
+ */
+ HuffmanTable() {
+ //
+ // Position and color components can have data lengths up to 16
+ // bits, with right shifts up to 15 bits. The position and color
+ // lookup tables are therefore 2*17*16=544 entries in length to
+ // account for all possible combinations of data lengths, shifts,
+ // and relative or absolute status.
+ //
+ colors = new HuffmanNode[544] ;
+ positions = new HuffmanNode[544] ;
+
+ //
+ // Delta normals can have uv components up to 7 bits in length with
+ // right shifts up to 6 bits. Absolute normals can have uv components
+ // up to 6 bits in length with right shifts up to 5 bits. The normal
+ // lookup table is therefore 2*8*7=112 entries in length.
+ //
+ normals = new HuffmanNode[112] ;
+ }
+
+ private final int getPositionIndex(int len, int shift, boolean absolute) {
+ return (absolute? 1:0)*272 + len*16 + shift ;
+ }
+
+ private final int getNormalIndex(int length, int shift, boolean absolute) {
+ return (absolute? 1:0)*56 + length*7 + shift ;
+ }
+
+ private final int getColorIndex(int length, int shift, boolean absolute) {
+ return getPositionIndex(length, shift, absolute) ;
+ }
+
+
+ /**
+ * Add a position entry with the given length, shift, and absolute
+ * status.
+ *
+ * @param length number of bits in each X, Y, and Z component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous vertex in the compression stream
+ */
+ void addPositionEntry(int length, int shift, boolean absolute) {
+ addEntry(positions, getPositionIndex(length, shift, absolute),
+ length, shift, absolute) ;
+ }
+
+ /**
+ * Get the position entry associated with the specified length, shift, and
+ * absolute status. This will contain a tag indicating the actual
+ * encoding to be used in the compression command stream, not necessarily
+ * the same as the original length and shift with which the the entry was
+ * created.
+ *
+ * @param length number of bits in each X, Y, and Z component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous vertex in the compression stream
+ * @return HuffmanNode mapped to the specified parameters
+ */
+ HuffmanNode getPositionEntry(int length, int shift, boolean absolute) {
+ return getEntry(positions, getPositionIndex(length, shift, absolute)) ;
+ }
+
+ /**
+ * Add a color entry with the given length, shift, and absolute
+ * status.
+ *
+ * @param length number of bits in each R, G, B, and A component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous color in the compression stream
+ */
+ void addColorEntry(int length, int shift, boolean absolute) {
+ addEntry(colors, getColorIndex(length, shift, absolute),
+ length, shift, absolute) ;
+ }
+
+ /**
+ * Get the color entry associated with the specified length, shift, and
+ * absolute status. This will contain a tag indicating the actual
+ * encoding to be used in the compression command stream, not necessarily
+ * the same as the original length and shift with which the the entry was
+ * created.
+ *
+ * @param length number of bits in each R, G, B, and A component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous color in the compression stream
+ * @return HuffmanNode mapped to the specified parameters
+ */
+ HuffmanNode getColorEntry(int length, int shift, boolean absolute) {
+ return getEntry(colors, getColorIndex(length, shift, absolute)) ;
+ }
+
+ /**
+ * Add a normal entry with the given length, shift, and absolute
+ * status.
+ *
+ * @param length number of bits in each U and V component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous normal in the compression stream
+ */
+ void addNormalEntry(int length, int shift, boolean absolute) {
+ addEntry(normals, getNormalIndex(length, shift, absolute),
+ length, shift, absolute) ;
+ }
+
+ /**
+ * Get the normal entry associated with the specified length, shift, and
+ * absolute status. This will contain a tag indicating the actual
+ * encoding to be used in the compression command stream, not necessarily
+ * the same as the original length and shift with which the the entry was
+ * created.
+ *
+ * @param length number of bits in each U and V component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous normal in the compression stream
+ * @return HuffmanNode mapped to the specified parameters
+ */
+ HuffmanNode getNormalEntry(int length, int shift, boolean absolute) {
+ return getEntry(normals, getNormalIndex(length, shift, absolute)) ;
+ }
+
+
+ private void addEntry(HuffmanNode table[], int index,
+ int length, int shift, boolean absolute) {
+
+ if (table[index] == null)
+ table[index] = new HuffmanNode(length, shift, absolute) ;
+
+ else if (table[index].cleared())
+ table[index].set(length, shift, absolute) ;
+
+ table[index].addCount() ;
+ }
+
+ private HuffmanNode getEntry(HuffmanNode table[], int index) {
+ HuffmanNode t = table[index] ;
+
+ while (t.merged())
+ t = t.getMergeNode() ;
+
+ return t ;
+ }
+
+ private void getEntries(HuffmanNode table[], Collection c) {
+ for (int i = 0 ; i < table.length ; i++)
+ if (table[i] != null && !table[i].cleared() &&
+ table[i].hasCount() && !table[i].merged())
+ c.add(table[i]) ;
+ }
+
+
+ /**
+ * Clear this HuffmanTable instance.
+ */
+ void clear() {
+ for (int i = 0 ; i < positions.length ; i++)
+ if (positions[i] != null)
+ positions[i].clear() ;
+
+ for (int i = 0 ; i < colors.length ; i++)
+ if (colors[i] != null)
+ colors[i].clear() ;
+
+ for (int i = 0 ; i < normals.length ; i++)
+ if (normals[i] != null)
+ normals[i].clear() ;
+ }
+
+ /**
+ * Compute optimized tags for each position, color, and normal entry.
+ */
+ void computeTags() {
+ LinkedList nodeList = new LinkedList() ;
+ getEntries(positions, nodeList) ;
+ computeTags(nodeList, 3) ;
+
+ nodeList.clear() ;
+ getEntries(colors, nodeList) ;
+ computeTags(nodeList, 3) ;
+
+ nodeList.clear() ;
+ getEntries(normals, nodeList) ;
+ computeTags(nodeList, 2) ;
+ }
+
+ //
+ // Compute tags for a list of Huffman tokens.
+ //
+ private void computeTags(LinkedList nodes, int minComponentCount) {
+ HuffmanNode node0, node1, node2 ;
+
+ // Return if there's nothing to do.
+ if (nodes.isEmpty())
+ return ;
+
+ while (true) {
+ // Sort the nodes in ascending order by frequency.
+ Collections.sort(nodes, HuffmanNode.frequencyComparator) ;
+
+ // Apply Huffman's algorithm to construct a binary tree with a
+ // minimum total weighted path length.
+ node0 = (HuffmanNode)nodes.removeFirst() ;
+ while (nodes.size() > 0) {
+ node1 = (HuffmanNode)nodes.removeFirst() ;
+ node2 = new HuffmanNode() ;
+
+ node2.addChildren(node0, node1) ;
+ addNodeInOrder(nodes, node2, HuffmanNode.frequencyComparator) ;
+
+ node0 = (HuffmanNode)nodes.removeFirst() ;
+ }
+
+ // node0 is the root of the resulting binary tree. Traverse it
+ // assigning tags and lengths to the leaf nodes. The leaves are
+ // collected into the now empty node list.
+ node0.collectLeaves(0, 0, nodes) ;
+
+ // Sort the nodes in descending order by tag length.
+ Collections.sort(nodes, HuffmanNode.tagLengthComparator) ;
+
+ // Check for tag length overrun.
+ if (((HuffmanNode)nodes.getFirst()).tagLength > MAX_TAG_LENGTH) {
+ // Tokens need to be merged and the tree rebuilt with the new
+ // combined frequencies.
+ merge(nodes) ;
+
+ } else {
+ // Increase tag length + data length if they're too small.
+ expand(nodes, minComponentCount) ;
+ break ;
+ }
+ }
+ }
+
+ //
+ // Merge a token with a long tag into some other token. The merged token
+ // will be removed from the list along with any duplicate node the merge
+ // created, reducing the size of the list by 1 or 2 elements until only
+ // unmergeable tokens are left.
+ //
+ private void merge(LinkedList nodes) {
+ ListIterator i = nodes.listIterator(0) ;
+ HuffmanNode node0, node1, node2 ;
+ int index = 0 ;
+
+ while (i.hasNext()) {
+ // Get the node with the longest possibly mergeable tag.
+ node0 = (HuffmanNode)i.next() ;
+ if (node0.unmergeable()) continue ;
+
+ // Try to find a node that can be merged with node0. This is any
+ // node that matches its absolute/relative status.
+ i.remove() ;
+ while (i.hasNext()) {
+ node1 = (HuffmanNode)i.next() ;
+ if (node0.mergeInto(node1)) {
+ // Search for a duplicate of the possibly modified node1
+ // and merge into it so that node weights remain valid.
+ // If a duplicate exists it must be further in the list,
+ // otherwise node0 would have merged into it.
+ i.remove() ;
+ while (i.hasNext()) {
+ node2 = (HuffmanNode)i.next() ;
+ if (node1.tokenEquals(node2)) {
+ node1.mergeInto(node2) ;
+ return ;
+ }
+ }
+ // node1 has no duplicate, so return it to the list.
+ i.add(node1) ;
+ return ;
+ }
+ }
+
+ // node0 can't be merged with any other node; it must be the only
+ // relative or absolute node in the list. Mark it as unmergeable
+ // to avoid unnecessary searches on subsequent calls to merge()
+ // and return it to the list.
+ node0.setUnmergeable() ;
+ i.add(node0) ;
+
+ // Restart the iteration.
+ i = nodes.listIterator(0) ;
+ }
+ }
+
+ //
+ // Empty bits within a compression command header are not allowed. If
+ // the tag length plus the total data length is less than 6 bits then
+ // the token's length must be increased.
+ //
+ private void expand(LinkedList nodes, int minComponentCount) {
+ Iterator i = nodes.iterator() ;
+
+ while (i.hasNext()) {
+ HuffmanNode n = (HuffmanNode)i.next() ;
+
+ while (n.tagLength +
+ (minComponentCount * (n.dataLength - n.shift)) < 6) {
+
+ n.incrementLength() ;
+ }
+ }
+ }
+
+ //
+ // Insert a node into the correct place in a sorted list of nodes.
+ //
+ private void addNodeInOrder(LinkedList l, HuffmanNode node, Comparator c) {
+ ListIterator i = l.listIterator(0) ;
+
+ while (i.hasNext()) {
+ HuffmanNode n = (HuffmanNode)i.next() ;
+ if (c.compare(n, node) > 0) {
+ n = (HuffmanNode)i.previous() ;
+ break ;
+ }
+ }
+ i.add(node) ;
+ }
+
+ /**
+ * Create compression stream commands for decompressors to use to set up
+ * their decompression tables.
+ *
+ * @param output CommandStream which receives the compression commands
+ */
+ void outputCommands(CommandStream output) {
+ LinkedList nodeList = new LinkedList() ;
+ getEntries(positions, nodeList) ;
+ outputCommands(nodeList, output, CommandStream.POSITION_TABLE) ;
+
+ nodeList.clear() ;
+ getEntries(colors, nodeList) ;
+ outputCommands(nodeList, output, CommandStream.COLOR_TABLE) ;
+
+ nodeList.clear() ;
+ getEntries(normals, nodeList) ;
+ outputCommands(nodeList, output, CommandStream.NORMAL_TABLE) ;
+ }
+
+ //
+ // Output a setTable command for each unique token.
+ //
+ private void outputCommands(Collection nodes,
+ CommandStream output, int tableId) {
+
+ Iterator i = nodes.iterator() ;
+ while (i.hasNext()) {
+ HuffmanNode n = (HuffmanNode)i.next() ;
+ int addressRange = (1 << n.tagLength) | n.tag ;
+ int dataLength = (n.dataLength == 16? 0 : n.dataLength) ;
+
+ int command =
+ CommandStream.SET_TABLE | (tableId << 1) | (addressRange >> 6) ;
+
+ long body =
+ ((addressRange & 0x3f) << 9) | (dataLength << 5) |
+ (n.absolute? 0x10 : 0) | n.shift ;
+
+ output.addCommand(command, 8, body, 15) ;
+ }
+ }
+
+ /**
+ * Print a collection of HuffmanNode objects to standard out.
+ *
+ * @param header descriptive string
+ * @param nodes Collection of HuffmanNode objects to print
+ */
+ void print(String header, Collection nodes) {
+ System.out.println(header + "\nentries: " + nodes.size() + "\n") ;
+
+ Iterator i = nodes.iterator() ;
+ while(i.hasNext()) {
+ HuffmanNode n = (HuffmanNode)i.next() ;
+ System.out.println(n.toString() + "\n") ;
+ }
+ }
+
+ /**
+ * Print the contents of this instance to standard out.
+ */
+ void print() {
+ LinkedList nodeList = new LinkedList() ;
+
+ getEntries(positions, nodeList) ;
+ Collections.sort(nodeList, HuffmanNode.frequencyComparator) ;
+ print("\nposition tokens and tags", nodeList) ;
+
+ nodeList.clear() ;
+ getEntries(colors, nodeList) ;
+ Collections.sort(nodeList, HuffmanNode.frequencyComparator) ;
+ print("\ncolor tokens and tags", nodeList) ;
+
+ nodeList.clear() ;
+ getEntries(normals, nodeList) ;
+ Collections.sort(nodeList, HuffmanNode.frequencyComparator) ;
+ print("\nnormal tokens and tags", nodeList) ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/MeshBuffer.java b/src/classes/share/com/sun/j3d/utils/geometry/compression/MeshBuffer.java
new file mode 100644
index 0000000..6b15dce
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/MeshBuffer.java
@@ -0,0 +1,242 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2006 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.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry.compression;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Color4f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+
+/**
+ * This class mirrors the vertex mesh buffer stack supported by the geometry
+ * compression semantics.
+ */
+class MeshBuffer {
+ //
+ // The fixed-length mesh buffer stack is represented by circular buffers.
+ // Three stack representations are provided: vertices, positions, and
+ // indices.
+ //
+ // The vertex representation stores references to CompressionStreamVertex
+ // objects. The position representation stores references to Point3f,
+ // Vector3f, Color3f, and Color4f objects, while the index representation
+ // stores indices into externally maintained arrays of those objects. All
+ // these representations may be used independently and all provide access
+ // to the stored references via a mesh buffer index.
+ //
+ // In addition, the position and index representations provide lookup
+ // mechanisms to check if positions or indices exist in the mesh buffer
+ // and return their mesh buffer indices if they do. This is used to
+ // implement a limited meshing algorithm which reduces the number of
+ // vertices when non-stripped abutting facets are added to a compression
+ // stream.
+ //
+ static final int NOT_FOUND = -1 ;
+
+ private static final int SIZE = 16 ;
+ private static final int NAN_HASH =
+ new Point3f(Float.NaN, Float.NaN, Float.NaN).hashCode() ;
+
+ private int topIndex = SIZE - 1 ;
+ private int positionIndices[] = new int[SIZE] ;
+ private int normalIndices[] = new int[SIZE] ;
+ private int colorIndices[] = new int[SIZE] ;
+
+ private int topPosition = SIZE - 1 ;
+ private int positionHashCodes[] = new int[SIZE] ;
+ private Point3f positions[] = new Point3f[SIZE] ;
+ private Vector3f normals[] = new Vector3f[SIZE] ;
+ private Color3f colors3[] = new Color3f[SIZE] ;
+ private Color4f colors4[] = new Color4f[SIZE] ;
+
+ private int topVertex = SIZE - 1 ;
+ private CompressionStreamVertex vertices[] =
+ new CompressionStreamVertex[SIZE] ;
+
+ MeshBuffer() {
+ for (int i = 0 ; i < SIZE ; i++) {
+ positionHashCodes[i] = NAN_HASH ;
+
+ positionIndices[i] = NOT_FOUND ;
+ normalIndices[i] = NOT_FOUND ;
+ colorIndices[i] = NOT_FOUND ;
+ }
+ }
+
+ private static int nextTop(int top) {
+ // The stack top references an element in the fixed-length backing
+ // array in which the stack is stored. Stack elements below it have
+ // decreasing indices into the backing array until element 0, at which
+ // point the indices wrap to the end of the backing array and back to
+ // the top.
+ //
+ // A push is accomplished by incrementing the stack top in a circular
+ // buffer and storing the data into the new stack element it
+ // references. The bottom of the stack is the element with the next
+ // higher index from the top in the backing array, and is overwritten
+ // with each new push.
+ return (top + 1) % SIZE ;
+ }
+
+ private static int flipOffset(int top, int offset) {
+ // Flips an offset relative to the beginning of the backing array to
+ // an offset from the top of the stack. Also works in reverse, from
+ // an offset from the top of the stack to an offset from the beginning
+ // of the backing array.
+ if (offset > top) offset -= SIZE ;
+ return top - offset ;
+ }
+
+ //
+ // Mesh buffer vertex stack. This is currently only used for vertex
+ // lookup during the quantization pass in order to compute delta values;
+ // no mesh reference lookup is necessary.
+ //
+ void push(CompressionStreamVertex v) {
+ topVertex = nextTop(topVertex) ;
+ vertices[topVertex] = v ;
+ }
+
+ CompressionStreamVertex getVertex(int meshReference) {
+ return vertices[flipOffset(topVertex, meshReference)] ;
+ }
+
+
+ //
+ // Mesh buffer index stack and index reference lookup support.
+ //
+ void push(int positionIndex, int normalIndex) {
+ topIndex = nextTop(topIndex) ;
+
+ positionIndices[topIndex] = positionIndex ;
+ normalIndices[topIndex] = normalIndex ;
+ }
+
+ void push(int positionIndex, int colorIndex, int normalIndex) {
+ push(positionIndex, normalIndex) ;
+ colorIndices[topIndex] = colorIndex ;
+ }
+
+ int getMeshReference(int positionIndex) {
+ int index ;
+ for (index = 0 ; index < SIZE ; index++)
+ if (positionIndices[index] == positionIndex)
+ break ;
+
+ if (index == SIZE) return NOT_FOUND ;
+ return flipOffset(topIndex, index) ;
+ }
+
+ int getPositionIndex(int meshReference) {
+ return positionIndices[flipOffset(topIndex, meshReference)] ;
+ }
+
+ int getColorIndex(int meshReference) {
+ return colorIndices[flipOffset(topIndex, meshReference)] ;
+ }
+
+ int getNormalIndex(int meshReference) {
+ return normalIndices[flipOffset(topIndex, meshReference)] ;
+ }
+
+
+ //
+ // Mesh buffer position stack and position reference lookup support.
+ //
+ void push(Point3f position, Vector3f normal) {
+ topPosition = nextTop(topPosition) ;
+
+ positionHashCodes[topPosition] = position.hashCode() ;
+ positions[topPosition] = position ;
+ normals[topPosition] = normal ;
+ }
+
+ void push(Point3f position, Color3f color, Vector3f normal) {
+ push(position, normal) ;
+ colors3[topPosition] = color ;
+ }
+
+ void push(Point3f position, Color4f color, Vector3f normal) {
+ push(position, normal) ;
+ colors4[topPosition] = color ;
+ }
+
+ void push(Point3f position, Object color, Vector3f normal) {
+ push(position, normal) ;
+ if (color instanceof Color3f)
+ colors3[topPosition] = (Color3f)color ;
+ else
+ colors4[topPosition] = (Color4f)color ;
+ }
+
+ int getMeshReference(Point3f position) {
+ int index ;
+ int hashCode = position.hashCode() ;
+
+ for (index = 0 ; index < SIZE ; index++)
+ if (positionHashCodes[index] == hashCode)
+ if (positions[index].equals(position))
+ break ;
+
+ if (index == SIZE) return NOT_FOUND ;
+ return flipOffset(topPosition, index) ;
+ }
+
+ Point3f getPosition(int meshReference) {
+ return positions[flipOffset(topPosition, meshReference)] ;
+ }
+
+ Color3f getColor3(int meshReference) {
+ return colors3[flipOffset(topPosition, meshReference)] ;
+ }
+
+ Color4f getColor4(int meshReference) {
+ return colors4[flipOffset(topPosition, meshReference)] ;
+ }
+
+ Vector3f getNormal(int meshReference) {
+ return normals[flipOffset(topPosition, meshReference)] ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/compression/package.html b/src/classes/share/com/sun/j3d/utils/geometry/compression/package.html
new file mode 100644
index 0000000..2977b07
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/compression/package.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+ <title>com.sun.j3d.utils.geometry.compression</title>
+</head>
+<body>
+<p>Provides compressed geometry utility classes.
+ This package supersedes the javax.media.j3d.CompressedGeometry class and
+ the com.sun.j3d.utils.compression package.</p>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/image/TextureLoader.java b/src/classes/share/com/sun/j3d/utils/image/TextureLoader.java
index 15dc01e..8fae388 100644
--- a/src/classes/share/com/sun/j3d/utils/image/TextureLoader.java
+++ b/src/classes/share/com/sun/j3d/utils/image/TextureLoader.java
@@ -73,7 +73,7 @@ public class TextureLoader extends Object {
/**
* Optional flag - specifies that mipmaps are generated for all levels
- **/
+ */
public static final int GENERATE_MIPMAP = 0x01;
/**
@@ -81,7 +81,7 @@ public class TextureLoader extends Object {
* access the image data by reference
*
* @since Java 3D 1.2
- **/
+ */
public static final int BY_REFERENCE = 0x02;
/**
@@ -90,10 +90,27 @@ public class TextureLoader extends Object {
* lower left
*
* @since Java 3D 1.2
- **/
+ */
public static final int Y_UP = 0x04;
/**
+ * Optional flag - specifies that the ImageComponent2D is allowed
+ * to have dimensions that are not a power of two. If this flag is set,
+ * TextureLoader will not perform any scaling of images. If this flag
+ * is not set, images will be scaled to the nearest power of two. This is
+ * the default mode.
+ * <p>
+ * Note that non-power-of-two textures may not be supported by all graphics
+ * cards. Applications should check whether a particular Canvas3D supports
+ * non-power-of-two textures by calling the {@link Canvas3D#queryProperties}
+ * method, and checking whether the
+ * <code>textureNonPowerOfTwoAvailable</code> property is set to true.
+ *
+ * @since Java 3D 1.5
+ */
+ public static final int ALLOW_NON_POWER_OF_TWO = 0x08;
+
+ /*
* Private declaration for BufferedImage allocation
*/
private static ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
@@ -107,8 +124,9 @@ public class TextureLoader extends Object {
private int textureFormat = Texture.RGBA;
private int imageComponentFormat = ImageComponent.FORMAT_RGBA;
private int flags;
- private boolean byRef;
- private boolean yUp;
+ private boolean byRef = false;
+ private boolean yUp = false;
+ private boolean forcePowerOfTwo = true;
/**
* Contructs a TextureLoader object using the specified BufferedImage
@@ -157,6 +175,9 @@ public class TextureLoader extends Object {
if ((flags & Y_UP) != 0) {
yUp = true;
}
+ if ((flags & ALLOW_NON_POWER_OF_TWO) != 0) {
+ forcePowerOfTwo = false;
+ }
}
/**
@@ -216,6 +237,9 @@ public class TextureLoader extends Object {
if ((flags & Y_UP) != 0) {
yUp = true;
}
+ if ((flags & ALLOW_NON_POWER_OF_TWO) != 0) {
+ forcePowerOfTwo = false;
+ }
}
/**
@@ -293,6 +317,9 @@ public class TextureLoader extends Object {
if ((flags & Y_UP) != 0) {
yUp = true;
}
+ if ((flags & ALLOW_NON_POWER_OF_TWO) != 0) {
+ forcePowerOfTwo = false;
+ }
}
/**
@@ -369,6 +396,9 @@ public class TextureLoader extends Object {
if ((flags & Y_UP) != 0) {
yUp = true;
}
+ if ((flags & ALLOW_NON_POWER_OF_TWO) != 0) {
+ forcePowerOfTwo = false;
+ }
}
@@ -434,8 +464,16 @@ public class TextureLoader extends Object {
if (tex == null) {
if (bufferedImage==null) return null;
- int width = getClosestPowerOf2(bufferedImage.getWidth());
- int height = getClosestPowerOf2(bufferedImage.getHeight());
+ int width;
+ int height;
+
+ if (forcePowerOfTwo) {
+ width = getClosestPowerOf2(bufferedImage.getWidth());
+ height = getClosestPowerOf2(bufferedImage.getHeight());
+ } else {
+ width = bufferedImage.getWidth();
+ height = bufferedImage.getHeight();
+ }
if ((flags & GENERATE_MIPMAP) != 0) {
@@ -455,8 +493,17 @@ public class TextureLoader extends Object {
byRef, yUp);
tex.setImage(i, scaledImageComponents[i]);
- if (newW > 1) newW >>= 1;
- if (newH > 1) newH >>= 1;
+ if (forcePowerOfTwo) {
+ if (newW > 1) newW >>= 1;
+ if (newH > 1) newH >>= 1;
+ } else {
+ if (newW > 1) {
+ newW = (int) Math.floor(newW / 2.0);
+ }
+ if (newH > 1) {
+ newH = (int) Math.floor(newH / 2.0);
+ }
+ }
origImage = scaledBufferedImages[i];
}
diff --git a/src/classes/share/com/sun/j3d/utils/pickfast/PickTool.java b/src/classes/share/com/sun/j3d/utils/pickfast/PickTool.java
index 5fe3d08..9ae035b 100644
--- a/src/classes/share/com/sun/j3d/utils/pickfast/PickTool.java
+++ b/src/classes/share/com/sun/j3d/utils/pickfast/PickTool.java
@@ -261,7 +261,8 @@ public class PickTool {
}
/** Sets the PickInfo content flags. The default is PickInfo.NODE.
- * @param flags One of the following:
+ * @param flags specified as one or more individual bits that are
+ * bitwise "OR"ed together :
* <ul>
* <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>
* <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
@@ -381,8 +382,8 @@ public class PickTool {
}
/** Select one of the nodes that intersect the PickShape
- @return An array of <code>PickInfo</code> objects which will contain
- information about the picked instances. <code>null</code> if nothing
+ @return A <code>PickInfo</code> object which will contain
+ information about the picked instance. <code>null</code> if nothing
was picked.
*/
public PickInfo pickAny () {
@@ -417,8 +418,8 @@ public class PickTool {
intersects the PickShape. See note above to see how "closest" is
determined.
<p>
- @return An array of <code>PickInfo</code> objects which will contain
- information about the picked instances. <code>null</code> if nothing
+ @return A <code>PickInfo</code> object which will contain
+ information about the picked instance. <code>null</code> if nothing
was picked.
*/
public PickInfo pickClosest () {
diff --git a/src/classes/share/com/sun/j3d/utils/picking/PickResult.java b/src/classes/share/com/sun/j3d/utils/picking/PickResult.java
index e58b13e..541c244 100644
--- a/src/classes/share/com/sun/j3d/utils/picking/PickResult.java
+++ b/src/classes/share/com/sun/j3d/utils/picking/PickResult.java
@@ -268,41 +268,6 @@ public class PickResult {
}
}
- // similar to the constructor, resets the data in PickResult so it can be
- // reused via a freelist
- void reset(SceneGraphPath sgp, PickShape ps) {
- firstIntersectOnly = false;
- geometryArrays = null;
- compressGeomShape3Ds = null;
- pickShapeBounds = null;
- intersections = null;
- pickedSceneGraphPath = sgp;
- pickedNode = sgp.getObject();
- localToVWorld = sgp.getTransform();
- pickShape = ps;
- initPickShape();
- }
-
- // similar to the constructor, resets the data in PickResult so
- // it can be reused via a freelist
- void reset(Node pn, Transform3D l2vw, PickShape ps) {
- if ((pn instanceof Shape3D) || (pn instanceof Morph)) {
- firstIntersectOnly = false;
- geometryArrays = null;
- compressGeomShape3Ds = null;
- pickShapeBounds = null;
- intersections = null;
- pickedSceneGraphPath = null;
- pickedNode = pn;
- localToVWorld = l2vw;
- pickShape = ps;
- initPickShape();
- }
- else {
- throw new IllegalArgumentException();
- }
- }
-
void initPickShape() {
if(pickShape instanceof PickRay) {
if (pickShapeStart == null) pickShapeStart = new Point3d();
@@ -713,7 +678,7 @@ public class PickResult {
for (int i=0; i < numPts; i++) {
// Need to transform each pnt by localToVWorld.
- pnts[i] = getPoint3d();
+ pnts[i] = new Point3d();
pnts[i].x = doubleData[offset++];
pnts[i].y = doubleData[offset++];
pnts[i].z = doubleData[offset++];
@@ -726,7 +691,7 @@ public class PickResult {
for (int i=0; i < numPts; i++) {
// Need to transform each pnt by localToVWorld.
- pnts[i] = getPoint3d();
+ pnts[i] = new Point3d();
pnts[i].x = floatData[offset++];
pnts[i].y = floatData[offset++];
pnts[i].z = floatData[offset++];
@@ -738,7 +703,7 @@ public class PickResult {
for (int i=0; i < numPts; i++) {
// Need to transform each pnt by localToVWorld.
- pnts[i] = getPoint3d();
+ pnts[i] = new Point3d();
pnts[i].set(p3fData[i]);
localToVWorld.transform(pnts[i]);
}
@@ -747,7 +712,7 @@ public class PickResult {
for (int i=0; i < numPts; i++) {
// Need to transform each pnt by localToVWorld.
- pnts[i] = getPoint3d();
+ pnts[i] = new Point3d();
pnts[i].set(p3dData[i]);
localToVWorld.transform(pnts[i]);
}
@@ -774,7 +739,7 @@ public class PickResult {
for (int i=0; i < numPts; i++) {
// Need to transform each pnt by localToVWorld.
- pnts[i] = getPoint3d();
+ pnts[i] = new Point3d();
pnts[i].x = floatData[offset];
pnts[i].y = floatData[offset+1];
pnts[i].z = floatData[offset+2];
@@ -835,13 +800,6 @@ public class PickResult {
} else {
throw new RuntimeException ("incorrect class type");
}
-
- if(retFlag == false) {
- for (int i = 0; i < numPts; i++) {
- freePoint3d(pnts[i]);
- }
- }
-
return retFlag;
}
@@ -2503,13 +2461,11 @@ public class PickResult {
static boolean intersectRay(Point3d coordinates[],
PickRay ray, PickIntersection pi) {
- Point3d origin = getPoint3d();
- Vector3d direction = getVector3d();
+ Point3d origin = new Point3d();
+ Vector3d direction = new Vector3d();
boolean result;
ray.get (origin, direction);
result = intersectRayOrSegment(coordinates, direction, origin, pi, false);
- freeVector3d(direction);
- freePoint3d(origin);
return result;
}
@@ -2523,9 +2479,9 @@ public class PickResult {
Vector3d vec0, vec1, pNrm, tempV3d;
Point3d iPnt;
- vec0 = getVector3d();
- vec1 = getVector3d();
- pNrm = getVector3d();
+ vec0 = new Vector3d();
+ vec1 = new Vector3d();
+ pNrm = new Vector3d();
double absNrmX, absNrmY, absNrmZ, pD = 0.0;
double pNrmDotrDir = 0.0;
@@ -2574,11 +2530,6 @@ public class PickResult {
origin,
direction,
pi);
-
- // put the Vectors on the freelist
- freeVector3d(vec0);
- freeVector3d(vec1);
- freeVector3d(pNrm);
return isIntersect;
}
@@ -2606,14 +2557,11 @@ public class PickResult {
break;
}
}
- freeVector3d(vec0);
- freeVector3d(vec1);
- freeVector3d(pNrm);
return isIntersect;
}
// Plane equation: (p - p0)*pNrm = 0 or p*pNrm = pD;
- tempV3d = getVector3d();
+ tempV3d = new Vector3d();
tempV3d.set((Tuple3d) coordinates[0]);
pD = pNrm.dot(tempV3d);
tempV3d.set((Tuple3d) origin);
@@ -2629,16 +2577,12 @@ public class PickResult {
(isSegment && (dist > 1.0+EPS))) {
// Ray intersects the plane behind the ray's origin
// or intersect point not fall in Segment
- freeVector3d(vec0);
- freeVector3d(vec1);
- freeVector3d(pNrm);
- freeVector3d(tempV3d);
return false;
}
// Now, one thing for sure the ray intersect the plane.
// Find the intersection point.
- iPnt = getPoint3d();
+ iPnt = new Point3d();
iPnt.x = origin.x + direction.x * dist;
iPnt.y = origin.y + direction.y * dist;
iPnt.z = origin.z + direction.z * dist;
@@ -2792,11 +2736,6 @@ public class PickResult {
pi.setDistance(dist*direction.length());
pi.setPointCoordinatesVW(iPnt);
}
- freeVector3d(vec0);
- freeVector3d(vec1);
- freeVector3d(pNrm);
- freeVector3d(tempV3d);
- freePoint3d(iPnt);
return isIntersect;
}
@@ -2807,18 +2746,15 @@ public class PickResult {
*/
static boolean intersectSegment (Point3d coordinates[], PickSegment segment,
PickIntersection pi) {
- Point3d start = getPoint3d();
- Point3d end = getPoint3d();
- Vector3d direction = getVector3d();
+ Point3d start = new Point3d();
+ Point3d end = new Point3d();
+ Vector3d direction = new Vector3d();
boolean result;
segment.get(start, end);
direction.x = end.x - start.x;
direction.y = end.y - start.y;
direction.z = end.z - start.z;
result = intersectRayOrSegment(coordinates, direction, start, pi, true);
- freeVector3d(direction);
- freePoint3d(start);
- freePoint3d(end);
return result;
}
@@ -2991,7 +2927,6 @@ public class PickResult {
pi.setDistance(0);
}
}
- //freeVector3d(lDir);
return isIntersect;
}
// Find the inverse.
@@ -3043,12 +2978,12 @@ public class PickResult {
static boolean intersectCylinder (Point3d coordinates[],
PickCylinder cyl, PickIntersection pi) {
- Point3d origin = getPoint3d();
- Point3d end = getPoint3d();
- Vector3d direction = getVector3d();
- Point3d iPnt1 = getPoint3d();
- Point3d iPnt2 = getPoint3d();
- Vector3d originToIpnt = getVector3d();
+ Point3d origin = new Point3d();
+ Point3d end = new Point3d();
+ Vector3d direction = new Vector3d();
+ Point3d iPnt1 = new Point3d();
+ Point3d iPnt2 = new Point3d();
+ Vector3d originToIpnt = new Vector3d();
// Get cylinder information
cyl.getOrigin (origin);
@@ -3064,25 +2999,11 @@ public class PickResult {
if (coordinates.length > 2) {
if (cyl instanceof PickCylinderRay) {
if (intersectRay (coordinates, new PickRay (origin, direction), pi)) {
- freePoint3d(origin);
- freePoint3d(end);
- freeVector3d(direction);
- freePoint3d(iPnt1);
- freePoint3d(iPnt2);
- freeVector3d(originToIpnt);
-
return true;
}
}
else {
if (intersectSegment (coordinates, new PickSegment (origin, end), pi)) {
- freePoint3d(origin);
- freePoint3d(end);
- freeVector3d(direction);
- freePoint3d(iPnt1);
- freePoint3d(iPnt2);
- freeVector3d(originToIpnt);
-
return true;
}
}
@@ -3107,25 +3028,9 @@ public class PickResult {
pi.setPointCoordinatesVW (iPnt2);
originToIpnt.sub (iPnt1, origin);
pi.setDistance (originToIpnt.length());
-
- freePoint3d(origin);
- freePoint3d(end);
- freeVector3d(direction);
- freePoint3d(iPnt1);
- freePoint3d(iPnt2);
- freeVector3d(originToIpnt);
-
return true;
}
}
-
- freePoint3d(origin);
- freePoint3d(end);
- freeVector3d(direction);
- freePoint3d(iPnt1);
- freePoint3d(iPnt2);
- freeVector3d(originToIpnt);
-
return false;
}
@@ -3136,15 +3041,15 @@ public class PickResult {
static boolean intersectCone (Point3d coordinates[],
PickCone cone, PickIntersection pi) {
- Point3d origin = getPoint3d();
- Point3d end = getPoint3d();
- Vector3d direction = getVector3d();
- Vector3d originToIpnt = getVector3d();
+ Point3d origin = new Point3d();
+ Point3d end = new Point3d();
+ Vector3d direction = new Vector3d();
+ Vector3d originToIpnt = new Vector3d();
double distance;
- Point3d iPnt1 = getPoint3d();
- Point3d iPnt2 = getPoint3d();
- Vector3d vector = getVector3d();
+ Point3d iPnt1 = new Point3d();
+ Point3d iPnt2 = new Point3d();
+ Vector3d vector = new Vector3d();
// Get cone information
cone.getOrigin (origin);
@@ -3160,26 +3065,12 @@ public class PickResult {
if (coordinates.length > 2) {
if (cone instanceof PickConeRay) {
if (intersectRay (coordinates, new PickRay (origin, direction), pi)) {
- freePoint3d(origin);
- freePoint3d(end);
- freePoint3d(iPnt1);
- freePoint3d(iPnt2);
- freeVector3d(direction);
- freeVector3d(originToIpnt);
- freeVector3d(vector);
return true;
}
}
else {
if (intersectSegment (coordinates, new PickSegment (origin, end),
pi)) {
- freePoint3d(origin);
- freePoint3d(end);
- freePoint3d(iPnt1);
- freePoint3d(iPnt2);
- freeVector3d(direction);
- freeVector3d(originToIpnt);
- freeVector3d(vector);
return true;
}
}
@@ -3207,27 +3098,9 @@ public class PickResult {
// System.out.println ("intersectCone: edge "+i+" intersected");
pi.setPointCoordinatesVW (iPnt2);
pi.setDistance (distance);
-
- freePoint3d(origin);
- freePoint3d(end);
- freePoint3d(iPnt1);
- freePoint3d(iPnt2);
- freeVector3d(direction);
- freeVector3d(originToIpnt);
- freeVector3d(vector);
-
return true;
}
}
-
- freePoint3d(origin);
- freePoint3d(end);
- freePoint3d(iPnt1);
- freePoint3d(iPnt2);
- freeVector3d(direction);
- freeVector3d(originToIpnt);
- freeVector3d(vector);
-
return false;
}
@@ -3239,11 +3112,11 @@ public class PickResult {
static boolean intersectCylinder (Point3d pt,
PickCylinder cyl, PickIntersection pi) {
- Point3d origin = getPoint3d();
- Point3d end = getPoint3d();
- Vector3d direction = getVector3d();
- Point3d iPnt = getPoint3d();
- Vector3d originToIpnt = getVector3d();
+ Point3d origin = new Point3d();
+ Point3d end = new Point3d();
+ Vector3d direction = new Vector3d();
+ Point3d iPnt = new Point3d();
+ Vector3d originToIpnt = new Vector3d();
// Get cylinder information
cyl.getOrigin (origin);
@@ -3262,19 +3135,8 @@ public class PickResult {
pi.setPointCoordinatesVW (pt);
originToIpnt.sub (iPnt, origin);
pi.setDistance (originToIpnt.length());
-
- freePoint3d(origin);
- freePoint3d(end);
- freeVector3d(direction);
- freePoint3d(iPnt);
- freeVector3d(originToIpnt);
return true;
}
- freePoint3d(origin);
- freePoint3d(end);
- freeVector3d(direction);
- freePoint3d(iPnt);
- freeVector3d(originToIpnt);
return false;
}
/**
@@ -3286,11 +3148,11 @@ public class PickResult {
// System.out.println ("Intersect.intersectCone point");
- Point3d origin = getPoint3d();
- Point3d end = getPoint3d();
- Vector3d direction = getVector3d();
- Point3d iPnt = getPoint3d();// the closest point on the cone vector
- Vector3d originToIpnt = getVector3d();
+ Point3d origin = new Point3d();
+ Point3d end = new Point3d();
+ Vector3d direction = new Vector3d();
+ Point3d iPnt = new Point3d();// the closest point on the cone vector
+ Vector3d originToIpnt = new Vector3d();
// Get cone information
cone.getOrigin (origin);
@@ -3312,41 +3174,9 @@ public class PickResult {
if (sqDist <= radius*radius) {
pi.setPointCoordinatesVW (pt);
pi.setDistance (distance);
-
- freePoint3d(origin);
- freePoint3d(end);
- freeVector3d(direction);
- freePoint3d(iPnt);
- freeVector3d(originToIpnt);
-
return true;
}
- freePoint3d(origin);
- freePoint3d(end);
- freeVector3d(direction);
- freePoint3d(iPnt);
- freeVector3d(originToIpnt);
-
return false;
}
- static Vector3d getVector3d() {
- //return (Vector3d)UtilFreelistManager.vector3dFreelist.getObject();
- return new Vector3d();
- }
-
- static void freeVector3d(Vector3d v) {
- //UtilFreelistManager.vector3dFreelist.add(v);
- }
-
- static Point3d getPoint3d() {
- //return (Point3d)UtilFreelistManager.point3dFreelist.getObject();
- return new Point3d();
- }
-
- static void freePoint3d(Point3d p) {
- //UtilFreelistManager.point3dFreelist.add(p);
- }
-
-
} // PickResult
diff --git a/src/classes/share/com/sun/j3d/utils/picking/PickTool.java b/src/classes/share/com/sun/j3d/utils/picking/PickTool.java
index d188f56..e888b4a 100644
--- a/src/classes/share/com/sun/j3d/utils/picking/PickTool.java
+++ b/src/classes/share/com/sun/j3d/utils/picking/PickTool.java
@@ -218,13 +218,11 @@ public class PickTool {
/**
- * This method is not supported.
- *
- * @exception UnsupportedOperationException this method is not supported
- *
+ * @deprecated This method does nothing other than return its
+ * input parameter.
*/
public Locale setBranchGroup (Locale l) {
- throw new UnsupportedOperationException();
+ return l;
}
/**
@@ -485,8 +483,8 @@ p @param end The end of the segment
}
/** Select one of the nodes that intersect the PickShape
- @return An array of <code>PickResult</code> objects which will contain
- information about the picked instances. <code>null</code> if nothing
+ @return A <code>PickResult</code> object which will contain
+ information about the picked instance. <code>null</code> if nothing
was picked.
*/
public PickResult pickAny () {
@@ -553,8 +551,8 @@ p @param end The end of the segment
intersects the PickShape. See note above to see how "closest" is
determined.
<p>
- @return An array of <code>PickResult</code> objects which will contain
- information about the picked instances. <code>null</code> if nothing
+ @return A <code>PickResult</code> object which will contain
+ information about the picked instance. <code>null</code> if nothing
was picked.
*/
public PickResult pickClosest () {
@@ -865,8 +863,7 @@ p @param end The end of the segment
PickResult[] pr = new PickResult[sgp.length];
for (i=0; i<sgp.length; i++) {
-// pr[i] = new PickResult(sgp[i], pickShape);
- pr[i] = getPickResult(sgp[i], pickShape);
+ pr[i] = new PickResult(sgp[i], pickShape);
int numIntersection = pr[i].numIntersections();
if (numIntersection > 0) {
// System.out.println ("numIntersection " + numIntersection);
@@ -940,9 +937,6 @@ p @param end The end of the segment
if (pr == null) {
return null;
} else {
- for (int i = 1; i < pr.length; i++) {
- freePickResult(pr[i]);
- }
return pr[0];
}
}
@@ -1017,20 +1011,6 @@ p @param end The end of the segment
if (l<r) quicksort(i, r, dist, pos);
}
- PickResult getPickResult(SceneGraphPath spg, PickShape ps) {
- //PickResult pr = (PickResult)UtilFreelistManager.pickResultFreelist.getObject();
- PickResult pr = new PickResult(spg,ps);
-// if (pr == null) {
-// pr = new PickResult();
-// }
-// pr.reset(spg, ps);
- return pr;
- }
-
- void freePickResult(PickResult pr) {
- //UtilFreelistManager.pickResultFreelist.add(pr);
- }
-
} // PickTool
diff --git a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickMouseBehavior.java b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickMouseBehavior.java
index c4f14df..87ec0b0 100644
--- a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickMouseBehavior.java
+++ b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickMouseBehavior.java
@@ -174,9 +174,5 @@ public abstract class PickMouseBehavior extends Behavior {
*/
public abstract void updateScene(int xpos, int ypos);
- void freePickResult(PickResult pr) {
- UtilFreelistManager.pickResultFreelist.add(pr);
- }
-
}
diff --git a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickRotateBehavior.java b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickRotateBehavior.java
index 589fcf2..5d6ff05 100644
--- a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickRotateBehavior.java
+++ b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickRotateBehavior.java
@@ -142,7 +142,8 @@ public class PickRotateBehavior extends PickMouseBehavior implements MouseBehavi
drag.wakeup();
currentTG = tg;
// free the PickResult
- freePickResult(pr);
+ // Need to clean up Issue 123 --- Chien
+ // freePickResult(pr);
} else if (callback!=null)
callback.transformChanged( PickingCallback.NO_PICK, null );
}
diff --git a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickTranslateBehavior.java b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickTranslateBehavior.java
index 7b19149..b2c996e 100644
--- a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickTranslateBehavior.java
+++ b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickTranslateBehavior.java
@@ -126,7 +126,8 @@ public class PickTranslateBehavior extends PickMouseBehavior implements MouseBeh
translate.setTransformGroup(tg);
translate.wakeup();
currentTG = tg;
- freePickResult(pr);
+ // Need to clean up Issue 123 --- Chien
+ // freePickResult(pr);
} else if (callback!=null)
callback.transformChanged( PickingCallback.NO_PICK, null );
}
diff --git a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickZoomBehavior.java b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickZoomBehavior.java
index 4ec5ebc..bf7e137 100644
--- a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickZoomBehavior.java
+++ b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickZoomBehavior.java
@@ -126,7 +126,8 @@ public class PickZoomBehavior extends PickMouseBehavior implements MouseBehavior
zoom.setTransformGroup(tg);
zoom.wakeup();
currentTG = tg;
- freePickResult(pr);
+ // Need to clean up Issue 123 --- Chien
+ // freePickResult(pr);
} else if (callback!=null)
callback.transformChanged( PickingCallback.NO_PICK, null );
}
diff --git a/src/classes/share/com/sun/j3d/utils/timer/J3DTimer.java b/src/classes/share/com/sun/j3d/utils/timer/J3DTimer.java
index 133529c..984c48e 100644
--- a/src/classes/share/com/sun/j3d/utils/timer/J3DTimer.java
+++ b/src/classes/share/com/sun/j3d/utils/timer/J3DTimer.java
@@ -42,13 +42,9 @@
* $State$
*/
-/**
- * A High Resolution operating system dependent interval timer.
- */
package com.sun.j3d.utils.timer;
/**
- *
* A High Resolution interval timer. The timer resolution is
* operating system dependent and can be queried using
* getTimerResolution().
@@ -56,16 +52,21 @@ package com.sun.j3d.utils.timer;
* These methods are not reentrant and should not
* be called concurrently from multiple threads.
*
+ * @deprecated Use java.lang.System.nanoTime() instead.
*/
public class J3DTimer {
+ // Since we can't get the resolution from the JDK, we will hard-code it
+ // at 1000 (microsecond resolution).
+ private static final long resolution = 1000L;
+
/**
* Private constructor because users should
* not construct instances of this class
*/
private J3DTimer() {
}
-
+
/**
* Get the timer value, in nanoseconds.
* The initial value of the timer is OS dependent.
@@ -73,30 +74,15 @@ public class J3DTimer {
* @return The current timer value in nanoseconds.
*/
public static long getValue() {
- return getNativeTimer();
+ return System.nanoTime();
}
-
+
/**
* Get the nanosecond resolution of the timer
*
* @return The timer resolution in nanoseconds.
*/
public static long getResolution() {
- return getNativeTimerResolution();
- }
-
- private static native long getNativeTimer();
-
- private static native long getNativeTimerResolution();
-
- static {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction() {
- public Object run() {
- System.loadLibrary("j3dutils");
- return null;
- }
- });
-
+ return resolution;
}
}
diff --git a/src/classes/share/com/sun/j3d/utils/timer/package.html b/src/classes/share/com/sun/j3d/utils/timer/package.html
index 33e3b6b..b842c0a 100644
--- a/src/classes/share/com/sun/j3d/utils/timer/package.html
+++ b/src/classes/share/com/sun/j3d/utils/timer/package.html
@@ -3,9 +3,9 @@
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
- <title>com.sun.j3d.XXXXX</title>
+ <title>com.sun.j3d.utils.timer</title>
</head>
<body>
-<p>Provides a high-resolution, operating-system-dependent interval timer.</p>
+<p><i><b>Deprecated</b>: Use java.lang.System.nanoTime() instead.</i></p>
</body>
</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ViewInfo.java b/src/classes/share/com/sun/j3d/utils/universe/ViewInfo.java
index 55df965..92d2cbf 100644
--- a/src/classes/share/com/sun/j3d/utils/universe/ViewInfo.java
+++ b/src/classes/share/com/sun/j3d/utils/universe/ViewInfo.java
@@ -45,6 +45,7 @@
package com.sun.j3d.utils.universe ;
import java.awt.GraphicsConfiguration ;
+import java.awt.GraphicsEnvironment;
import java.awt.Point ;
import java.awt.Rectangle ;
import java.text.DecimalFormat ;
@@ -2728,7 +2729,14 @@ public class ViewInfo {
}
}
- this.screenBounds = graphicsConfiguration.getBounds() ;
+ GraphicsConfiguration gc1 = graphicsConfiguration;
+ // Workaround for Issue 316 - use the default config for screen 0
+ // if the graphics config is null
+ if (gc1 == null) {
+ gc1 = GraphicsEnvironment.getLocalGraphicsEnvironment().
+ getDefaultScreenDevice().getDefaultConfiguration();
+ }
+ this.screenBounds = gc1.getBounds() ;
double mpx = screenWidth / (double)screenBounds.width ;
double mpy = screenHeight / (double)screenBounds.height ;
if ((mpx != metersPerPixelX) || (mpy != metersPerPixelY)) {
diff --git a/src/classes/share/com/sun/j3d/utils/universe/Viewer.java b/src/classes/share/com/sun/j3d/utils/universe/Viewer.java
index c171632..46afef2 100644
--- a/src/classes/share/com/sun/j3d/utils/universe/Viewer.java
+++ b/src/classes/share/com/sun/j3d/utils/universe/Viewer.java
@@ -50,10 +50,8 @@ import java.net.URL;
import java.util.*;
import javax.media.j3d.*;
import javax.swing.*;
-import javax.vecmath.*;
import com.sun.j3d.audioengines.AudioEngine3DL2;
import java.lang.reflect.Constructor;
-import java.applet.*;
/**
* The Viewer class holds all the information that describes the physical
@@ -550,9 +548,11 @@ public class Viewer {
"No GraphicsConfiguration on screen " +
cs[i].frameBufferNumber + " conforms to template");
- bounds = cfg.getBounds();
+ // Workaround for Issue 316 - use the default config for the screen
+ GraphicsConfiguration defCfg = cfg.getDevice().getDefaultConfiguration();
+ bounds = defCfg.getBounds();
cs[i].j3dJFrame = j3dJFrames[i] =
- new JFrame(cs[i].instanceName, cfg);
+ new JFrame(cs[i].instanceName, defCfg);
if (cs[i].noBorderFullScreen) {
try {
@@ -978,26 +978,37 @@ public class Viewer {
throw new UnsupportedOperationException("No AudioDevice specified");
}
- ClassLoader audioDeviceClassLoader =
- (ClassLoader) java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction() {
- public Object run() {
- return ClassLoader.getSystemClassLoader();
- }
- });
-
- if (audioDeviceClassLoader == null) {
- throw new IllegalStateException("System ClassLoader is null");
- }
+ // Issue 341: try the current class loader first before trying the
+ // system class loader
+ Class audioDeviceClass = null;
+ try {
+ audioDeviceClass = Class.forName(audioDeviceClassName);
+ } catch (ClassNotFoundException ex) {
+ // Ignore excpetion and try system class loader
+ }
+
+ if (audioDeviceClass == null) {
+ ClassLoader audioDeviceClassLoader =
+ (ClassLoader) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ return ClassLoader.getSystemClassLoader();
+ }
+ });
+
+ if (audioDeviceClassLoader == null) {
+ throw new IllegalStateException("System ClassLoader is null");
+ }
+
+ audioDeviceClass = Class.forName(audioDeviceClassName, true, audioDeviceClassLoader);
+ }
- Class audioDeviceClass =
- Class.forName(audioDeviceClassName, true, audioDeviceClassLoader);
Class physEnvClass = PhysicalEnvironment.class;
Constructor audioDeviceConstructor =
audioDeviceClass.getConstructor(new Class[] {physEnvClass});
PhysicalEnvironment[] args = new PhysicalEnvironment[] { physicalEnvironment };
AudioEngine3DL2 mixer =
- (AudioEngine3DL2) audioDeviceConstructor.newInstance(args);
+ (AudioEngine3DL2) audioDeviceConstructor.newInstance((Object[])args);
mixer.initialize();
return mixer;
}
diff --git a/src/native/share/J3DTimer.c b/src/native/share/J3DTimer.c
deleted file mode 100644
index 6725d2f..0000000
--- a/src/native/share/J3DTimer.c
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * $RCSfile$
- *
- * Copyright (c) 2006 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.
- *
- * $Revision$
- * $Date$
- * $State$
- */
-
-/*
- * Portions of this code were derived from work done by the Blackdown
- * group (www.blackdown.org), who did the initial Linux implementation
- * of the Java 3D API.
- */
-
-#include "com_sun_j3d_utils_timer_J3DTimer.h"
-
-#define NSEC_PER_SEC ((jlong)1000000000)
-
-#ifdef __linux__
-#include <sys/time.h>
-#include <time.h>
-#include <unistd.h>
-#endif
-
-#ifdef SOLARIS
-#include <time.h>
-#include <sys/systeminfo.h>
-#include <string.h>
-#ifndef CLOCK_HIGHRES
-#define CLOCK_HIGHRES 4 /* Solaris 7 does not define this */
-#endif /* constant. When run on Solaris 7 */
-#endif /* CLOCK_HIGHRES is not used. */
-
-#ifdef WIN32
-#include <Windows.h>
-#include <math.h>
-static double timerScale = -1.0;
-#endif
-
-/*
- * Class: com_sun_j3d_utils_timer_J3DTimer
- * Method: getNativeTimer
- * Signature: ()J
- */
-JNIEXPORT jlong JNICALL
-Java_com_sun_j3d_utils_timer_J3DTimer_getNativeTimer(JNIEnv *env,
- jclass clazz)
-{
- jlong timerNsec;
-
-#ifdef SOLARIS
- /*
- struct timespec tp;
- clock_gettime( CLOCK_HIGHRES, &tp );
-
- return (jlong)tp.tv_nsec + (jlong)tp.tv_sec * NSEC_PER_SEC;
- */
-
- timerNsec = (jlong)gethrtime();
-#endif /* SOLARIS */
-
-#ifdef WIN32
- LARGE_INTEGER time;
- LARGE_INTEGER freq;
-
- if (timerScale < 0.0) {
- QueryPerformanceFrequency( &freq );
- if (freq.QuadPart <= 0) {
- timerScale = 0.0;
- }
- else {
- timerScale = (double) NSEC_PER_SEC / (double)freq.QuadPart;
- }
- }
-
- QueryPerformanceCounter(&time);
- timerNsec = (jlong)((double)time.QuadPart * timerScale);
-
-#endif /* WIN32 */
-
-#ifdef __linux__
- struct timeval t;
-
- gettimeofday(&t, 0);
- timerNsec = ((jlong)t.tv_sec) * NSEC_PER_SEC + ((jlong)t.tv_usec) * ((jlong)1000);
-#endif /* __linux__ */
-
- return timerNsec;
-}
-
-
-/*
- * Class: com_sun_j3d_utils_timer_J3DTimer
- * Method: getNativeTimerResolution
- * Signature: ()J
- */
-JNIEXPORT jlong JNICALL
-Java_com_sun_j3d_utils_timer_J3DTimer_getNativeTimerResolution(JNIEnv *env,
- jclass clazz)
-{
- jlong res;
-
-#ifdef SOLARIS
- char buf[4];
-
- sysinfo( SI_RELEASE, &buf[0], 4 );
-
- if (strcmp( "5.7", &buf[0] )==0) {
- /* Hard-coded for Solaris 7, since clock_getres isn't available */
- res = (jlong)3;
- } else {
- struct timespec tp;
- clock_getres( CLOCK_HIGHRES, &tp );
- res = (jlong)tp.tv_nsec;
- }
-#endif /* SOLARIS */
-
-#ifdef WIN32
- LARGE_INTEGER freq;
- QueryPerformanceFrequency( &freq );
-
- if ((jlong)freq.QuadPart <= 0)
- res = 0;
- else {
- res = (NSEC_PER_SEC + (jlong)freq.QuadPart - 1) / ((jlong)freq.QuadPart);
-
- if (res < 1) {
- res = 1;
- }
- }
-#endif
-
-#ifdef __linux__
- /* Hard-coded at 1 microsecond -- the resolution of gettimeofday */
- res = 1000;
-#endif /* __linux__ */
-
- return res;
-}