java3d-1.5.2+dfsg/ 0000755 0000000 0000000 00000000000 11240226610 012276 5 ustar root root java3d-1.5.2+dfsg/j3d-core-utils/ 0000755 0000000 0000000 00000000000 11240226670 015050 5 ustar root root java3d-1.5.2+dfsg/j3d-core-utils/THIRDPARTY-LICENSE-DISTANCE.txt 0000644 0000000 0000000 00000012300 10067113606 021547 0 ustar root root The following additional provisions apply to third party software
included as part of this product.
com/sun/j3d/internal/
Distance.java
Copyright (c) 2004 Magic Software, Inc. All Rights Reserved.
The Wild Magic Library (WML) source code is supplied under the terms of
the license agreement http://www.magic-software.com/License/WildMagic.pdf
and may not be copied or disclosed except in accordance with the terms of
that agreement.
---------- Text of WildMagic.pdf downloaded 8-Jun-2004 ----------
License Agreement for the Wild Magic (Version 2) Software Library
Revision 1: January 28, 2004
THIS WILD MAGIC (VERSION 2) SOFTWARE LICENSE AGREEMENT ("Agreement")
is made by and between Magic Software, Inc. ("We/Us/Our"), a North
Carolina corporation, having its principal place of business at 6006
Meadow Run Court, Chapel Hill, North Carolina 27516 and any person or
legal entity using or accepting any software governed by this
Agreement ("You/Your"). This Agreement shall be effective on the first
day you use or accept software ("The Software") governed by this
Agreement, whichever is earlier.
THE PARTIES AGREE as follows:
1. Grant. We grant you a nonexclusive, nontransferable, and perpetual
license to use The Software subject to the terms and conditions of the
Agreement:
(a) There is no charge to you for this license.
(b) The Software may be used, edited, modified, copied, and
distributed by you for noncommercial products.
(c) The Software may be used, edited, modified, copied, and
distributed by you for commercial products provided that such
products are not intended to wrap The Software solely for the
purposes of selling it as if it were your own product. The
intent of this clause is that you use The Software, in part or
in whole, to assist you in building your own original
products. An example of acceptable use is to incorporate the
graphics portion of The Software in a game to be sold to an end
user. An example that violates this clause is to compile a
library from only The Software, bundle it with the headers
files as a Software Development Kit (SDK), then sell that SDK
to others. If there is any doubt about whether you can use The
Software for a commercial product, contact us and explain what
portions you intend to use. We will consider creating a
separate legal document that grants you permission to use those
portions of The Software in your commercial product.
2. Limitation of Liability. We will have no liability for special,
incidental or consequential damages even if advised of the
possibility of such damages. We will not be liable for any other
damages or loss in any way connected with The Software.
3. Warranties. We make no warranties at all. The Software is
transferred to you on an as-is basis. You use The Software at your
own peril. You assume all risk of loss for all claims or
controversies, now existing or hereafter, arising out of use of The
Software. We shall have no liability based on a claim that your use
or combination of The Software with products or data not supplied
by us infringes any patent, copyright, or proprietary right. All
other warranties, expressed or implied, including, without
limitation, any warranty of merchantability or fitness for a
particular purpose are hereby excluded.
4. Taxes and Duties. You shall pay or reimburse us for federal, state,
provincial, local or other tariffs, duties and taxes not based on
our net income, including all taxes, tariffs, duties, or amounts
levied in lieu thereof, based on charges payable under this
Agreement or based on The Software, its use or any services
performed hereunder, whether such tariffs, duties or taxes are now
or hereafter imposed under the authority of any federal, state,
provincial, local or other jurisdiction.
5. Entire Agreement, Amendments. This Agreement represents the
complete and exclusive statement of the Agreements between the
parties relating to the licensing of The Software and maintenance
of The Software and supersedes all prior Agreements and
representations between them relating to such
licensing. Modifications to this Agreement shall not be effective
unless in writing and signed by the party against whom enforcement
is sought. The terms of this Agreement shall not be amended or
changed by any purchase order or acknowledgment even if we have
signed such documents.
6. North Carolina Law, Severability. This Agreement will be governed
by North Carolina law. If any provision of this Agreement shall be
unlawful, void, or for any reason unenforceable, it shall be deemed
severable from and shall in no way affect the validity or
enforceability of the remaining provisions of this Agreement.
7. Force Majeure. It is herein agreed that neither party to this
Agreement shall be liable for delays for failures in performance
resulting from acts beyond the control of such party. Such acts
include, without limitation, acts of God, strikes, lockouts, riots,
acts of war, epidemics, governmental regulations superimposed after
the fact, fire, power failures, earthquakes or other disasters.
java3d-1.5.2+dfsg/j3d-core-utils/COPYRIGHT.txt 0000644 0000000 0000000 00000007502 10563126501 017164 0 ustar root root Copyright (c) 2007 Sun Microsystems, Inc., 4150 Network Circle, Santa
Clara, California 95054, U.S.A. All rights reserved.
Sun Microsystems, Inc. has intellectual property rights relating to
technology embodied in the product that is described in this
document. In particular, and without limitation, these intellectual
property rights may include one or more of the U.S. patents listed at
http://www.sun.com/patents and one or more additional patents or
pending patent applications in the U.S. and in other countries.
U.S. Government Rights - Commercial software. Government users are
subject to the Sun Microsystems, Inc. standard license agreement and
applicable provisions of the FAR and its supplements.
Use is subject to license terms.
This distribution may include materials developed by third parties.
Parts of the product may be derived from Berkeley BSD systems,
licensed from the University of California. UNIX is a registered
trademark in the U.S. and in other countries, exclusively licensed
through X/Open Company, Ltd.
Sun, Sun Microsystems, the Sun logo, Java, Solaris, Java 3D, the 100%
Pure Java logo, the Duke logo and the Java Coffee Cup logo are
trademarks or registered trademarks of Sun Microsystems, Inc. in the
U.S. and other countries.
This product is covered and controlled by U.S. Export Control laws and
may be subject to the export or import laws in other countries.
Nuclear, missile, chemical biological weapons or nuclear maritime end
uses or end users, whether direct or indirect, are strictly
prohibited. Export or reexport to countries subject to U.S. embargo or
to entities identified on U.S. export exclusion lists, including, but
not limited to, the denied persons and specially designated nationals
lists is strictly prohibited.
Copyright (c) 2007 Sun Microsystems, Inc., 4150 Network Circle, Santa
Clara, California 95054, Etats-Unis. Tous droits réservés.
Sun Microsystems, Inc. détient les droits de propriété intellectuels
relatifs à la technologie incorporée dans le produit qui est décrit
dans ce document. En particulier, et ce sans limitation, ces droits de
propriété intellectuelle peuvent inclure un ou plus des brevets
américains listés à l'adresse http://www.sun.com/patents et un ou les
brevets supplémentaires ou les applications de brevet en attente aux
Etats - Unis et dans les autres pays.
L'utilisation est soumise aux termes de la Licence.
Cette distribution peut comprendre des composants développés par des
tierces parties.
Des parties de ce produit pourront être dérivées des systèmes Berkeley
BSD licenciés par l'Université de Californie. UNIX est une marque
déposée aux Etats-Unis et dans d'autres pays et licenciée
exclusivement par X/Open Company, Ltd.
Sun, Sun Microsystems, le logo Sun, Java, Solaris, Java 3D, le logo
100% Pure Java, le logo Duke et le logo Java Coffee Cup sont des
marques de fabrique ou des marques déposées de Sun Microsystems,
Inc. aux Etats-Unis et dans d'autres pays.
Ce produit est soumis à la législation américaine en matière de
contrôle des exportations et peut être soumis à la règlementation en
vigueur dans d'autres pays dans le domaine des exportations et
importations. Les utilisations, ou utilisateurs finaux, pour des armes
nucléaires,des missiles, des armes biologiques et chimiques ou du
nucléaire maritime, directement ou indirectement, sont strictement
interdites. Les exportations ou réexportations vers les pays sous
embargo américain, ou vers des entités figurant sur les listes
d'exclusion d'exportation américaines, y compris, mais de manière non
exhaustive, la liste de personnes qui font objet d'un ordre de ne pas
participer, d'une façon directe ou indirecte, aux exportations des
produits ou des services qui sont régis par la législation américaine
en matière de contrôle des exportations et la liste de ressortissants
spécifiquement désignés, sont rigoureusement interdites.
java3d-1.5.2+dfsg/j3d-core-utils/THIRDPARTY-LICENSE-MAINFRAME.txt 0000644 0000000 0000000 00000003023 10067113606 021656 0 ustar root root The following additional provisions apply to third party software
included as part of this product.
com/sun/j3d/utils/applet/
MainFrame.java - run an Applet as an application in AWT Frame
JMainFrame.java - run an Applet as an application in Swing JFrame
Copyright (C) 1996 by Jef Poskanzer This project contains the source code
for the 3D core utilities in the Related projects include: vecmath
(the 3D vector math package), j3d-core
(the
core 3D package), and j3d-examples
(the 3D
example programs). The j3d-core-utils project is built as part of
j3d-core. Developers should refer to the CVS
download
and build
instructions for information on downloading the source code and
building j3d-core-utils. For all other project
information, including contributing to the 3D projects and reporting
issues,
please go to the parent java3d
project. NOTE: The issue Tracker in this j3d-core-utils subproject cannot be
used
to file or track issues. Please use the Issue Tracker
in the parent java3d
project
to track issues (bugs, feature requests, etc.) for
j3d-core-utils and other 3D sub-projects. To
build the Java 3DTM packages,
you must checkout the following three CVS repositories: These three top-level directories must be named exactly as
shown above and they must be sibling directories. To ensure this, run
the cvs checkout command for each of the respositories from the same
parent
directory. For example: After you have downloaded the three CVS repositories, read the
README-FIRST.txt file, then read and follow the
instructions in the README-build.html
files in all three CVS repositories. Go to the "CVS client setup"
page for instructions on how to access the j3d-core-utils source code
from CVS. Click on the "Version
Control - CVS" link to browse the j3d-core-utils source code.
Automated CVS change messages are sent to the cvs 'at'
j3d-core-utils.dev.java.net
list list. Click here to
subscribe to this list.
*
* When a texture is applied to a cylinder, the texture is applied to the
* caps and the body different. A texture is mapped CCW from the back of the
* body. The top and bottom caps are mapped such that the texture appears
* front facing when the caps are rotated 90 degrees toward the viewer.
*
* By default all primitives with the same parameters share their
* geometry (e.g., you can have 50 shperes in your scene, but the
* geometry is stored only once). A change to one primitive will
* effect all shared nodes. Another implication of this
* implementation is that the capabilities of the geometry are shared,
* and once one of the shared nodes is live, the capabilities cannot
* be set. Use the GEOMETRY_NOT_SHARED flag if you do not wish to
* share geometry among primitives with the same parameters.
*/
public class Cylinder extends Primitive{
float radius, height;
int xdivisions, ydivisions;
static final int MID_REZ_DIV_X = 15;
static final int MID_REZ_DIV_Y = 1;
/**
* Designates the body of the cylinder. Used by
* For any NodeComponent objects
* contained by the object being duplicated, each NodeComponent
* object's
*
* If two (or more) triangles in the model share the same coordinate
* index then the normal generator will attempt to generate one normal
* for the vertex, resulting in a "smooth" looking surface. If two
* coordinates don't have the same index then they will have two
* separate normals, even if they have the same position. This will
* result in a "crease" in your object. If you suspect that your
* data isn't properly indexed, call GeometryInfo.recomputeIndexes().
*
* Of course, sometimes your model *has* a crease in it. That's what
* creaseAngle is. If two triangles' normals differ by more than
* creaseAngle, then the vertex will get two separate normals, creating a
* discontinuous crease in the model. This is perfect for the edge
* of a table or the corner of a cube, for instance.
*/
public class NormalGenerator {
private double creaseAngle;
private Vector3f facetNorms[];
private ArrayList tally;
private GeometryInfo gi;
private int coordInds[];
private int normalInds[];
private int colorInds[];
private int texInds[][];
private int stripCounts[];
private static long t1=0, t2=0, t3=0, t4=0, t5=0, t6=0;
private Triangulator tr = null;
private int numTexSets;
// 0 - No debug info
// 1 - Facet Normals
// 2 - Connection info
// 4 - Normals
// 8 - StripCounts
// 16 - Timing
// 32 - Hard edges
// 64 - Coordinate and normal indices
// 128 - Vertex normal calculation info
private static final int DEBUG = 0;
// Calculate the normal of each triangle in the list by finding
// the cross product
private void calculatefacetNorms()
{
Point3f coordinates[] = gi.getCoordinates();
facetNorms = new Vector3f[coordInds.length / 3];
Vector3f a = new Vector3f();
Vector3f b = new Vector3f();
if ((DEBUG & 1) != 0) System.out.println("Facet normals:");
if (gi.getOldPrim() != gi.QUAD_ARRAY) {
for (int t = 0 ; t < coordInds.length ; t += 3) {
a.sub(coordinates[coordInds[t + 2]], coordinates[coordInds[t + 1]]);
b.sub(coordinates[coordInds[t + 0]], coordinates[coordInds[t + 1]]);
facetNorms[t / 3] = new Vector3f();
facetNorms[t / 3].cross(a, b);
facetNorms[t / 3].normalize();
if (Float.isNaN(facetNorms[t / 3].x)) {
// Normal isn't valid
facetNorms[t / 3].x = 1.0f;
facetNorms[t / 3].y = facetNorms[t / 3].z = 0.0f;
}
if ((DEBUG & 1) != 0) {
System.out.println(" " + (t/3) + " " + facetNorms[t / 3]);
}
}
} else {
// For quads, the facet normal of both triangles is the cross
// product of the two vectors that make an 'X' across the quad.
for (int t = 0 ; t < coordInds.length ; t += 6) {
a.sub(coordinates[coordInds[t + 2]], coordinates[coordInds[t + 0]]);
b.sub(coordinates[coordInds[t + 5]], coordinates[coordInds[t + 1]]);
facetNorms[t / 3] = new Vector3f();
facetNorms[t / 3].cross(a, b);
facetNorms[t / 3].normalize();
if (Float.isNaN(facetNorms[t / 3].x)) {
// Normal isn't valid
facetNorms[t / 3].x = 1.0f;
facetNorms[t / 3].y = facetNorms[t / 3].z = 0.0f;
}
// Second triangle of quad
facetNorms[t / 3 + 1] = new Vector3f(facetNorms[t / 3]);
if ((DEBUG & 1) != 0) {
System.out.println(" " + (t/3) + "&" + (t/3 + 1) + " " +
facetNorms[t / 3]);
}
}
}
} // End of calculatefacetNorms
// The vertex normals will be calculated by averaging the facet normals
// of groups of triangles sharing the vertex. At the end of this routine
// the groups of coordinate indexes will all be made, and the normal
// indices will point to these groups.
//
// The routine works by going through each vertex of each triangle.
// Starting at a triangle, we see if the vertex normal can be shared
// with the neighbor triangle (their facet normals differ by less than
// creaseAngle). If they can be shared, then we move from that triangle
// to the next and the next in a circle around the vertex.
//
// If we hit the edge of the model or a Hard Edge (crease) then we stop
// and then try going around the vertex in the other direction.
//
// Each time we step from one triangle to the next around the center
// vertex, the triangle is added to the group of triangles whose normals
// will be averaged to make the vertex normal.
//
// Returns the largest number of triangles that share a single normal.
//
private int createHardEdges()
{
EdgeTable et = new EdgeTable(coordInds);
tally = new ArrayList();
int normalMap[] = new int[coordInds.length];
int maxShare = 1;
float cosine;
boolean smooth;
float threshold = (float)Math.cos(creaseAngle);
boolean goingRight;
// Set Normal Indices array values to a flag
for (int c = 0 ; c < coordInds.length ; c++)
normalMap[c] = Integer.MAX_VALUE;
// Cycle through each vertex
for (int c = 0 ; c < coordInds.length ; c++) {
// See if this vertex's normal has already been done
if (normalMap[c] == Integer.MAX_VALUE) {
if ((DEBUG & 32) != 0) {
System.out.println(
"Coordinate Index " + c + ": vertex " + coordInds[c]);
}
// Create a list of vertices used for calculating this normal
ArrayList sharers = new ArrayList();
tally.add(sharers);
// Put this coordinate in the list
sharers.add(new Integer(c));
// Point this coordinate's index at its list
normalMap[c] = tally.size() - 1;
// First do right edge
goingRight = true;
Edge edge = new Edge(coordInds[c],
coordInds[(c + 1) % 3 == 0 ? c - 2 : c + 1]);
if ((DEBUG & 32) != 0)
System.out.println( " Right edge: " + edge);
// This is how we'll know we've gone all the way around
int endVertex = coordInds[c % 3 == 0 ? c + 2 : c - 1];
// Start at current triangle
int cur = c;
// Proceed from one triangle to the next
do {
// Look up edge in Edge Table to find neighbor triangle
Integer tableVal = et.get(edge.v2, edge.v1);
if ((DEBUG & 32) != 0) {
System.out.println(
" Search Edge: " + (new Edge(edge.v2, edge.v1)));
}
// See if there is no triangle on the other side of this edge
if (tableVal == null) {
smooth = false;
if ((DEBUG & 32) != 0)
System.out.println(" No neighboring triangle found.");
} else {
int n = tableVal.intValue();
if ((DEBUG & 32) != 0) {
System.out.println(
" Table lookup result: " + n + " (vertex " + coordInds[n] +
")");
System.out.print(" Triangles " + (cur/3) + " & " + (n/3) +
": ");
}
cosine = facetNorms[cur / 3].dot(facetNorms[n / 3]);
smooth = cosine > threshold;
if (smooth) {
// The center coordinate (c) shares the same normal in these
// two triangles. Find that coordinate and set its index
// normalMap[n] = normalMap[cur];
int centerv = (((n + 1) % 3) == 0 ? n - 2 : n + 1);
if (coordInds[c] != coordInds[centerv]) {
centerv = ((n % 3) == 0 ? n + 2 : n - 1);
}
if ((DEBUG & 32) != 0)
System.out.println("Smooth! Adding " + centerv);
if (normalMap[centerv] != Integer.MAX_VALUE) {
smooth = false;
if ((DEBUG & 32) != 0) System.out.println(
" Error: Coordinate aleady has normal (bad data).");
} else {
normalMap[centerv] = tally.size() - 1;
// Consider this triangle's facet normal when calculating the
// vertex's normal
sharers.add(new Integer(centerv));
if (sharers.size() > maxShare) maxShare = sharers.size();
// Continue on around the vertex to the next triangle
cur = n;
if (goingRight) edge.v2 = coordInds[cur];
else edge.v1 = coordInds[cur];
}
} else if ((DEBUG & 32) != 0) System.out.println("Hard Edge!");
}
if (!smooth && goingRight) {
// We've hit an impasse going right, so now try going left
// from the original triangle
goingRight = false;
smooth = true; // Trick do loop
cur = c; // Go back to original triangle
edge = new Edge(coordInds[(c % 3) == 0 ? c + 2 : c - 1],
coordInds[c]);
if ((DEBUG & 32) != 0) System.out.println( " Left edge: " + edge);
}
} while (smooth && ((goingRight && (edge.v2 != endVertex)) ||
!goingRight));
if (((DEBUG & 32) != 0) && goingRight && (edge.v2 == endVertex))
System.out.println(" Went all the way around!");
}
}
if ((DEBUG & 32) != 0) {
System.out.println("Tally:");
for (int i = 0 ; i < tally.size() ; i++) {
System.out.print(" " + i + ": ");
ArrayList sharers = (ArrayList)(tally.get(i));
for (int j = 0 ; j < sharers.size() ; j++) {
System.out.print(" " + sharers.get(j));
}
System.out.println();
}
System.out.println("Normal Indexes:");
for (int i = 0 ; i < normalMap.length ; i++) {
System.out.println(" " + i + ": " + normalMap[i]);
}
}
return maxShare;
} // End of createHardEdges
// Now take all of the triangles who share a vertex (who have
// been grouped by the hard edge process) and average their facet
// normals to get the vertex normal
//
// This routine has something of a hack in it. We found that our
// method of breaking up data into individual triangles before
// calculating normals was causing a bug. If a polygon was broken
// into two triangles at a particular vertex, then that facet's
// normal would get averaged into the vertex normal *twice*,
// skewing the normal toward the decomposed facet. So what we did
// was to check for duplicate facet normals as we're averaging,
// not allowing the same facet normal to be counted twice.
//
// What should be done is to put the facets' normals into a separate,
// indexed, table. That way, to tell if two triangles have the
// same normal, we just need to compare indexes. This would speed up
// the process of checking for duplicates.
private void calculateVertexNormals(int maxShare)
{
Vector3f normals[];
ArrayList sharers;
int triangle;
Vector3f fn[]; // Normals of facets joined by this vertex
int fnsize; // Number of elements currently ised in fn
if (creaseAngle != 0.0) {
fn = new Vector3f[maxShare];
normals = new Vector3f[tally.size()];
normalInds = new int[coordInds.length];
for (int n = 0 ; n < tally.size() ; n++) {
sharers = (ArrayList)(tally.get(n));
if ((DEBUG & 128) != 0) {
System.out.println(n + ": " + sharers.size() +
" triangles:");
}
fnsize = 0;
normals[n] = new Vector3f();
for (int t = 0 ; t < sharers.size() ; t++) {
int v = ((Integer)sharers.get(t)).intValue();
// See if index removed by hard edge process
if (v != -1) {
triangle = v / 3;
if (!Float.isNaN(facetNorms[triangle].x)) {
int f;
// Don't add the same facet normal twice
for (f = 0 ; f < fnsize ; f++) {
if (fn[f].equals(facetNorms[triangle])) break;
}
normalInds[v] = n;
if (f == fnsize) {
// Didn't find this triangle's normal already in the list
normals[n].add(facetNorms[triangle]);
fn[fnsize++] = facetNorms[triangle];
} else if ((DEBUG & 128) != 0) {
System.out.println(" triangle " + t + " ignored.");
}
}
}
}
normals[n].normalize();
if (Float.isNaN(normals[n].x)) {
// Normal isn't valid
normals[n].x = 1.0f; normals[n].y = normals[n].z = 0.0f;
}
if ((DEBUG & 128) != 0) {
for (int t = 0 ; t < sharers.size() ; t++) {
int v = ((Integer)sharers.get(t)).intValue();
if (v != -1) {
triangle = v / 3;
System.out.println(" " + facetNorms[triangle]);
}
}
System.out.println(" Result: " + normals[n]);
System.out.println();
}
}
} else {
// This code renders the facet normals
normals = facetNorms;
normalInds = new int[facetNorms.length * 3];
for (int i = 0 ; i < facetNorms.length ; i++) {
normalInds[i * 3 + 0] = i;
normalInds[i * 3 + 1] = i;
normalInds[i * 3 + 2] = i;
}
}
gi.setNormals(normals);
if ((DEBUG & 4) != 0) {
System.out.println("Normals:");
for (int i = 0 ; i < normals.length ; i++) {
System.out.println(" " + i + " " + normals[i]);
}
System.out.println("Indices:");
for (int i = 0 ; i < normalInds.length ; i++) {
System.out.println(" " + i + " " + normalInds[i]);
}
}
} // End of calculateVertexNormals
// The original data was in quads and we converted it to triangles to
// calculate the normals. Now we are converting it back to quads.
// It's a very simple algorithm.
// Since both sub-triangles of a quad have the same facet normal,
// there should never be a hard edge down the middle of the quad.
// Therefore, the vertices of the shared edge of the two subtriangles
// should have the same normal index in both triangles.
private int[] triToQuadIndices(int oldList[])
{
if (oldList == null) return null;
int newList[] = new int[oldList.length / 6 * 4];
// index list to pass back
// Cycle through each pair of triangles and put them together
for (int q = 0 ; q < oldList.length / 6 ; q++) {
newList[q * 4 + 0] = oldList[q * 6 + 0];
newList[q * 4 + 1] = oldList[q * 6 + 1];
newList[q * 4 + 2] = oldList[q * 6 + 2];
newList[q * 4 + 3] = oldList[q * 6 + 5];
}
return newList;
} // End of triToQuadIndices
// The original data was in quads. We converted it to triangles to
// calculate normals. Now we need to convert it back to quads.
private void convertTriToQuad(GeometryInfo geom)
{
// Create the new arrays
geom.setCoordinateIndices(
triToQuadIndices(geom.getCoordinateIndices()));
geom.setColorIndices(triToQuadIndices(geom.getColorIndices()));
geom.setNormalIndices(triToQuadIndices(geom.getNormalIndices()));
int num = geom.getTexCoordSetCount();
for (int i = 0 ; i < num ; i++) {
geom.setTextureCoordinateIndices(i,
triToQuadIndices(geom.getTextureCoordinateIndices(i)));
}
geom.setPrimitive(gi.QUAD_ARRAY);
} // End of convertTriToQuad()
// The original data was in fans and we converted it to triangles to
// calculate the normals. Now we are converting it back to fans.
// We have already calculated the new stripCounts, so now we need
// to change the index lists so they match up with the stripCounts.
// It's a very simple algorithm. The paramater oldList is the
// index list being compressed back into fans (could be coordinate,
// color, normal, or texCoord indices) and numVerts is the pre-
// calculated total of all entries of the stripCounts array.
private int[] triToFanIndices(int sc[], int oldList[], int numVerts)
{
if (oldList == null) return null;
int newList[] = new int[numVerts]; // index list to pass back
int vert1 = 0; // 1st vertex of triangle
int n = 0; // index into newList
// Cycle through each fan in the new list
for (int f = 0 ; f < sc.length ; f++) {
// Copy entire first triangle into new array
newList[n++] = oldList[vert1++];
newList[n++] = oldList[vert1++];
newList[n++] = oldList[vert1++];
// Each additional triangle in the fan only needs one vertex
for (int t = 3 ; t < sc[f] ; t++) {
newList[n++] = oldList[vert1 + 2];
vert1 += 3;
}
}
return newList;
} // End of triToFanIndices
//
// The original data was in fans. We converted it to triangles to
// calculate normals. Now we need to convert it back to fans.
// The hard part is that, if we found a hard edge in the middle of
// a fan, we need to split the fan into two. To tell if there's
// a hard edge there, we compare the normal indices of both
// vertices comprising the edge.
private void convertTriToFan(GeometryInfo geom, int oldStripCounts[])
{
int ni[] = geom.getNormalIndices();
//
// Calculate new stripCounts array
//
int tri = 0; // Which triangle currently being converted
ArrayList newStripCounts;
newStripCounts = new ArrayList(oldStripCounts.length + 100);
// Use the original stripCounts array
for (int f = 0 ; f < oldStripCounts.length ; f++) {
int stripCount = 3;
// Cycle through each triangle in the fan, comparing to the
// next triangle in the fan. Compare the normal indices of
// both vertices of the edge to see if the two triangles
// can be mated
for (int t = 0 ; t < oldStripCounts[f] - 3 ; t++) {
// The first vertex of this triangle must match the first
// vertex of the next, AND the third vertex of this
// triangle must match the second vertex of the next.
if ((ni[tri * 3] == ni[(tri+1) * 3]) &&
(ni[tri * 3 + 2] == ni[(tri+1) * 3 + 1])) {
// OK to extend fan
stripCount++;
} else {
// hard edge within fan
newStripCounts.add(new Integer(stripCount));
stripCount = 3;
}
tri++;
}
tri++;
newStripCounts.add(new Integer(stripCount));
}
// Convert from ArrayList to int[]
int sc[] = new int[newStripCounts.size()];
for (int i = 0 ; i < sc.length ; i++)
sc[i] = ((Integer)newStripCounts.get(i)).intValue();
newStripCounts = null;
//
// Change the index lists so they match up with the new stripCounts
//
// See how many vertices we'll need
int c = 0;
for (int i = 0 ; i < sc.length ; i++) c += sc[i];
// Create the new arrays
geom.setCoordinateIndices(
triToFanIndices(sc, geom.getCoordinateIndices(), c));
geom.setColorIndices(triToFanIndices(sc, geom.getColorIndices(), c));
geom.setNormalIndices(triToFanIndices(sc, geom.getNormalIndices(), c));
int num = geom.getTexCoordSetCount();
for (int i = 0 ; i < num ; i++) {
geom.setTextureCoordinateIndices(i,
triToFanIndices(sc, geom.getTextureCoordinateIndices(i), c));
}
if ((DEBUG & 8) != 0) {
System.out.print("Old stripCounts:");
for (int i = 0 ; i < oldStripCounts.length ; i++) {
System.out.print(" " + oldStripCounts[i]);
}
System.out.println();
System.out.print("New stripCounts:");
for (int i = 0 ; i < sc.length ; i++) {
System.out.print(" " + sc[i]);
}
System.out.println();
}
geom.setStripCounts(sc);
geom.setPrimitive(gi.TRIANGLE_FAN_ARRAY);
} // End of convertTriToFan()
// The original data was in strips and we converted it to triangles to
// calculate the normals. Now we are converting it back to strips.
// We have already calculated the new stripCounts, so now we need
// to change the index lists so they match up with the stripCounts.
// It's a very simple algorithm. The paramater oldList is the
// index list being compressed back into strips (could be coordinate,
// color, normal, or texCoord indices) and numVerts is the pre-
// calculated total of all entries of the stripCounts array.
private int[] triToStripIndices(int sc[], int oldList[], int numVerts)
{
if (oldList == null) return null;
int newList[] = new int[numVerts]; // index list to pass back
int vert1 = 0; // 1st vertex of triangle
int n = 0; // index into newList
// Cycle through each strip in the new list
for (int f = 0 ; f < sc.length ; f++) {
// Copy entire first triangle into new array
newList[n++] = oldList[vert1++];
newList[n++] = oldList[vert1++];
newList[n++] = oldList[vert1++];
// Each additional triangle in the fan only needs one vertex
for (int t = 3 ; t < sc[f] ; t++) {
// Every other triangle has been reversed to preserve winding
newList[n++] = oldList[vert1 + 2 - (t % 2)];
vert1 += 3;
}
}
return newList;
} // End of triToStripIndices
private void convertTriToStrip(GeometryInfo geom, int oldStripCounts[])
{
int ni[] = geom.getNormalIndices();
//
// Calculate new stripCounts array
//
int tri = 0; // Which triangle currently being converted
ArrayList newStripCounts;
newStripCounts = new ArrayList(oldStripCounts.length + 100);
// Use the original stripCounts array
for (int f = 0 ; f < oldStripCounts.length ; f++) {
int stripCount = 3;
// Cycle through each triangle in the strip, comparing to the
// next triangle in the strip. Compare the normal indices of
// both vertices of the edge to see if the two triangles
// can be mated.
for (int t = 0 ; t < oldStripCounts[f] - 3 ; t++) {
// Every other triangle has been reversed to preserve winding
if (t % 2 == 0) {
// The middle vertex of this triangle needs to match the
// first vertex of the next, AND the third vertices of
// the two triangles must match
if ((ni[tri * 3 + 1] == ni[(tri+1) * 3]) &&
(ni[tri * 3 + 2] == ni[(tri+1) * 3 + 2])) {
// OK to extend strip
stripCount++;
} else {
// hard edge within strip
newStripCounts.add(new Integer(stripCount));
stripCount = 3;
// Can't start a new strip on an odd edge so output
// isolated triangle
if (t < oldStripCounts[f] - 4) {
newStripCounts.add(new Integer(3));
t++;
}
}
} else {
// The middle vertex of this triangle must match the middle
// vertex of the next, AND the third vertex of this triangle
// must match the first vertex of the next
if ((ni[tri * 3 + 1] == ni[(tri+1) * 3 + 1]) &&
(ni[tri * 3 + 2] == ni[(tri+1) * 3])) {
// OK to extend strip
stripCount++;
} else {
// hard edge within strip
newStripCounts.add(new Integer(stripCount));
stripCount = 3;
}
}
tri++;
}
tri++;
newStripCounts.add(new Integer(stripCount));
}
// Convert from ArrayList to int[]
int sc[] = new int[newStripCounts.size()];
for (int i = 0 ; i < sc.length ; i++)
sc[i] = ((Integer)newStripCounts.get(i)).intValue();
newStripCounts = null;
//
// Change the index lists so they match up with the new stripCounts
//
// See how many vertices we'll need
int c = 0;
for (int i = 0 ; i < sc.length ; i++) c += sc[i];
// Create the new arrays
geom.setCoordinateIndices(
triToStripIndices(sc, geom.getCoordinateIndices(), c));
geom.setColorIndices(triToStripIndices(sc, geom.getColorIndices(), c));
geom.setNormalIndices(triToStripIndices(sc, geom.getNormalIndices(), c));
int num = geom.getTexCoordSetCount();
for (int i = 0 ; i < num ; i++) {
geom.setTextureCoordinateIndices(i,
triToStripIndices(sc, geom.getTextureCoordinateIndices(i), c));
}
if ((DEBUG & 8) != 0) {
System.out.print("Old stripCounts:");
for (int i = 0 ; i < oldStripCounts.length ; i++) {
System.out.print(" " + oldStripCounts[i]);
}
System.out.println();
System.out.print("New stripCounts:");
for (int i = 0 ; i < sc.length ; i++) {
System.out.print(" " + sc[i]);
}
System.out.println();
}
geom.setStripCounts(sc);
geom.setPrimitive(gi.TRIANGLE_STRIP_ARRAY);
}// End of convertTriToStrip()
/**
* Used when the user calls the NormalGenerator and not
* the Stripifier or Triangulator. We had to convert
* the user's data to indexed triangles before we could
* generate normals, so now we need to switch back to
* the original format.
*/
void convertBackToOldPrim(GeometryInfo geom, int oldPrim,
int oldStripCounts[])
{
if (oldPrim == geom.TRIANGLE_ARRAY) return;
switch (oldPrim) {
case GeometryInfo.QUAD_ARRAY:
convertTriToQuad(geom);
break;
case GeometryInfo.TRIANGLE_FAN_ARRAY:
convertTriToFan(geom, oldStripCounts);
break;
case GeometryInfo.TRIANGLE_STRIP_ARRAY:
convertTriToStrip(geom, oldStripCounts);
break;
}
if ((DEBUG & 64) != 0) {
System.out.println("Coordinate and normal indices (original):");
for (int i = 0 ; i < coordInds.length ; i++) {
System.out.println(i + " " + coordInds[i] + " " + normalInds[i]);
}
}
} // End of convertBackToOldPrim
/**
* Generate normals for the GeometryInfo object. If the GeometryInfo
* object didn't previously contain indexed data, indexes are made
* by collapsing identical positions into a single index. Any
* normal information previously contained in the GeometryInfo
* object is lost. Strips and Fans are converted into individual
* triangles for Normal generation, but are stitched back together
* if GeometryInfo.getGeometryArray() (or getIndexedGeometryArray())
* is called without stripifying first.
*/
public void generateNormals(GeometryInfo geom)
{
gi = geom;
gi.setNormals((Vector3f[])null);
gi.setNormalIndices(null);
long time = 0L;
if ((DEBUG & 16) != 0) {
time = System.currentTimeMillis();
}
if (gi.getPrimitive() == gi.POLYGON_ARRAY) {
if (tr == null) tr = new Triangulator();
tr.triangulate(gi);
} else {
// NOTE: We should calculate facet normals before converting to
// triangles.
gi.rememberOldPrim();
gi.convertToIndexedTriangles();
}
// Cache some of the GeometryInfo fields
coordInds = gi.getCoordinateIndices();
colorInds = gi.getColorIndices();
normalInds = gi.getNormalIndices();
numTexSets = gi.getTexCoordSetCount();
texInds = new int[numTexSets][];
for (int i = 0 ; i < numTexSets ; i++) {
texInds[i] = gi.getTextureCoordinateIndices(i);
}
stripCounts = gi.getStripCounts();
if ((DEBUG & 16) != 0) {
t1 += System.currentTimeMillis() - time;
System.out.println("Convert to triangles: " + t1 + " ms");
time = System.currentTimeMillis();
}
calculatefacetNorms();
if ((DEBUG & 16) != 0) {
t2 += System.currentTimeMillis() - time;
System.out.println("Calculate Facet Normals: " + t2 + " ms");
time = System.currentTimeMillis();
}
int maxShare = createHardEdges();
if ((DEBUG & 16) != 0) {
t3 += System.currentTimeMillis() - time;
System.out.println("Hard Edges: " + t3 + " ms");
time = System.currentTimeMillis();
}
calculateVertexNormals(maxShare);
if ((DEBUG & 16) != 0) {
t5 += System.currentTimeMillis() - time;
System.out.println("Vertex Normals: " + t5 + " ms");
time = System.currentTimeMillis();
}
if ((DEBUG & 64) != 0) {
System.out.println("Coordinate and normal indices (triangles):");
for (int i = 0 ; i < coordInds.length ; i++) {
System.out.println(i + " " + coordInds[i] + " " + normalInds[i]);
}
}
// We have been caching some info from the GeometryInfo, so we need
// to update it.
gi.setCoordinateIndices(coordInds);
gi.setColorIndices(colorInds);
gi.setNormalIndices(normalInds);
for (int i = 0 ; i < numTexSets ; i++) {
gi.setTextureCoordinateIndices(i, texInds[i]);
}
gi.setStripCounts(stripCounts);
} // End of generateNormals
/**
* Set the crease angle.
* If two triangles' normals differ by more than
* creaseAngle, then the vertex will get two separate normals, creating a
* discontinuous crease in the model. This is perfect for the edge
* of a table or the corner of a cube, for instance. Clamped to
* 0 <= creaseAngle <= PI. Optimizations are made for creaseAngle == 0
* (facet normals) and creaseAngle == PI (smooth shading).
*/
public void setCreaseAngle(double radians)
{
if (radians > Math.PI) radians = Math.PI;
if (radians < 0.0) radians = 0.0;
creaseAngle = radians;
} // End of setCreaseAngle
/**
* Returns the current value of the crease angle, in radians.
*/
public double getCreaseAngle()
{
return creaseAngle;
} // End of getCreaseAngle
/**
* Constructor. Construct a NormalGenerator object with creaseAngle
* set to the given value.
*/
public NormalGenerator(double radians)
{
creaseAngle = radians;
} // End of NormalGenerator(double)
/**
* Constructor. Construct a NormalGenerator object with creaseAngle
* set to 44 degrees (0.767944871 radians).
*/
public NormalGenerator()
{
this(44.0 * Math.PI / 180.0);
} // End of NormalGenerator()
} // End of class NormalGenerator
// End of file NormalGenerator.java
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Desperate.java 0000644 0000000 0000000 00000032666 10563126522 030232 0 ustar root root /*
* $RCSfile: Desperate.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:18 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import java.io.*;
import java.util.*;
import javax.vecmath.*;
class Desperate {
/**
* the functions in this file try to ensure that we always end up with
* something that (topologically) is a triangulation.
*
* the more desperate we get, the more aggressive means we choose for making
* diagonals "valid".
*/
static boolean desperate(Triangulator triRef, int ind, int i, boolean[] splitted) {
int[] i1 = new int[1];
int[] i2 = new int[1];
int[] i3 = new int[1];
int[] i4 = new int[1];
int[] ind1 = new int[1];
int[] ind2 = new int[1];
int[] ind3 = new int[1];
int[] ind4 = new int[1];
splitted[0] = false;
// check whether there exist consecutive vertices i1, i2, i3, i4 such
// that i1, i2 and i3, i4 intersect
if (existsCrossOver(triRef, ind, ind1, i1, ind2, i2, ind3, i3, ind4, i4)) {
// insert two new diagonals around the cross-over without checking
// whether they are intersection-free
handleCrossOver(triRef, ind1[0], i1[0], ind2[0], i2[0], ind3[0], i3[0],
ind4[0], i4[0]);
return false;
}
NoHash.prepareNoHashEdges(triRef, i, i+1);
// check whether there exists a valid diagonal that splits the polygon
// into two parts
if (existsSplit(triRef, ind, ind1, i1, ind2, i2)) {
// break up the polygon by inserting this diagonal (which can't be an
// ear -- otherwise, we would not have ended up in this part of the
// code). then, let's treat the two polygons separately. hopefully,
// this will help to handle self-overlapping polygons in the "correct"
// way.
handleSplit(triRef, ind1[0], i1[0], ind2[0], i2[0]);
splitted[0] = true;
return false;
}
return true;
}
static boolean existsCrossOver(Triangulator triRef, int ind, int[] ind1, int[] i1,
int[] ind2, int[] i2, int[] ind3, int[] i3,
int[] ind4, int[] i4) {
BBox bb1, bb2;
ind1[0] = ind;
i1[0] = triRef.fetchData(ind1[0]);
ind2[0] = triRef.fetchNextData(ind1[0]);
i2[0] = triRef.fetchData(ind2[0]);
ind3[0] = triRef.fetchNextData(ind2[0]);
i3[0] = triRef.fetchData(ind3[0]);
ind4[0] = triRef.fetchNextData(ind3[0]);
i4[0] = triRef.fetchData(ind4[0]);
do {
bb1 = new BBox(triRef, i1[0], i2[0]);
bb2 = new BBox(triRef, i3[0], i4[0]);
if (bb1.BBoxOverlap(bb2)) {
if (Numerics.segIntersect(triRef, bb1.imin, bb1.imax, bb2.imin, bb2.imax, -1))
return true;
}
ind1[0] = ind2[0];
i1[0] = i2[0];
ind2[0] = ind3[0];
i2[0] = i3[0];
ind3[0] = ind4[0];
i3[0] = i4[0];
ind4[0] = triRef.fetchNextData(ind3[0]);
i4[0] = triRef.fetchData(ind4[0]);
} while (ind1[0] != ind);
return false;
}
static void handleCrossOver(Triangulator triRef, int ind1, int i1, int ind2,
int i2, int ind3, int i3, int ind4, int i4) {
double ratio1, ratio4;
boolean first;
int angle1, angle4;
// which pair of triangles shall I insert?? we can use either i1, i2, i3
// and i1, i3, i4, or we can use i2, i3, i4 and i1, i2, i4...
angle1 = triRef.getAngle(ind1);
angle4 = triRef.getAngle(ind4);
if (angle1 < angle4) first = true;
else if (angle1 > angle4) first = false;
else if (triRef.earsSorted) {
ratio1 = Numerics.getRatio(triRef, i3, i4, i1);
ratio4 = Numerics.getRatio(triRef, i1, i2, i4);
if (ratio4 < ratio1) first = false;
else first = true;
}
else {
first = true;
}
if (first) {
// first clip i1, i2, i3, then clip i1, i3, i4
triRef.deleteLinks(ind2);
// StoreTriangle(GetOriginal(ind1), GetOriginal(ind2), GetOriginal(ind3));
triRef.storeTriangle(ind1, ind2, ind3);
triRef.setAngle(ind3, 1);
Heap.insertIntoHeap(triRef, 0.0, ind3, ind1, ind4);
}
else {
// first clip i2, i3, i4, then clip i1, i2, i4
triRef.deleteLinks(ind3);
//StoreTriangle(GetOriginal(ind2), GetOriginal(ind3), GetOriginal(ind4));
triRef.storeTriangle(ind2, ind3, ind4);
triRef.setAngle(ind2, 1);
Heap.insertIntoHeap(triRef, 0.0, ind2, ind1, ind4);
}
}
static boolean letsHope(Triangulator triRef, int ind) {
int ind0, ind1, ind2;
int i0, i1, i2;
// let's clip the first convex corner. of course, we know that this is no
// ear in an ideal world. but this polygon isn't ideal, either!
ind1 = ind;
i1 = triRef.fetchData(ind1);
do {
if (triRef.getAngle(ind1) > 0) {
ind0 = triRef.fetchPrevData(ind1);
i0 = triRef.fetchData(ind0);
ind2 = triRef.fetchNextData(ind1);
i2 = triRef.fetchData(ind2);
Heap.insertIntoHeap(triRef, 0.0, ind1, ind0, ind2);
return true;
}
ind1 = triRef.fetchNextData(ind1);
i1 = triRef.fetchData(ind1);
} while (ind1 != ind);
// no convex corners? so, let's cheat! this code won't stop without some
// triangulation... ;-) g-i-g-o? right! perhaps, this is what you
// call a robust code?!
triRef.setAngle(ind, 1);
ind0 = triRef.fetchPrevData(ind);
i0 = triRef.fetchData(ind0);
ind2 = triRef.fetchNextData(ind);
i2 = triRef.fetchData(ind2);
Heap.insertIntoHeap(triRef, 0.0, ind, ind0, ind2);
i1 = triRef.fetchData(ind);
return true;
// see, we never have to return "false"...
/*
return false;
*/
}
static boolean existsSplit(Triangulator triRef, int ind, int[] ind1, int[] i1,
int[] ind2, int[] i2) {
int ind3, ind4, ind5;
int i3, i4, i5;
if (triRef.numPoints > triRef.maxNumDist) {
// System.out.println("Desperate: Expanding distances array ...");
triRef.maxNumDist = triRef.numPoints;
triRef.distances = new Distance[triRef.maxNumDist];
for (int k = 0; k < triRef.maxNumDist; k++)
triRef.distances[k] = new Distance();
}
ind1[0] = ind;
i1[0] = triRef.fetchData(ind1[0]);
ind4 = triRef.fetchNextData(ind1[0]);
i4 = triRef.fetchData(ind4);
// assert(*ind1 != ind4);
ind5 = triRef.fetchNextData(ind4);
i5 = triRef.fetchData(ind5);
// assert(*ind1 != *ind2);
ind3 = triRef.fetchPrevData(ind1[0]);
i3 = triRef.fetchData(ind3);
// assert(*ind2 != ind3);
if (foundSplit(triRef, ind5, i5, ind3, ind1[0], i1[0], i3, i4, ind2, i2))
return true;
i3 = i1[0];
ind1[0] = ind4;
i1[0] = i4;
ind4 = ind5;
i4 = i5;
ind5 = triRef.fetchNextData(ind4);
i5 = triRef.fetchData(ind5);
while (ind5 != ind) {
if (foundSplit(triRef, ind5, i5, ind, ind1[0], i1[0], i3, i4, ind2, i2))
return true;
i3 = i1[0];
ind1[0] = ind4;
i1[0] = i4;
ind4 = ind5;
i4 = i5;
ind5 = triRef.fetchNextData(ind4);
i5 = triRef.fetchData(ind5);
}
return false;
}
/**
* This function computes the winding number of a polygon with respect to a
* point p. no care is taken to handle cases where p lies on the
* boundary of the polygon. (this is no issue in our application, as we will
* always compute the winding number with respect to the mid-point of a
* valid diagonal.)
*/
static int windingNumber(Triangulator triRef, int ind, Point2f p) {
double angle;
int ind2;
int i1, i2, number;
i1 = triRef.fetchData(ind);
ind2 = triRef.fetchNextData(ind);
i2 = triRef.fetchData(ind2);
angle = Numerics.angle(triRef, p, triRef.points[i1], triRef.points[i2]);
while (ind2 != ind) {
i1 = i2;
ind2 = triRef.fetchNextData(ind2);
i2 = triRef.fetchData(ind2);
angle += Numerics.angle(triRef, p, triRef.points[i1], triRef.points[i2]);
}
angle += Math.PI;
number = (int)(angle / (Math.PI*2.0));
return number;
}
static boolean foundSplit(Triangulator triRef, int ind5, int i5, int ind, int ind1,
int i1, int i3, int i4, int[] ind2, int[] i2) {
Point2f center;
int numDist = 0;
int j, i6, i7;
int ind6, ind7;
BBox bb;
boolean convex, coneOk;
// Sort the points according to their distance from i1
do {
// assert(numDist < triRef.maxNumDist);
triRef.distances[numDist].dist = Numerics.baseLength(triRef.points[i1],
triRef.points[i5]);
triRef.distances[numDist].ind = ind5;
++numDist;
ind5 = triRef.fetchNextData(ind5);
i5 = triRef.fetchData(ind5);
} while (ind5 != ind);
Bridge.sortDistance(triRef.distances, numDist);
// find a valid diagonal.
for (j = 0; j < numDist; ++j) {
ind2[0] = triRef.distances[j].ind;
i2[0] = triRef.fetchData(ind2[0]);
if (i1 != i2[0]) {
ind6 = triRef.fetchPrevData(ind2[0]);
i6 = triRef.fetchData(ind6);
ind7 = triRef.fetchNextData(ind2[0]);
i7 = triRef.fetchData(ind7);
convex = triRef.getAngle(ind2[0]) > 0;
coneOk = Numerics.isInCone(triRef, i6, i2[0], i7, i1, convex);
if (coneOk) {
convex = triRef.getAngle(ind1) > 0;
coneOk = Numerics.isInCone(triRef, i3, i1, i4, i2[0], convex);
if (coneOk) {
bb = new BBox(triRef, i1, i2[0]);
if (!NoHash.noHashEdgeIntersectionExists(triRef, bb, -1, -1, ind1, -1)) {
// check whether this is a good diagonal; we do not want a
// diagonal that may create figure-8's!
center = new Point2f();
Basic.vectorAdd2D(triRef.points[i1], triRef.points[i2[0]], center);
Basic.multScalar2D(0.5, center);
if (windingNumber(triRef, ind, center) == 1) return true;
}
}
}
}
}
return false;
}
static void handleSplit(Triangulator triRef, int ind1, int i1, int ind3, int i3) {
int ind2, ind4, prev, next;
int prv, nxt, angle;
int vIndex, comIndex = -1;
// duplicate nodes in order to form end points of the new diagonal
ind2 = triRef.makeNode(i1);
triRef.insertAfter(ind1, ind2);
// Need to get the original data, before setting it.
comIndex = triRef.list[ind1].getCommonIndex();
triRef.list[ind2].setCommonIndex(comIndex);
ind4 = triRef.makeNode(i3);
triRef.insertAfter(ind3, ind4);
comIndex = triRef.list[ind3].getCommonIndex();
triRef.list[ind4].setCommonIndex(comIndex);
// insert the diagonal into the boundary loop, thus splitting the loop
// into two loops
triRef.splitSplice(ind1, ind2, ind3, ind4);
// store pointers to the two new loops
triRef.storeChain(ind1);
triRef.storeChain(ind3);
// reset the angles
next = triRef.fetchNextData(ind1);
nxt = triRef.fetchData(next);
prev = triRef.fetchPrevData(ind1);
prv = triRef.fetchData(prev);
angle = Numerics.isConvexAngle(triRef, prv, i1, nxt, ind1);
triRef.setAngle(ind1, angle);
next = triRef.fetchNextData(ind2);
nxt = triRef.fetchData(next);
prev = triRef.fetchPrevData(ind2);
prv = triRef.fetchData(prev);
angle = Numerics.isConvexAngle(triRef, prv, i1, nxt, ind2);
triRef.setAngle(ind2, angle);
next = triRef.fetchNextData(ind3);
nxt = triRef.fetchData(next);
prev = triRef.fetchPrevData(ind3);
prv = triRef.fetchData(prev);
angle = Numerics.isConvexAngle(triRef, prv, i3, nxt, ind3);
triRef.setAngle(ind3, angle);
next = triRef.fetchNextData(ind4);
nxt = triRef.fetchData(next);
prev = triRef.fetchPrevData(ind4);
prv = triRef.fetchData(prev);
angle = Numerics.isConvexAngle(triRef, prv, i3, nxt, ind4);
triRef.setAngle(ind4, angle);
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/HeapNode.java 0000644 0000000 0000000 00000005604 10563126523 027772 0 ustar root root /*
* $RCSfile: HeapNode.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:19 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
class HeapNode {
int index, prev, next;
double ratio;
HeapNode() {
}
void copy(HeapNode hNode) {
index = hNode.index;
prev = hNode.prev;
next = hNode.next;
ratio = hNode.ratio;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/BottleNeck.java 0000644 0000000 0000000 00000012371 10563126521 030336 0 ustar root root /*
* $RCSfile: BottleNeck.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:17 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import java.io.*;
import java.util.*;
import javax.vecmath.*;
class BottleNeck {
static boolean checkArea(Triangulator triRef, int ind4, int ind5) {
int ind1, ind2;
int i0, i1, i2;
double area = 0.0, area1 = 0, area2 = 0.0;
i0 = triRef.fetchData(ind4);
ind1 = triRef.fetchNextData(ind4);
i1 = triRef.fetchData(ind1);
while (ind1 != ind5) {
ind2 = triRef.fetchNextData(ind1);
i2 = triRef.fetchData(ind2);
area =Numerics.stableDet2D(triRef, i0, i1, i2);
area1 += area;
ind1 = ind2;
i1 = i2;
}
if (Numerics.le(area1, triRef.ZERO)) return false;
ind1 = triRef.fetchNextData(ind5);
i1 = triRef.fetchData(ind1);
while (ind1 != ind4) {
ind2 = triRef.fetchNextData(ind1);
i2 = triRef.fetchData(ind2);
area = Numerics.stableDet2D(triRef, i0, i1, i2);
area2 += area;
ind1 = ind2;
i1 = i2;
}
if (Numerics.le(area2, triRef.ZERO)) return false;
else return true;
}
// Yet another check needed in order to handle degenerate cases!
static boolean checkBottleNeck(Triangulator triRef,
int i1, int i2, int i3, int ind4) {
int ind5;
int i4, i5;
boolean flag;
i4 = i1;
ind5 = triRef.fetchPrevData(ind4);
i5 = triRef.fetchData(ind5);
if ((i5 != i2) && (i5 != i3)) {
flag = Numerics.pntInTriangle(triRef, i1, i2, i3, i5);
if (flag) return true;
}
if (i2 <= i3) {
if (i4 <= i5) flag = Numerics.segIntersect(triRef, i2, i3, i4, i5, -1);
else flag = Numerics.segIntersect(triRef, i2, i3, i5, i4, -1);
}
else {
if (i4 <= i5) flag = Numerics.segIntersect(triRef, i3, i2, i4, i5, -1);
else flag = Numerics.segIntersect(triRef, i3, i2, i5, i4, -1);
}
if (flag) return true;
ind5 = triRef.fetchNextData(ind4);
i5 = triRef.fetchData(ind5);
if ((i5 != i2) && (i5 != i3)) {
flag = Numerics.pntInTriangle(triRef, i1, i2, i3, i5);
if (flag) return true;
}
if (i2 <= i3) {
if (i4 <= i5) flag = Numerics.segIntersect(triRef, i2, i3, i4, i5, -1);
else flag = Numerics.segIntersect(triRef, i2, i3, i5, i4, -1);
}
else {
if (i4 <= i5) flag = Numerics.segIntersect(triRef, i3, i2, i4, i5, -1);
else flag = Numerics.segIntersect(triRef, i3, i2, i5, i4, -1);
}
if (flag) return true;
ind5 = triRef.fetchNextData(ind4);
i5 = triRef.fetchData(ind5);
while (ind5 != ind4) {
if (i4 == i5) {
if (checkArea(triRef, ind4, ind5)) return true;
}
ind5 = triRef.fetchNextData(ind5);
i5 = triRef.fetchData(ind5);
}
return false;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Quadrics.java 0000644 0000000 0000000 00000047042 10613312335 030056 0 ustar root root /*
* $RCSfile: Quadrics.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.6 $
* $Date: 2007/04/24 05:25:49 $
* $State: Exp $
*/
package com.sun.j3d.utils.geometry;
import com.sun.j3d.utils.geometry.*;
import java.io.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.math.*;
class Quadrics extends Object {
Quadrics(){ }
// new disk code to remove transforms in the primitive code
GeomBuffer disk(double r, int xdiv, double y, boolean outside, boolean texCoordYUp) {
double theta, dtheta, sign, sinTheta, cosTheta;
if (outside) sign = 1.0;
else sign = -1.0;
dtheta = 2.0*Math.PI / xdiv;
GeomBuffer gbuf = new GeomBuffer(xdiv+2);
gbuf.begin(GeomBuffer.TRIANGLE_FAN);
gbuf.normal3d(0.0, 1.0*sign, 0.0);
gbuf.texCoord2d(0.5, 0.5);
gbuf.vertex3d(0.0, y, 0.0);
// create the disk by evaluating points along the unit circle.
// theta is the angle around the y-axis. Then we obtain
// (cos(theta), sin(theta)) = (x,z) sample points. The y value
// was passed in as a parameter.
// texture coordinates are obtain from the unit circle centered at
// (.5, .5) in s, t space. thus portions of the texture are not used.
if (!outside) {
for (int i = 0; i <= xdiv; i++) {
theta = i * dtheta;
// add 90 degrees to theta so lines up wtih the body
sinTheta = Math.sin(theta - Math.PI/2.0);
cosTheta = Math.cos(theta - Math.PI/2.0);
gbuf.normal3d(0.0, 1.0*sign, 0.0);
if (texCoordYUp) {
gbuf.texCoord2d(0.5+cosTheta*0.5, 1.0 - (0.5+sinTheta*0.5));
}
else {
gbuf.texCoord2d(0.5+cosTheta*0.5, 0.5+sinTheta*0.5);
}
gbuf.vertex3d(r*cosTheta, y, r*sinTheta);
}
} else {
for (int i = xdiv; i >= 0; i--) {
theta = i * dtheta;
// add 90 degrees to theta so lines up with the body
sinTheta = Math.sin(theta - Math.PI/2.0);
cosTheta = Math.cos(theta - Math.PI/2.0);
gbuf.normal3d(0.0, 1.0*sign, 0.0);
if (texCoordYUp) {
gbuf.texCoord2d(0.5+cosTheta*0.5, 1.0 - (0.5-sinTheta*0.5));
}
else {
gbuf.texCoord2d(0.5+cosTheta*0.5, 0.5-sinTheta*0.5);
}
gbuf.vertex3d(cosTheta*r, y, sinTheta*r);
}
}
gbuf.end();
return gbuf;
}
// new cylinder to remove transforms in the cylinder code and to optimize
// by using triangle strip
GeomBuffer cylinder(double height, double radius,
int xdiv, int ydiv, boolean outside, boolean texCoordYUp) {
double sign;
if (outside) sign = 1.0;
else sign = -1.0;
// compute the deltas
double dtheta = 2.0*Math.PI / (double)xdiv;
double dy = height / (double)ydiv;
double du = 1.0/(double)xdiv;
double dv = 1.0/(double)ydiv;
GeomBuffer gbuf = new GeomBuffer(ydiv*2*(xdiv+1));
double s = 0.0, t = 0.0;
double px, pz, qx, qz;
double py = -height/2.0;
double qy;
gbuf.begin(GeomBuffer.QUAD_STRIP);
for (int i = 0; i < ydiv; i++) {
qy = py+dy;
if (outside) {
px = Math.cos(xdiv*dtheta - Math.PI/2.0);
pz = Math.sin(xdiv*dtheta - Math.PI/2.0);
qx = Math.cos((xdiv-1)*dtheta - Math.PI/2.0);
qz = Math.sin((xdiv-1)*dtheta - Math.PI/2.0);
// vert 2
gbuf.normal3d(px*sign, 0.0, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s, t+dv);
}
gbuf.vertex3d(px*radius, qy, pz*radius);
// vert 1
gbuf.normal3d(px*sign, 0.0, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - t);
} else {
gbuf.texCoord2d(s, t);
}
gbuf.vertex3d(px*radius, py, pz*radius);
// vert 4
gbuf.normal3d(qx*sign, 0.0, qz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s+du, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s+du, t+dv);
}
gbuf.vertex3d(qx*radius, qy, qz*radius);
// vert 3
gbuf.normal3d(qx*sign, 0.0, qz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s+du, 1.0 - t);
}
else {
gbuf.texCoord2d(s+du, t);
}
gbuf.vertex3d(qx*radius, py, qz*radius);
s += (du*2.0);
for (int j = xdiv-2; j >=0; j--) {
px = Math.cos(j*dtheta - Math.PI/2.0);
pz = Math.sin(j*dtheta - Math.PI/2.0);
// vert 6
gbuf.normal3d(px*sign, 0.0, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s, t+dv);
}
gbuf.vertex3d(px*radius, qy, pz*radius);
// vert 5
gbuf.normal3d(px*sign, 0.0, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - t);
}
else {
gbuf.texCoord2d(s, t);
}
gbuf.vertex3d(px*radius, py, pz*radius);
s += du;
}
} else {
// c = 0;
px = Math.cos(-Math.PI/2.0);
pz = Math.sin(-Math.PI/2.0);
qx = Math.cos(dtheta - Math.PI/2.0);
qz = Math.sin(dtheta - Math.PI/2.0);
gbuf.normal3d(px*sign, 0.0, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s, t+dv);
}
gbuf.vertex3d(px*radius, qy, pz*radius);
// vert 1
gbuf.normal3d(px*sign, 0.0, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - t);
}
else {
gbuf.texCoord2d(s, t);
}
gbuf.vertex3d(px*radius, py, pz*radius);
gbuf.normal3d(qx*sign, 0.0, qz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s+du, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s+du, t+dv);
}
gbuf.vertex3d(qx*radius, qy, qz*radius);
gbuf.normal3d(qx*sign, 0.0, qz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s+du, 1.0 - t);
}
else {
gbuf.texCoord2d(s+du, t);
}
gbuf.vertex3d(qx*radius, py, qz*radius);
s += (du*2.0);
for (int j = 2; j <= xdiv; j++) {
px = Math.cos(j*dtheta - Math.PI/2.0);
pz = Math.sin(j*dtheta - Math.PI/2.0);
gbuf.normal3d(px*sign, 0.0, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s, t+dv);
}
gbuf.vertex3d(px*radius, qy, pz*radius);
gbuf.normal3d(px*sign, 0.0, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - t);
}
else {
gbuf.texCoord2d(s, t);
}
gbuf.vertex3d(px*radius, py, pz*radius);
s += du;
}
}
s = 0.0;
t += dv;
py += dy;
}
gbuf.end();
return gbuf;
}
// new coneBody method to remove transform in the Cone primitive
// and to optimize by using triangle strip
GeomBuffer coneBody(double bottom, double top, double bottomR, double topR,
int xdiv, int ydiv, double dv, boolean outside, boolean texCoordYUp) {
double r, sign;
if (outside) sign = 1.0;
else sign = -1.0;
// compute the deltas
double dtheta = 2.0*Math.PI/(double)xdiv;
double dr = (topR-bottomR)/(double)ydiv;
double height = top-bottom;
double dy = height/(double)ydiv;
double ynormal = (bottomR-topR)/height;
double du = 1.0/(double)xdiv;
// double dv = 1.0/(double)(ydiv+1);
GeomBuffer gbuf = new GeomBuffer(ydiv*2*(xdiv+1));
double s = 0.0, t = 0.0;
double px, pz, qx, qz;
double py = bottom;
double qy;
r = bottomR;
gbuf.begin(GeomBuffer.QUAD_STRIP);
for (int i = 0; i < ydiv; i++) {
qy = py+dy;
if (outside) {
px = Math.cos(xdiv*dtheta - Math.PI/2.0);
pz = Math.sin(xdiv*dtheta - Math.PI/2.0);
qx = Math.cos((xdiv-1)*dtheta - Math.PI/2.0);
qz = Math.sin((xdiv-1)*dtheta - Math.PI/2.0);
// vert2
gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s, t+dv);
}
gbuf.vertex3d(px*(r+dr), qy, pz*(r+dr));
// vert1
gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - t);
}
else {
gbuf.texCoord2d(s, t);
}
gbuf.vertex3d(px*r, py, pz*r);
// vert4
gbuf.normal3d(qx*sign, ynormal*sign, qz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s+du, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s+du, t+dv);
}
gbuf.vertex3d(qx*(r+dr), qy, qz*(r+dr));
// vert3
gbuf.normal3d(qx*sign, ynormal*sign, qz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s+du, 1.0 - t);
}
else {
gbuf.texCoord2d(s+du, t);
}
gbuf.vertex3d(qx*r, py, qz*r);
s += (du*2.0);
for (int j = xdiv-2; j >= 0; j--) {
px = Math.cos(j*dtheta - Math.PI/2.0);
pz = Math.sin(j*dtheta - Math.PI/2.0);
// vert 6
gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - (t + dv));
} else {
gbuf.texCoord2d(s, t+dv);
}
gbuf.vertex3d(px*(r+dr), qy, pz*(r+dr));
// vert 5
gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - t);
}
else {
gbuf.texCoord2d(s, t);
}
gbuf.vertex3d(px*r, py, pz*r);
s += du;
}
} else {
px = Math.cos(-Math.PI/2.0);
pz = Math.sin(-Math.PI/2.0);
qx = Math.cos(dtheta - Math.PI/2.0);
qz = Math.sin(dtheta - Math.PI/2.0);
// vert1
gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s, t+dv);
}
gbuf.vertex3d(px*(r+dr), qy, pz*(r+dr));
gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - t);
}
else {
gbuf.texCoord2d(s, t);
}
gbuf.vertex3d(px*r, py, pz*r);
gbuf.normal3d(qx*sign, ynormal*sign, qz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s+du, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s+du, t+dv);
}
gbuf.vertex3d(qx*(r+dr), qy, qz*(r+dr));
gbuf.normal3d(qx*sign, ynormal*sign, qz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s+du, 1.0 - t);
}
else {
gbuf.texCoord2d(s+du, t);
}
gbuf.vertex3d(qx*r, py, qz*r);
s += (du*2.0);
for (int j = 2; j <= xdiv; j++) {
px = Math.cos(j*dtheta - Math.PI/2.0);
pz = Math.sin(j*dtheta - Math.PI/2.0);
gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - (t + dv));
}
else {
gbuf.texCoord2d(s, t+dv);
}
gbuf.vertex3d(px*(r+dr), qy, pz*(r+dr));
gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - t);
}
else {
gbuf.texCoord2d(s, t);
}
gbuf.vertex3d(px*r, py, pz*r);
s += du;
}
}
s = 0.0;
t += dv;
py += dy;
r += dr;
}
gbuf.end();
return gbuf;
}
// new coneTop method to remove transforms in the cone code
GeomBuffer coneTop(double bottom, double radius, double height,
int xdiv,double t, boolean outside, boolean texCoordYUp) {
double sign;
if (outside) sign = 1.0;
else sign = -1.0;
// compute the deltas
double dtheta = 2.0*Math.PI/(double)xdiv;
double ynormal = radius/height;
double du = 1.0/(double)xdiv;
double top = bottom + height;
// initialize the geometry buffer
GeomBuffer gbuf = new GeomBuffer(xdiv + 2);
gbuf.begin(GeomBuffer.TRIANGLE_FAN);
// add the tip, which is the center of the fan
gbuf.normal3d(0.0, ynormal*sign, 0.0);
if (texCoordYUp) {
gbuf.texCoord2d(.5, 0.0);
}
else {
gbuf.texCoord2d(.5, 1.0);
}
gbuf.vertex3d(0.0, top, 0.0);
// go around the circle and add the rest of the fan
double s = 0.0;
double px, pz;
if (outside) {
for (int i = xdiv; i >= 0; i--) {
px = Math.cos(i*dtheta - Math.PI/2.0);
pz = Math.sin(i*dtheta - Math.PI/2.0);
gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - t);
} else {
gbuf.texCoord2d(s, t);
}
gbuf.vertex3d(px*radius, bottom, pz*radius);
s += du;
}
} else {
for (int i = 0; i <= xdiv; i++) {
px = Math.cos(i*dtheta - Math.PI/2.0);
pz = Math.sin(i*dtheta - Math.PI/2.0);
gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
if (texCoordYUp) {
gbuf.texCoord2d(s, 1.0 - t);
} else {
gbuf.texCoord2d(s, t);
}
gbuf.vertex3d(px*radius, bottom, pz*radius);
s += du;
}
}
gbuf.end();
return gbuf;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Cone.java 0000644 0000000 0000000 00000034660 10613450623 027174 0 ustar root root /*
* $RCSfile: Cone.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.6 $
* $Date: 2007/04/24 18:50:59 $
* $State: Exp $
*/
package com.sun.j3d.utils.geometry;
import com.sun.j3d.utils.geometry.*;
import java.io.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;
/**
* Cone is a geometry primitive defined with a radius and a height.
* It is a capped cone centered at the origin with its central axis
* aligned along the Y-axis. The center of the cone is defined to be
* the center of its bounding box (rather than its centroid).
*
* If the GENERATE_TEXTURE_COORDS flag is set, the texture coordinates
* are generated such that the texture gets mapped onto the cone similarly
* to how it gets mapped onto a cylinder, the difference being the top
* cap is nonexistent.
*
* By default all primitives with the same parameters share their
* geometry (e.g., you can have 50 shperes in your scene, but the
* geometry is stored only once). A change to one primitive will
* effect all shared nodes. Another implication of this
* implementation is that the capabilities of the geometry are shared,
* and once one of the shared nodes is live, the capabilities cannot
* be set. Use the GEOMETRY_NOT_SHARED flag if you do not wish to
* share geometry among primitives with the same parameters.
*/
public class Cone extends Primitive {
float radius, height;
int xdivisions, ydivisions;
static final int MID_REZ_DIV_X = 15;
static final int MID_REZ_DIV_Y = 1;
/**
* Designates the body of the cone. Used by
* If appearance is null, the default white appearance will be used.
* @param radius Radius
* @param height Height
* @param xdivision Number of divisions along X direction.
* @param ydivision Number of divisions along the height of cone.
* @param primflags flags
* @param ap Appearance
*/
public Cone(float radius, float height, int primflags,
int xdivision, int ydivision,
Appearance ap)
{
super();
Shape3D shape[] = new Shape3D[2];
this.radius = radius;
this.height = height;
xdivisions = xdivision;
ydivisions = ydivision;
flags = primflags;
boolean outside = (flags & GENERATE_NORMALS_INWARD) == 0;
boolean texCoordYUp = (flags & GENERATE_TEXTURE_COORDS_Y_UP) != 0;
Quadrics q = new Quadrics();
GeomBuffer gbuf = null;
GeomBuffer cache = getCachedGeometry(Primitive.CONE,
radius, 0.0f, height,
xdivision, ydivision, primflags);
if (cache != null){
// System.out.println("using cached geometry");
shape[BODY] = new Shape3D(cache.getComputedGeometry());
numVerts += cache.getNumVerts();
numTris += cache.getNumTris();
}
else {
// the body of the cone consists of the top of the cone and if
// ydivisions is greater than 1, the body of the cone.
gbuf = q.coneTop((double)(height/2.0 - height/ydivisions),
(double)(radius/ydivisions), height/ydivisions,
xdivisions, 1.0-1.0/(double)ydivisions,
outside, texCoordYUp);
shape[BODY] = new Shape3D(gbuf.getGeom(flags));
numVerts += gbuf.getNumVerts();
numTris += gbuf.getNumTris();
if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
cacheGeometry(Primitive.CONE,
radius, 0.0f, height,
xdivision, ydivision, primflags, gbuf);
}
}
// only need to add a body if the ydivisions is greater than 1
if (ydivisions > 1) {
cache = getCachedGeometry(Primitive.CONE_DIVISIONS, radius, 0.0f,
height, xdivision, ydivision, primflags);
if (cache != null) {
// System.out.println("using cached divisions");
shape[BODY].addGeometry(cache.getComputedGeometry());
numVerts += cache.getNumVerts();
numTris += cache.getNumTris();
}
else {
gbuf = q.coneBody(-(double)(height/2.0),
(double)(height/2.0-height/ydivisions),
(double)radius, (double)(radius/ydivisions),
xdivisions, ydivisions-1, 1.0/(double)ydivisions,
outside, texCoordYUp);
shape[BODY].addGeometry(gbuf.getGeom(flags));
numVerts += gbuf.getNumVerts();
numTris += gbuf.getNumTris();
if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
cacheGeometry(Primitive.CONE_DIVISIONS, radius, 0.0f, height,
xdivision, ydivision, primflags, gbuf);
}
}
}
if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
(shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
(shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
}
if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
(shape[BODY]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
}
this.addChild(shape[BODY]);
// Create bottom cap.
cache = getCachedGeometry(Primitive.BOTTOM_DISK, radius,
radius, -height/2.0f, xdivision, xdivision,
primflags);
if (cache != null) {
// System.out.println("using cached bottom");
shape[CAP] = new Shape3D(cache.getComputedGeometry());
numVerts += cache.getNumVerts();
numTris += cache.getNumTris();
}
else {
gbuf = q.disk((double)radius, xdivision, -(double)height/2.0,
!outside, texCoordYUp);
shape[CAP] = new Shape3D(gbuf.getGeom(flags));
numVerts += gbuf.getNumVerts();
numTris += gbuf.getNumTris();
if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
cacheGeometry(Primitive.BOTTOM_DISK, radius, radius, -height/2.0f,
xdivision, xdivision, primflags, gbuf);
}
}
if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
(shape[CAP]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
(shape[CAP]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
}
if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
(shape[CAP]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
}
// Transform3D t2 = new Transform3D();
// Flip it to match up the texture coords.
/* This causes the bottom not to match up for odd xdivision values
objectMat = new Matrix4d();
objectMat.setIdentity();
rotMat = new Matrix4d();
rotMat.setIdentity();
rotMat.rotZ(Math.PI);
objectMat.mul(objectMat, rotMat);
t2.set(objectMat);
*/
this.addChild(shape[CAP]);
if (ap == null){
setAppearance();
}
else setAppearance(ap);
}
/**
* Used to create a new instance of the node. This routine is called
* by
* For any NodeComponent objects
* contained by the object being duplicated, each NodeComponent
* object's Provides geometry construction, triangulation, and optimization
utility classes.
*
* 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.
*
* 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.
*
* 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() ;
}
././@LongLink 0000000 0000000 0000000 00000000166 00000000000 011570 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamColor.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/Compressio0000644 0000000 0000000 00000021726 10563126526 032061 0 ustar root root /*
* $RCSfile: CompressionStreamColor.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:22 $
* $State: Exp $
*/
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.
*
* 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.
*
* @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") ;
}
}
././@LongLink 0000000 0000000 0000000 00000000173 00000000000 011566 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDecompressorShape3D.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDe0000644 0000000 0000000 00000041235 10563126526 031777 0 ustar root root /*
* $RCSfile: GeometryDecompressorShape3D.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:22 $
* $State: Exp $
*/
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() ;
}
}
././@LongLink 0000000 0000000 0000000 00000000164 00000000000 011566 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDecompressor.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryDe0000644 0000000 0000000 00000112202 10563126526 031770 0 ustar root root /*
* $RCSfile: GeometryDecompressor.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:22 $
* $State: Exp $
*/
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< 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") ;
}
}
././@LongLink 0000000 0000000 0000000 00000000155 00000000000 011566 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/CommandStream.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/CommandStr0000644 0000000 0000000 00000020632 10563126525 031777 0 ustar root root /*
* $RCSfile: CommandStream.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:21 $
* $State: Exp $
*/
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.
*
* 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.
*
* 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.
*
* 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 ;
}
}
././@LongLink 0000000 0000000 0000000 00000000154 00000000000 011565 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/HuffmanTable.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/HuffmanTab0000644 0000000 0000000 00000037436 10563126527 031757 0 ustar root root /*
* $RCSfile: HuffmanTable.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:23 $
* $State: Exp $
*/
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.
*
* 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) ;
}
}
././@LongLink 0000000 0000000 0000000 00000000166 00000000000 011570 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryFile.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/Compressed0000644 0000000 0000000 00000077202 10563126525 032041 0 ustar root root /*
* $RCSfile: CompressedGeometryFile.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:21 $
* $State: Exp $
*/
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() ;
}
}
././@LongLink 0000000 0000000 0000000 00000000147 00000000000 011567 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/package.html java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/package.ht0000644 0000000 0000000 00000000636 10507260110 031722 0 ustar root root
Provides compressed geometry utility classes.
This package supersedes the javax.media.j3d.CompressedGeometry class and
the com.sun.j3d.utils.compression package.
*
* At 1 bit of quantization it is not possible to express positive
* absolute or delta positions.
*/
int positionQuant ;
/**
* Current color component (R, G, B, A) quantization value. This can
* range from 2 to 16 bits and has a default of 9.
*
* A color component is represented with a signed fixed-point value in
* order to be able express negative deltas; the default of 9 bits
* corresponds to the 8-bit color component range of the graphics hardware
* commonly available. Colors must be non-negative, so the lower limit of
* quantization is 2 bits.
*/
int colorQuant ;
/**
* Current normal component (U and V) quantization value. This can range
* from 0 to 6 bits and has a default of 6.
*
* At 0 bits of quantization normals are represented only as 6 bit
* sextant/octant pairs and 14 specially encoded normals (the 6 axis
* normals and the 8 octant midpoint normals); since U and V can only be 0
* at the minimum quantization, the totally number of unique normals is
* 12 + 14 = 26.
*/
int normalQuant ;
/**
* Flag indicating position quantization change.
*/
boolean positionQuantChanged ;
/**
* Flag indicating color quantization change.
*/
boolean colorQuantChanged ;
/**
* Flag indicating normal quantization change.
*/
boolean normalQuantChanged ;
/**
* Last quantized position.
*/
int lastPosition[] = new int[3] ;
/**
* Last quantized color.
*/
int lastColor[] = new int[4] ;
/**
* Last quantized normal's sextant.
*/
int lastSextant ;
/**
* Last quantized normal's octant.
*/
int lastOctant ;
/**
* Last quantized normal's U encoding parameter.
*/
int lastU ;
/**
* Last quantized normal's V encoding parameter.
*/
int lastV ;
/**
* Flag indicating last normal used a special encoding.
*/
boolean lastSpecialNormal ;
/**
* Flag indicating the first position in this stream.
*/
boolean firstPosition ;
/**
* Flag indicating the first color in this stream.
*/
boolean firstColor ;
/**
* Flag indicating the first normal in this stream.
*/
boolean firstNormal ;
/**
* The total number of bytes used to create the uncompressed geometric
* elements in this stream, useful for performance analysis. This
* excludes mesh buffer references.
*/
int byteCount ;
/**
* The number of vertices created for this stream, excluding mesh buffer
* references.
*/
int vertexCount ;
/**
* The number of mesh buffer references created for this stream.
*/
int meshReferenceCount ;
/**
* Mesh buffer mirror used for computing deltas during quantization pass
* and a limited meshing algorithm for unstripped data.
*/
MeshBuffer meshBuffer = new MeshBuffer() ;
// Collection which holds the elements of this stream.
private Collection stream ;
// True if preceding stream elements were colors or normals. Used to flag
// color and normal mesh buffer substitution when computing deltas during
// quantization pass.
private boolean lastElementColor = false ;
private boolean lastLastElementColor = false ;
private boolean lastElementNormal = false ;
private boolean lastLastElementNormal = false ;
// Some convenient temporary holding variables.
private Point3f p3f = new Point3f() ;
private Color3f c3f = new Color3f() ;
private Color4f c4f = new Color4f() ;
private Vector3f n3f = new Vector3f() ;
// Private constructor for common initializations.
private CompressionStream() {
this.stream = new LinkedList() ;
byteCount = 0 ;
vertexCount = 0 ;
meshReferenceCount = 0 ;
mcBounds[0] = new Point3d(Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY) ;
mcBounds[1] = new Point3d(Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY) ;
qcBounds[0] = new Point3i(Integer.MAX_VALUE,
Integer.MAX_VALUE,
Integer.MAX_VALUE) ;
qcBounds[1] = new Point3i(Integer.MIN_VALUE,
Integer.MIN_VALUE,
Integer.MIN_VALUE) ;
/* normalized bounds computed from quantized bounds */
ncBounds[0] = new Point3d() ;
ncBounds[1] = new Point3d() ;
}
/**
* Creates a new CompressionStream for the specified geometry type and
* vertex format.
*
* @param streamType type of data in this stream, either
* CompressedGeometryData.Header.POINT_BUFFER,
* CompressedGeometryData.Header.LINE_BUFFER, or
* CompressedGeometryData.Header.TRIANGLE_BUFFER
* @param vertexComponents a mask indicating which components are present
* in each vertex, as defined by GeometryArray: COORDINATES, NORMALS, and
* COLOR_3 or COLOR_4.
* @see GeometryCompressor
* @see GeometryArray
*/
CompressionStream(int streamType, int vertexComponents) {
this() ;
this.streamType = streamType ;
this.vertexComponents = getVertexComponents(vertexComponents) ;
}
// See what vertex geometry components are present. The byReference,
// interleaved, useNIOBuffer, and useCoordIndexOnly flags are not
// examined.
private int getVertexComponents(int vertexFormat) {
int components = 0 ;
vertexColors = vertexColor3 = vertexColor4 = vertexNormals =
vertexTextures = vertexTexture2 = vertexTexture3 = vertexTexture4 =
false ;
if ((vertexFormat & GeometryArray.NORMALS) != 0) {
vertexNormals = true ;
components &= GeometryArray.NORMALS ;
if (debug) System.out.println("vertexNormals") ;
}
if ((vertexFormat & GeometryArray.COLOR_3) != 0) {
vertexColors = true ;
if ((vertexFormat & GeometryArray.COLOR_4) != 0) {
vertexColor4 = true ;
components &= GeometryArray.COLOR_4 ;
if (debug) System.out.println("vertexColor4") ;
}
else {
vertexColor3 = true ;
components &= GeometryArray.COLOR_3 ;
if (debug) System.out.println("vertexColor3") ;
}
}
if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
vertexTextures = true ;
vertexTexture2 = true ;
components &= GeometryArray.TEXTURE_COORDINATE_2 ;
if (debug) System.out.println("vertexTexture2") ;
}
else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
vertexTextures = true ;
vertexTexture3 = true ;
components &= GeometryArray.TEXTURE_COORDINATE_3 ;
if (debug) System.out.println("vertexTexture3") ;
}
else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
vertexTextures = true ;
vertexTexture4 = true ;
components &= GeometryArray.TEXTURE_COORDINATE_4 ;
if (debug) System.out.println("vertexTexture4") ;
}
if (vertexTextures)
// Throw exception for now until texture is supported.
throw new UnsupportedOperationException
("\ncompression of texture coordinates is not supported") ;
return components ;
}
// Get the streamType associated with a GeometryArray instance.
private int getStreamType(GeometryArray ga) {
if (ga instanceof TriangleStripArray ||
ga instanceof IndexedTriangleStripArray ||
ga instanceof TriangleFanArray ||
ga instanceof IndexedTriangleFanArray ||
ga instanceof TriangleArray ||
ga instanceof IndexedTriangleArray ||
ga instanceof QuadArray ||
ga instanceof IndexedQuadArray)
return CompressedGeometryData.Header.TRIANGLE_BUFFER ;
else if (ga instanceof LineArray ||
ga instanceof IndexedLineArray ||
ga instanceof LineStripArray ||
ga instanceof IndexedLineStripArray)
return CompressedGeometryData.Header.LINE_BUFFER ;
else
return CompressedGeometryData.Header.POINT_BUFFER ;
}
/**
* Iterates across all compression stream elements and applies
* quantization parameters, encoding consecutive vertices as delta values
* whenever possible. Each geometric element is mapped to a HuffmanNode
* object containing its resulting bit length, right shift (trailing 0
* count), and absolute or relative status.
*
* Positions are normalized to span a unit cube via an offset and a
* uniform scale factor that maps the midpoint of the object extents along
* each dimension to the origin, and the longest dimension of the object to
* the open interval (-1.0 .. +1.0). The geometric endpoints along that
* dimension are both one quantum away from unity; for example, at a
* position quantization of 6 bits, an object would be normalized so that
* its most negative dimension is at (-1 + 1/64) and the most positive is
* at (1 - 1/64).
*
* Normals are assumed to be of unit length. Color components are clamped
* to the [0..1) range, where the right endpoint is one quantum less
* than 1.0.
*
* @param huffmanTable Table which will map geometric compression stream
* elements to HuffmanNode objects describing each element's data
* representation. This table can then be processed with Huffman's
* algorithm to optimize the bit length of descriptor tags according to
* the number of geometric elements mapped to each tag.
*/
void quantize(HuffmanTable huffmanTable) {
// Set up default initial quantization parameters. The position and
// color parameters specify the number of bits for each X, Y, Z, R, G,
// B, or A component. The normal quantization parameter specifies the
// number of bits for each U and V component.
positionQuant = 16 ;
colorQuant = 9 ;
normalQuant = 6 ;
// Compute position center and scaling for normalization to the unit
// cube. This is a volume bounded by the open intervals (-1..1) on
// each axis.
center[0] = (mcBounds[1].x + mcBounds[0].x) / 2.0 ;
center[1] = (mcBounds[1].y + mcBounds[0].y) / 2.0 ;
center[2] = (mcBounds[1].z + mcBounds[0].z) / 2.0 ;
double xRange = mcBounds[1].x - mcBounds[0].x ;
double yRange = mcBounds[1].y - mcBounds[0].y ;
double zRange = mcBounds[1].z - mcBounds[0].z ;
if (xRange > yRange)
positionRangeMaximum = xRange ;
else
positionRangeMaximum = yRange ;
if (zRange > positionRangeMaximum)
positionRangeMaximum = zRange ;
// Adjust the range of the unit cube to match the default
// quantization.
//
// This scale factor along with the center values computed above will
// produce 16-bit integer representations of the floating point
// position coordinates ranging symmetrically about 0 from -32767 to
// +32767. -32768 is not used and the normalized floating point
// position coordinates of -1.0 as well as +1.0 will not be
// represented.
//
// Applications which wish to seamlessly stitch together compressed
// objects will need to be aware that the range of normalized
// positions will be one quantum away from the [-1..1] endpoints of
// the unit cube and should adjust scale factors accordingly.
scale = (2.0 / positionRangeMaximum) * (32767.0 / 32768.0) ;
// Flag quantization change.
positionQuantChanged = colorQuantChanged = normalQuantChanged = true ;
// Flag first position, color, and normal.
firstPosition = firstColor = firstNormal = true ;
// Apply quantization.
Iterator i = stream.iterator() ;
while (i.hasNext()) {
Object o = i.next() ;
if (o instanceof CompressionStreamElement) {
((CompressionStreamElement)o).quantize(this, huffmanTable) ;
// Keep track of whether last two elements were colors or
// normals for mesh buffer component substitution semantics.
lastLastElementColor = lastElementColor ;
lastLastElementNormal = lastElementNormal ;
lastElementColor = lastElementNormal = false ;
if (o instanceof CompressionStreamColor)
lastElementColor = true ;
else if (o instanceof CompressionStreamNormal)
lastElementNormal = true ;
}
}
// Compute the bounds in normalized coordinates.
ncBounds[0].x = (double)qcBounds[0].x / 32768.0 ;
ncBounds[0].y = (double)qcBounds[0].y / 32768.0 ;
ncBounds[0].z = (double)qcBounds[0].z / 32768.0 ;
ncBounds[1].x = (double)qcBounds[1].x / 32768.0 ;
ncBounds[1].y = (double)qcBounds[1].y / 32768.0 ;
ncBounds[1].z = (double)qcBounds[1].z / 32768.0 ;
}
/**
* Iterates across all compression stream elements and builds the
* compressed geometry command stream output.
*
* @param huffmanTable Table which maps geometric elements in this stream
* to tags describing the encoding parameters (length, shift, and
* absolute/relative status) to be used for their representations in the
* compressed output. All tags must be 6 bits or less in length, and the
* sum of the number of bits in the tag plus the number of bits in the
* data it describes must be at least 6 bits in length.
*
* @param outputBuffer CommandStream to use for collecting the compressed
* bits.
*/
void outputCommands(HuffmanTable huffmanTable, CommandStream outputBuffer) {
//
// The first command output is setState to indicate what data is
// bundled with each vertex. Although the semantics of geometry
// decompression allow setState to appear anywhere in the stream, this
// cannot be handled by the current Java 3D software decompressor,
// which internally decompresses an entire compressed buffer into a
// single retained object sharing a single consistent vertex format.
// This limitation may be removed in subsequent releases of Java 3D.
//
int bnv = (vertexNormals? 1 : 0) ;
int bcv = ((vertexColor3 || vertexColor4)? 1 : 0) ;
int cap = (vertexColor4? 1 : 0) ;
int command = CommandStream.SET_STATE | bnv ;
long data = (bcv << 2) | (cap << 1) ;
// Output the setState command.
outputBuffer.addCommand(command, 8, data, 3) ;
// Output the Huffman table commands.
huffmanTable.outputCommands(outputBuffer) ;
// Output each compression stream element's data.
Iterator i = stream.iterator() ;
while (i.hasNext()) {
Object o = i.next() ;
if (o instanceof CompressionStreamElement)
((CompressionStreamElement)o).outputCommand(huffmanTable,
outputBuffer) ;
}
// Finish the header-forwarding interleave and long-word align.
outputBuffer.end() ;
}
/**
* Retrieve the total size of the uncompressed geometric data in bytes,
* excluding mesh buffer references.
* @return uncompressed byte count
*/
int getByteCount() {
return byteCount ;
}
/**
* Retrieve the the number of vertices created for this stream, excluding
* mesh buffer references.
* @return vertex count
*/
int getVertexCount() {
return vertexCount ;
}
/**
* Retrieve the number of mesh buffer references created for this stream.
* @return mesh buffer reference count
*/
int getMeshReferenceCount() {
return meshReferenceCount ;
}
/**
* Stream element that sets position quantization during quantize pass.
*/
private class PositionQuant extends CompressionStreamElement {
int value ;
PositionQuant(int value) {
this.value = value ;
}
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.
*
* Each Shape3D in the array must be of the same dimensionality (point,
* line, or surface) and have the same vertex format as the others.
* Texture coordinates are ignored.
*
* If a color is specified in the material attributes for a Shape3D then
* that color is added to the CompressionStream as the current global
* color. Subsequent colors as well as any colors bundled with vertices
* will override it. Only the material diffuse colors are used; all other
* appearance attributes are ignored.
*
* @param positionQuant
* number of bits to quantize each position's X, Y,
* and Z components, ranging from 1 to 16
*
* @param colorQuant
* number of bits to quantize each color's R, G, B, and
* alpha components, ranging from 2 to 16
*
* @param normalQuant
* number of bits for quantizing each normal's U and V components, ranging
* from 0 to 6
*
* @param shapes
* an array of Shape3D scene graph objects containing
* GeometryArray objects, all with the same vertex format and
* dimensionality
*
* @exception IllegalArgumentException if any Shape3D has an inconsistent
* dimensionality or vertex format, or if any Shape3D contains a geometry
* component that is not a GeometryArray
*
* @see Shape3D
* @see GeometryArray
* @see GeometryCompressor
*/
public CompressionStream(int positionQuant, int colorQuant,
int normalQuant, Shape3D shapes[]) {
this() ;
if (debug) System.out.println("CompressionStream(Shape3D[]):") ;
if (shapes == null)
throw new IllegalArgumentException("null Shape3D array") ;
if (shapes.length == 0)
throw new IllegalArgumentException("zero-length Shape3D array") ;
if (shapes[0] == null)
throw new IllegalArgumentException("Shape3D at index 0 is null") ;
long startTime = 0 ;
if (benchmark) startTime = System.currentTimeMillis() ;
Geometry g = shapes[0].getGeometry() ;
if (! (g instanceof GeometryArray))
throw new IllegalArgumentException
("Shape3D at index 0 is not a GeometryArray") ;
GeometryArray ga = (GeometryArray)g ;
this.streamType = getStreamType(ga) ;
this.vertexComponents = getVertexComponents(ga.getVertexFormat()) ;
// Add global quantization parameters to the start of the stream.
addPositionQuantization(positionQuant) ;
addColorQuantization(colorQuant) ;
addNormalQuantization(normalQuant) ;
// Loop through all shapes.
for (int s = 0 ; s < shapes.length ; s++) {
if (debug) System.out.println("\nShape3D " + s + ":") ;
g = shapes[s].getGeometry() ;
if (! (g instanceof GeometryArray))
throw new IllegalArgumentException
("Shape3D at index " + s + " is not a GeometryArray") ;
// Check for material color and add it to the stream if it exists.
Appearance a = shapes[s].getAppearance() ;
if (a != null) {
Material m = a.getMaterial() ;
if (m != null) {
m.getDiffuseColor(c3f) ;
if (vertexColor4) {
c4f.set(c3f.x, c3f.y, c3f.z, 1.0f) ;
addColor(c4f) ;
} else
addColor(c3f) ;
}
}
// Add the geometry array to the stream.
addGeometryArray((GeometryArray)g) ;
}
if (benchmark) {
long t = System.currentTimeMillis() - startTime ;
System.out.println
("\nCompressionStream:\n" + shapes.length + " shapes in " +
(t / 1000f) + " sec") ;
}
}
/**
* Creates a CompressionStream from an array of Shape3D scene graph
* objects. These Shape3D objects may only consist of a GeometryArray
* component and an optional Appearance component. The resulting stream
* may be used as input to the GeometryCompressor methods.
*
* Each Shape3D in the array must be of the same dimensionality (point,
* line, or surface) and have the same vertex format as the others.
* Texture coordinates are ignored.
*
* If a color is specified in the material attributes for a Shape3D then
* that color is added to the CompressionStream as the current global
* color. Subsequent colors as well as any colors bundled with vertices
* will override it. Only the material diffuse colors are used; all other
* appearance attributes are ignored.
*
* Defaults of 16, 9, and 6 bits are used as the quantization values for
* positions, colors, and normals respectively. These are the maximum
* resolution values defined for positions and normals; the default of 9
* for color is the equivalent of the 8 bits of RGBA component resolution
* commonly available in graphics frame buffers.
*
* @param shapes
* an array of Shape3D scene graph objects containing
* GeometryArray objects, all with the same vertex format and
* dimensionality.
*
* @exception IllegalArgumentException if any Shape3D has an inconsistent
* dimensionality or vertex format, or if any Shape3D contains a geometry
* component that is not a GeometryArray
*
* @see Shape3D
* @see GeometryArray
* @see GeometryCompressor
*/
public CompressionStream(Shape3D shapes[]) {
this(16, 9, 6, shapes) ;
}
/**
* Creates a CompressionStream from an array of GeometryInfo objects. The
* resulting stream may be used as input to the GeometryCompressor
* methods.
*
* Each GeometryInfo in the array must be of the same dimensionality
* (point, line, or surface) and have the same vertex format as the
* others. Texture coordinates are ignored.
*
* @param positionQuant
* number of bits to quantize each position's X, Y,
* and Z components, ranging from 1 to 16
*
* @param colorQuant
* number of bits to quantize each color's R, G, B, and
* alpha components, ranging from 2 to 16
*
* @param normalQuant
* number of bits for quantizing each normal's U and V components, ranging
* from 0 to 6
*
* @param geometry
* an array of GeometryInfo objects, all with the same
* vertex format and dimensionality
*
* @exception IllegalArgumentException if any GeometryInfo object has an
* inconsistent dimensionality or vertex format
*
* @see GeometryInfo
* @see GeometryCompressor
*/
public CompressionStream(int positionQuant, int colorQuant,
int normalQuant, GeometryInfo geometry[]) {
this() ;
if (debug) System.out.println("CompressionStream(GeometryInfo[])") ;
if (geometry == null)
throw new IllegalArgumentException("null GeometryInfo array") ;
if (geometry.length == 0)
throw new IllegalArgumentException
("zero-length GeometryInfo array") ;
if (geometry[0] == null)
throw new IllegalArgumentException
("GeometryInfo at index 0 is null") ;
long startTime = 0 ;
if (benchmark) startTime = System.currentTimeMillis() ;
GeometryArray ga = geometry[0].getGeometryArray() ;
this.streamType = getStreamType(ga) ;
this.vertexComponents = getVertexComponents(ga.getVertexFormat()) ;
// Add global quantization parameters to the start of the stream.
addPositionQuantization(positionQuant) ;
addColorQuantization(colorQuant) ;
addNormalQuantization(normalQuant) ;
// Loop through all GeometryInfo objects and add them to the stream.
for (int i = 0 ; i < geometry.length ; i++) {
if (debug) System.out.println("\nGeometryInfo " + i + ":") ;
addGeometryArray(geometry[i].getGeometryArray()) ;
}
if (benchmark) {
long t = System.currentTimeMillis() - startTime ;
System.out.println
("\nCompressionStream:\n" + geometry.length +
" GeometryInfo objects in " + (t / 1000f) + " sec") ;
}
}
/**
* Creates a CompressionStream from an array of GeometryInfo objects. The
* resulting stream may be used as input to the GeometryCompressor
* methods.
*
* Each GeometryInfo in the array must be of the same dimensionality
* (point, line, or surface) and have the same vertex format as the
* others. Texture coordinates are ignored.
*
* Defaults of 16, 9, and 6 bits are used as the quantization values for
* positions, colors, and normals respectively. These are the maximum
* resolution values defined for positions and normals; the default of 9
* for color is the equivalent of the 8 bits of RGBA component resolution
* commonly available in graphics frame buffers.
*
* @param geometry
* an array of GeometryInfo objects, all with the same
* vertex format and dimensionality
*
* @exception IllegalArgumentException if any GeometryInfo object has an
* inconsistent dimensionality or vertex format
*
* @see GeometryInfo
* @see GeometryCompressor
*/
public CompressionStream(GeometryInfo geometry[]) {
this(16, 9, 6, geometry) ;
}
/**
* Get the original bounds of the coordinate data, in modeling coordinates.
* Coordinate data is positioned and scaled to a normalized cube after
* compression.
*
* @return Point3d array of length 2, where the 1st Point3d is the lower
* bounds and the 2nd Point3d is the upper bounds.
* @since Java 3D 1.3
*/
public Point3d[] getModelBounds() {
Point3d[] bounds = new Point3d[2] ;
bounds[0] = new Point3d(mcBounds[0]) ;
bounds[1] = new Point3d(mcBounds[1]) ;
return bounds ;
}
/**
* Get the bounds of the compressed object in normalized coordinates.
* These have an maximum bounds by [-1.0 .. +1.0] across each axis.
*
* @return Point3d array of length 2, where the 1st Point3d is the lower
* bounds and the 2nd Point3d is the upper bounds.
* @since Java 3D 1.3
*/
public Point3d[] getNormalizedBounds() {
Point3d[] bounds = new Point3d[2] ;
bounds[0] = new Point3d(ncBounds[0]) ;
bounds[1] = new Point3d(ncBounds[1]) ;
return bounds ;
}
}
././@LongLink 0000000 0000000 0000000 00000000166 00000000000 011570 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryData.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/Compressed0000644 0000000 0000000 00000051574 10563126525 032045 0 ustar root root /*
* $RCSfile: CompressedGeometryData.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:21 $
* $State: Exp $
*/
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.
*
* 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.
*
*
* 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.
*
* 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.
*
* If the CompressedGeometryData is created with copy access semantics, then
*
*
* @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.
*
* @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 ;
}
}
././@LongLink 0000000 0000000 0000000 00000000172 00000000000 011565 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressedGeometryRetained.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/Compressed0000644 0000000 0000000 00000023467 10563126525 032045 0 ustar root root /*
* $RCSfile: CompressedGeometryRetained.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:21 $
* $State: Exp $
*/
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 ;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/GeometryInfo.java 0000644 0000000 0000000 00000276420 10563126523 030724 0 ustar root root /*
* $RCSfile: GeometryInfo.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:19 $
* $State: Exp $
*/
package com.sun.j3d.utils.geometry;
import com.sun.j3d.utils.geometry.Triangulator;
import java.io.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.internal.J3dUtilsI18N;
import java.util.HashMap;
import com.sun.j3d.utils.geometry.GeometryInfoGenerator;
import com.sun.j3d.internal.BufferWrapper;
import com.sun.j3d.internal.ByteBufferWrapper;
import com.sun.j3d.internal.FloatBufferWrapper;
import com.sun.j3d.internal.DoubleBufferWrapper;
import com.sun.j3d.internal.ByteOrderWrapper;
import javax.media.j3d.J3DBuffer;
/**
* The GeometryInfo object holds data for processing by the Java3D geometry
* utility tools.
*
* The Stripifier combines adjacent triangles into triangle strips for
* more efficent rendering.
* Geometry is loaded into a GeometryInfo in a manner similar to the
*
* GeometryArray methods. The constructor for the GeometryInfo takes a flag
* that specifies the kind of data being loaded. The vertex data is
* specified using methods that are similar to the GeometryArray methods, but
* with fewer variations.
* The major difference between GeometryInfo and GeometryArray is
* that the number of vertices, vertex format, and other data are specified
* implictly, rather than as part of the constructor. The number of verticies
* comes from the number of coordinates passed to the setCoordinates()
* method. The format comes from the set of data components that are
* specified. For example, calling the setCoordinates(), setColors3() and
* setTextureCoordinatesParames(1, 2) methods implies a
* format of COORDINATES | COLOR_3
* | TEXTURE_COORDINATE_2. Indexed representation is specified by calling
* the methods that specify the indices, for example
* setCoordinateIndices().
* Stripped primitives are loaded using the TRIANGLE_FAN_ARRAY or
* TRIANGLE_STRIP_ARRAY flags to the constructor. The setStripCounts()
* method specifies the length of each strip.
* A set of complex polygons is loaded using the POLYGON_ARRAY
* flag to the constructor. The setStripCounts() method specifies the length
* of each contour of the polygons. The setContourCounts() method specifies
* the number of countours in each polygon. For example, a triangle with a
* triangular hole would have strip counts [3, 3] (indicating two contours of
* three points) and contour counts [2] (indicating a single polygon with two
* contours).
* GeometryInfo itelf contains some simple utilities, such as
* calculating indices for non-indexed data ("indexifying") and getting rid
* of unused data in your indexed geometry ("compacting").
* The geometry utility tools modify the contents of the
* GeometryInfo. After processing, the resulting geometry can be extracted
* from the GeometryInfo by calling getGeometryArray(). If multiple tools
* are used, the order of processing should be: generate normals, then
* stripify. For example, to convert a general mesh of polygons without
* normals into an optimized mesh call:
*
* If the GeometryArray uses the Note: If the texCoordSetMap is not set, multi-texturing is
* turned off. Only the texture coordinate set at index 0 (if set) will be
* used. Any other sets specified by the GeometryInfo.setTextureCoordinate*
* methods will be ignored.
*/
public void setTexCoordSetMap(int map[]) {
texCoordSetMap = map;
}
/**
* Returns a reference to the texture coordinate set map.
* See the
*
* GeometryArray constructor for further details.
*/
public int[] getTexCoordSetMap() {
return texCoordSetMap;
}
/**
* Sets the 2D texture coordinates for the specified set.
* No data copying is done - a reference to user data is used.
* @param texCoordSet The texture coordinate set for which these
* coordinates are being specified.
* @param texCoords Array of 2D texture coordinates.
* @throws IllegalArgumentException if com.sun.j3d.*
hierarchy.
CVS download and build instructions for j3d-core-utils
cd <cvs-root-dir>
cvs checkout vecmath
cvs checkout j3d-core
cvs checkout j3d-core-utils
* Example of usage:
* Triangulator tr = new Triangulator();
* tr.triangulate(ginfo); // ginfo contains the geometry.
* shape.setGeometry(ginfo.getGeometryArray()); // shape is a Shape3D.
*
* @param gi Geometry to be triangulated
**/
public void triangulate(GeometryInfo gi) {
int i, j, k;
int sIndex = 0, index, currLoop, lastInd, ind;
boolean proceed;
boolean reset = false, troubles = false;
boolean done[] = new boolean[1];
boolean gotIt[] = new boolean[1];
if (gi.getPrimitive() != GeometryInfo.POLYGON_ARRAY){
throw new IllegalArgumentException(J3dUtilsI18N.getString("Triangulator0"));
}
gi.indexify();
vertices = gi.getCoordinates();
if(vertices != null)
vertexIndices = gi.getCoordinateIndices();
else
vertexIndices = null;
colors = gi.getColors();
normals = gi.getNormals();
this.gInfo= gi;
stripCounts = gi.getStripCounts();
faces = gi.getContourCounts();
if(faces == null) {
if(stripCounts == null)
System.out.println("StripCounts is null! Don't know what to do.");
faces = new int[stripCounts.length];
for(i=0; igetShape.
*
* @see Cylinder#getShape
*/
public static final int BODY = 0;
/**
* Designates the top end-cap of the cylinder.
* Used by getShape.
*
* @see Cylinder#getShape
*/
public static final int TOP = 1;
/**
* Designates the bottom end-cap of the cylinder.
* Used by getShape.
*
* @see Cylinder#getShape
*/
public static final int BOTTOM = 2;
/**
* Constructs a default cylinder of radius of 1.0 and height
* of 2.0. Normals are generated by default, texture
* coordinates are not. Resolution defaults to 15 divisions
* along X axis and 1 along the Y axis.
*/
public Cylinder() {
this(1.0f, 2.0f, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, null);
}
/**
* Constructs a default cylinder of a given radius and height.
* Normals are generated by default, texture coordinates are not.
* @param radius Radius
* @param height Height
*/
public Cylinder (float radius, float height) {
this(radius, height, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y,
null);
}
/**
* Constructs a default cylinder of a given radius, height, and
* appearance. Normals are generated by default, texture
* coordinates are not.
* @param radius Radius
* @param height Height
* @param ap Appearance
*/
public Cylinder (float radius, float height, Appearance ap)
{
this(radius, height, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y,
ap);
}
/**
*
* Constructs a default cylinder of a given radius, height,
* primitive flags and appearance.
* @param radius Radius
* @param height Height
* @param primflags Flags
* @param ap Appearance
*/
public Cylinder (float radius, float height, int primflags, Appearance ap)
{
this(radius, height, primflags, MID_REZ_DIV_X, MID_REZ_DIV_Y, ap);
}
/**
* Obtains the Shape3D node associated with a given part of the cylinder.
* This allows users to modify the appearance or geometry
* of individual parts.
* @param partId The part to return (BODY, TOP, or BOTTOM).
* @return The Shape3D object associated with the partID. If an
* invalid partId is passed in, null is returned.
*/
public Shape3D getShape(int partId){
if (partId > BOTTOM || partId < BODY) return null;
return (Shape3D)getChild(partId);
}
/** Sets appearance of the cylinder. This will set each part of the
* cylinder (TOP,BOTTOM,BODY) to the same appearance. To set each
* part's appearance separately, use getShape(partId) to get the
* individual shape and call shape.setAppearance(ap).
*/
public void setAppearance(Appearance ap) {
((Shape3D)getChild(BODY)).setAppearance(ap);
((Shape3D)getChild(TOP)).setAppearance(ap);
((Shape3D)getChild(BOTTOM)).setAppearance(ap);
}
/**
* Gets the appearance of the specified part of the cylinder.
*
* @param partId identifier for a given subpart of the cylinder
*
* @return The appearance object associated with the partID. If an
* invalid partId is passed in, null is returned.
*
* @since Java 3D 1.2.1
*/
public Appearance getAppearance(int partId) {
if (partId > BOTTOM || partId < BODY) return null;
return getShape(partId).getAppearance();
}
/**
* Constructs a customized cylinder of a given radius, height,
* resolution (X and Y dimensions), and appearance. The
* resolution is defined in terms of number of subdivisions
* along the object's X axis (width) and Y axis (height). More divisions
* lead to more finely tesselated objects.
* @param radius Radius
* @param height Height
* @param xdivision Number of divisions along X direction.
* @param ydivision Number of divisions along height of cylinder.
* @param primflags Primitive flags.
* @param ap Appearance
*/
public Cylinder(float radius, float height, int primflags,
int xdivision, int ydivision, Appearance ap) {
super();
this.radius = radius;
this.height = height;
this.xdivisions = xdivision;
this.ydivisions = ydivision;
flags = primflags;
boolean outside = (flags & GENERATE_NORMALS_INWARD) == 0;
boolean texCoordYUp = (flags & GENERATE_TEXTURE_COORDS_Y_UP) != 0;
// Create many body of the cylinder.
Quadrics q = new Quadrics();
GeomBuffer gbuf = null;
Shape3D shape[] = new Shape3D[3];
GeomBuffer cache = getCachedGeometry(Primitive.CYLINDER,
(float)BODY, radius, height,
xdivision, ydivision, primflags);
if (cache != null){
// System.out.println("using cached geometry");
shape[BODY] = new Shape3D(cache.getComputedGeometry());
numVerts += cache.getNumVerts();
numTris += cache.getNumTris();
}
else {
gbuf = q.cylinder((double)height, (double)radius,
xdivision, ydivision, outside, texCoordYUp);
shape[BODY] = new Shape3D(gbuf.getGeom(flags));
numVerts += gbuf.getNumVerts();
numTris += gbuf.getNumTris();
if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0)
cacheGeometry(Primitive.CYLINDER,
(float)BODY, radius, height,
xdivision, ydivision, primflags, gbuf);
}
if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
(shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
(shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
}
if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
(shape[BODY]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
}
this.addChild(shape[BODY]);
// Create top of cylinder
cache = getCachedGeometry(Primitive.TOP_DISK, radius, radius,
height/2.0f, xdivision, xdivision, primflags);
if (cache != null) {
// System.out.println("using cached top");
shape[TOP] = new Shape3D(cache.getComputedGeometry());
numVerts += cache.getNumVerts();
numTris += cache.getNumTris();
}
else {
gbuf = q.disk((double)radius, xdivision, height/2.0,
outside, texCoordYUp);
shape[TOP] = new Shape3D(gbuf.getGeom(flags));
numVerts += gbuf.getNumVerts();
numTris += gbuf.getNumTris();
if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
cacheGeometry(Primitive.TOP_DISK, radius, radius,
height/2.0f, xdivision, xdivision,
primflags, gbuf);
}
}
if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
(shape[TOP]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
(shape[TOP]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
}
if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
(shape[TOP]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
}
this.addChild(shape[TOP]);
// Create bottom
cache = getCachedGeometry(Primitive.BOTTOM_DISK, radius, radius,
-height/2.0f, xdivision, xdivision,
primflags);
if (cache != null) {
// System.out.println("using cached bottom");
shape[BOTTOM] = new Shape3D(cache.getComputedGeometry());
numVerts += cache.getNumVerts();
numTris += cache.getNumTris();
}
else {
gbuf = q.disk((double)radius, xdivision, -height/2.0, !outside, texCoordYUp);
shape[BOTTOM] = new Shape3D(gbuf.getGeom(flags));
numVerts += gbuf.getNumVerts();
numTris += gbuf.getNumTris();
if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
cacheGeometry(Primitive.BOTTOM_DISK, radius, radius,
-height/2.0f, xdivision, xdivision,
primflags, gbuf);
}
}
if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
(shape[BOTTOM]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
(shape[BOTTOM]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
}
if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
(shape[BOTTOM]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
}
this.addChild(shape[BOTTOM]);
// Set Appearance
if (ap == null){
setAppearance();
}
else setAppearance(ap);
}
/**
* Used to create a new instance of the node. This routine is called
* by cloneTree to duplicate the current node.
* cloneNode should be overridden by any user subclassed
* objects. All subclasses must have their cloneNode
* method consist of the following lines:
*
* @param forceDuplicate when set to
* public Node cloneNode(boolean forceDuplicate) {
* UserSubClass usc = new UserSubClass();
* usc.duplicateNode(this, forceDuplicate);
* return usc;
* }
* true, causes the
* duplicateOnCloneTree flag to be ignored. When
* false, the value of each node's
* duplicateOnCloneTree variable determines whether
* NodeComponent data is duplicated or copied.
*
* @see Node#cloneTree
* @see Node#duplicateNode
* @see NodeComponent#setDuplicateOnCloneTree
*/
public Node cloneNode(boolean forceDuplicate) {
Cylinder c = new Cylinder(radius, height, flags, xdivisions,
ydivisions, getAppearance());
c.duplicateNode(this, forceDuplicate);
return c;
}
/**
* Copies all node information from originalNode into
* the current node. This method is called from the
* cloneNode method which is, in turn, called by the
* cloneTree method.
* duplicateOnCloneTree value is used to determine
* whether the NodeComponent should be duplicated in the new node
* or if just a reference to the current node should be placed in the
* new node. This flag can be overridden by setting the
* forceDuplicate parameter in the cloneTree
* method to true.
*
* @param originalNode the original node to duplicate.
* @param forceDuplicate when set to true, causes the
* duplicateOnCloneTree flag to be ignored. When
* false, the value of each node's
* duplicateOnCloneTree variable determines whether
* NodeComponent data is duplicated or copied.
*
* @see Node#cloneTree
* @see Node#cloneNode
* @see NodeComponent#setDuplicateOnCloneTree
*/
public void duplicateNode(Node originalNode, boolean forceDuplicate) {
super.duplicateNode(originalNode, forceDuplicate);
}
/**
* Returns the radius of the cylinder
*
* @since Java 3D 1.2.1
*/
public float getRadius() {
return radius;
}
/**
* Returns the height of the cylinder
*
* @since Java 3D 1.2.1
*/
public float getHeight() {
return height;
}
/**
* Returns the number divisions along the X direction
*
* @since Java 3D 1.2.1
*/
public int getXdivisions() {
return xdivisions;
}
/**
* Returns the number of divisions along the height of the cylinder
*
* @since Java 3D 1.2.1
*/
public int getYdivisions() {
return ydivisions;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Clean.java 0000644 0000000 0000000 00000013216 10563126522 027326 0 ustar root root /*
* $RCSfile: Clean.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:18 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import java.io.*;
import java.util.*;
import javax.vecmath.*;
class Clean {
static void initPUnsorted(Triangulator triRef, int number) {
if (number > triRef.maxNumPUnsorted) {
triRef.maxNumPUnsorted = number;
triRef.pUnsorted = new Point2f[triRef.maxNumPUnsorted];
for(int i = 0; igetShape.
*
* @see Cone#getShape
*/
public static final int BODY = 0;
/**
* Designates the end-cap of the cone. Used by getShape.
*
* @see Cone#getShape
*/
public static final int CAP = 1;
/**
* Constructs a default Cone of radius of 1.0 and height
* of 2.0. Resolution defaults to 15 divisions along X and axis
* and 1 along the Y axis. Normals are generated, texture
* coordinates are not.
*/
public Cone(){
this(1.0f, 2.0f, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, null);
}
/**
*
* Constructs a default Cone of a given radius and height. Normals
* are generated, texture coordinates are not.
* @param radius Radius
* @param height Height
*/
public Cone (float radius, float height)
{
this(radius, height, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, null);
}
/**
*
* Constructs a default cone of a given radius, height,
* and appearance. Normals are generated, texture coordinates are not.
* @param radius Radius
* @param height Height
* @param ap Appearance
*
* @since Java 3D 1.2.1
*/
public Cone (float radius, float height, Appearance ap)
{
this(radius, height, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, ap);
}
/**
*
* Constructs a default cone of a given radius, height,
* primitive flags, and appearance.
* @param radius Radius
* @param height Height
* @param primflags Primitive flags
* @param ap Appearance
*/
public Cone (float radius, float height, int primflags, Appearance ap)
{
this(radius, height, primflags, MID_REZ_DIV_X, MID_REZ_DIV_Y, ap);
}
/**
* Obtains the Shape3D node associated with one of the parts of the
* cone (the body or the cap). This allows users to modify the appearance
* or geometry of individual parts.
* @param partId The part to return (BODY or CAP).
* @return The Shape3D object associated with the partId. If an
* invalid partId is passed in, null is returned.
*/
public Shape3D getShape(int partId){
if (partId > CAP || partId < BODY) return null;
return (Shape3D)getChild(partId);
}
/**
* Sets appearance of the cone. This will set each part of the
* cone (cap & body) to the same appearance. To set each
* part's appearance separately, use getShape(partId) to get the
* individual shape and call shape.setAppearance(ap).
*/
public void setAppearance(Appearance ap){
((Shape3D)getChild(BODY)).setAppearance(ap);
((Shape3D)getChild(CAP)).setAppearance(ap);
}
/**
* Gets the appearance of the specified part of the cone.
*
* @param partId identifier for a given subpart of the cone
*
* @return The appearance object associated with the partID. If an
* invalid partId is passed in, null is returned.
*
* @since Java 3D 1.2.1
*/
public Appearance getAppearance(int partId) {
if (partId > CAP || partId < BODY) return null;
return getShape(partId).getAppearance();
}
/**
* Constructs a customized Cone of a given radius, height, flags,
* resolution (X and Y dimensions), and appearance. The
* resolution is defined in terms of number of subdivisions
* along the object's X axis (width) and Y axis (height). More divisions
* lead to finer tesselated objects.
* cloneTree to duplicate the current node.
* cloneNode should be overridden by any user subclassed
* objects. All subclasses must have their cloneNode
* method consist of the following lines:
*
* @param forceDuplicate when set to
* public Node cloneNode(boolean forceDuplicate) {
* UserSubClass usc = new UserSubClass();
* usc.duplicateNode(this, forceDuplicate);
* return usc;
* }
* true, causes the
* duplicateOnCloneTree flag to be ignored. When
* false, the value of each node's
* duplicateOnCloneTree variable determines whether
* NodeComponent data is duplicated or copied.
*
* @see Node#cloneTree
* @see Node#duplicateNode
* @see NodeComponent#setDuplicateOnCloneTree
*/
public Node cloneNode(boolean forceDuplicate) {
Cone c = new Cone(radius, height, flags, xdivisions,
ydivisions, getAppearance());
c.duplicateNode(this, forceDuplicate);
return c;
}
/**
* Copies all node information from originalNode into
* the current node. This method is called from the
* cloneNode method which is, in turn, called by the
* cloneTree method.
* duplicateOnCloneTree value is used to determine
* whether the NodeComponent should be duplicated in the new node
* or if just a reference to the current node should be placed in the
* new node. This flag can be overridden by setting the
* forceDuplicate parameter in the cloneTree
* method to true.
*
* @param originalNode the original node to duplicate.
* @param forceDuplicate when set to true, causes the
* duplicateOnCloneTree flag to be ignored. When
* false, the value of each node's
* duplicateOnCloneTree variable determines whether
* NodeComponent data is duplicated or copied.
*
* @see Node#cloneTree
* @see Node#cloneNode
* @see NodeComponent#setDuplicateOnCloneTree
*/
public void duplicateNode(Node originalNode, boolean forceDuplicate) {
super.duplicateNode(originalNode, forceDuplicate);
}
/**
* Returns the radius of the cone
*
* @since Java 3D 1.2.1
*/
public float getRadius() {
return radius;
}
/**
* Returns the height of the cone
*
* @since Java 3D 1.2.1
*/
public float getHeight() {
return height;
}
/**
* Returns the number divisions along the X direction
*
* @since Java 3D 1.2.1
*/
public int getXdivisions() {
return xdivisions;
}
/**
* Returns the number of divisions along the height of the cone
*
* @since Java 3D 1.2.1
*/
public int getYdivisions() {
return ydivisions;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Heap.java 0000644 0000000 0000000 00000015556 10563126523 027173 0 ustar root root /*
* $RCSfile: Heap.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:19 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import java.io.*;
import java.util.*;
import javax.vecmath.*;
class Heap {
static void printHeapData(Triangulator triRef) {
int i;
System.out.println("\nHeap Data : numZero " + triRef.numZero +
" numHeap " + triRef.numHeap);
for(i=0; i< triRef.numHeap; i++)
System.out.println(i + " ratio " + triRef.heap[i].ratio + ", index " +
triRef.heap[i].index + ", prev " +
triRef.heap[i].prev + ", next " +
triRef.heap[i].next);
System.out.println(" ");
}
static void initHeap(Triangulator triRef) {
// Calculate the maximum bounds : N + (N -2)* 2.
// triRef.maxNumHeap = triRef.numPoints * 3;
triRef.maxNumHeap = triRef.numPoints;
triRef.heap = new HeapNode[triRef.maxNumHeap];
triRef.numHeap = 0;
triRef.numZero = 0;
}
static void storeHeapData(Triangulator triRef, int index, double ratio,
int ind, int prev, int next) {
triRef.heap[index] = new HeapNode();
triRef.heap[index].ratio = ratio;
triRef.heap[index].index = ind;
triRef.heap[index].prev = prev;
triRef.heap[index].next = next;
}
static void dumpOnHeap(Triangulator triRef, double ratio,
int ind, int prev, int next) {
int index;
if (triRef.numHeap >= triRef.maxNumHeap) {
// System.out.println("Heap:dumpOnHeap.Expanding heap array ...");
HeapNode old[] = triRef.heap;
triRef.maxNumHeap = triRef.maxNumHeap + triRef.numPoints;
triRef.heap = new HeapNode[triRef.maxNumHeap];
System.arraycopy(old, 0, triRef.heap, 0, old.length);
}
if (ratio == 0.0) {
if (triRef.numZero < triRef.numHeap)
if(triRef.heap[triRef.numHeap] == null)
storeHeapData(triRef, triRef.numHeap, triRef.heap[triRef.numZero].ratio,
triRef.heap[triRef.numZero].index,
triRef.heap[triRef.numZero].prev,
triRef.heap[triRef.numZero].next);
else
triRef.heap[triRef.numHeap].copy(triRef.heap[triRef.numZero]);
/*
storeHeapData(triRef, triRef.numHeap, triRef.heap[triRef.numZero].ratio,
triRef.heap[triRef.numZero].index,
triRef.heap[triRef.numZero].prev,
triRef.heap[triRef.numZero].next);
*/
index = triRef.numZero;
++triRef.numZero;
}
else {
index = triRef.numHeap;
}
storeHeapData(triRef, index, ratio, ind, prev, next);
++triRef.numHeap;
}
static void insertIntoHeap(Triangulator triRef, double ratio,
int ind, int prev, int next) {
dumpOnHeap(triRef, ratio, ind, prev, next);
}
static boolean deleteFromHeap(Triangulator triRef, int[] ind,
int[] prev, int[] next) {
double rnd;
int rndInd;
// earSorted is not implemented yet.
if (triRef.numZero > 0) {
// assert(num_heap >= num_zero);
--triRef.numZero;
--triRef.numHeap;
ind[0] = triRef.heap[triRef.numZero].index;
prev[0] = triRef.heap[triRef.numZero].prev;
next[0] = triRef.heap[triRef.numZero].next;
if (triRef.numZero < triRef.numHeap)
triRef.heap[triRef.numZero].copy(triRef.heap[triRef.numHeap]);
/*
storeHeapData( triRef, triRef.numZero, triRef.heap[triRef.numHeap].ratio,
triRef.heap[triRef.numHeap].index,
triRef.heap[triRef.numHeap].prev,
triRef.heap[triRef.numHeap].next);
*/
return true;
}
else if (triRef.earsRandom) {
if (triRef.numHeap <= 0) {
triRef.numHeap = 0;
return false;
}
rnd = triRef.randomGen.nextDouble();
rndInd = (int)(rnd * triRef.numHeap);
--triRef.numHeap;
if (rndInd > triRef.numHeap) rndInd = triRef.numHeap;
ind[0] = triRef.heap[rndInd].index;
prev[0] = triRef.heap[rndInd].prev;
next[0] = triRef.heap[rndInd].next;
if (rndInd < triRef.numHeap)
triRef.heap[rndInd].copy(triRef.heap[triRef.numHeap]);
/*
storeHeapData( triRef, rndInd, triRef.heap[triRef.numHeap].ratio,
triRef.heap[triRef.numHeap].index,
triRef.heap[triRef.numHeap].prev,
triRef.heap[triRef.numHeap].next);
*/
return true;
}
else {
if (triRef.numHeap <= 0) {
triRef.numHeap = 0;
return false;
}
--triRef.numHeap;
ind[0] = triRef.heap[triRef.numHeap].index;
prev[0] = triRef.heap[triRef.numHeap].prev;
next[0] = triRef.heap[triRef.numHeap].next;
return true;
}
// return false;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Simple.java 0000644 0000000 0000000 00000017725 10563126524 027550 0 ustar root root /*
* $RCSfile: Simple.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:20 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import java.io.*;
import java.util.*;
import javax.vecmath.*;
class Simple {
/**
* Handle a triangle or a quadrangle in a simple and efficient way. if the
* face is more complex than false is returned.
*
* warning: the correctness of this function depends upon the fact that
* `CleanPolyhedralFace' has not yet been executed; i.e., the
* vertex indices have not been changed since the execution of
* `CleanPolyhedron'! (otherwise, we would have to get the original
* indices via calls to `GetOriginal'...)
*/
static boolean simpleFace(Triangulator triRef, int ind1) {
int ind0, ind2, ind3, ind4;
int i1, i2, i3, i0, i4;
Point3f pq, pr, nr;
double x, y, z;
int ori2, ori4;
ind0 = triRef.fetchPrevData(ind1);
i0 = triRef.fetchData(ind0);
if (ind0 == ind1) {
// this polygon has only one vertex! nothing to triangulate...
System.out.println("***** polygon with only one vertex?! *****\n");
return true;
}
ind2 = triRef.fetchNextData(ind1);
i2 = triRef.fetchData(ind2);
if (ind0 == ind2) {
// this polygon has only two vertices! nothing to triangulate...
System.out.println("***** polygon with only two vertices?! *****\n");
return true;
}
ind3 = triRef.fetchNextData(ind2);
i3 = triRef.fetchData(ind3);
if (ind0 == ind3) {
// this polygon is a triangle! let's triangulate it!
i1 = triRef.fetchData(ind1);
// triRef.storeTriangle(i1, i2, i3);
triRef.storeTriangle(ind1, ind2, ind3);
return true;
}
ind4 = triRef.fetchNextData(ind3);
i4 = triRef.fetchData(ind4);
if (ind0 == ind4) {
// this polygon is a quadrangle! not too hard to triangulate it...
// we project the corners of the quadrangle onto one of the coordinate
// planes
triRef.initPnts(5);
i1 = triRef.fetchData(ind1);
pq = new Point3f();
pr = new Point3f();
nr = new Point3f();
/*
System.out.println("ind0 " + ind0 + ", ind1 " + ind1 + ", ind2 " +
ind2 + ", ind3 " + ind3 + ", ind4 " + ind4);
System.out.println("i0 " + i0 +", i1 " + i1 + ", i2 " + i2 +
", i3 " + i3 + ", i4 " + i4);
System.out.println("vert[i1] " + triRef.vertices[i1] +
"vert[i2] " + triRef.vertices[i2] +
"vert[i3] " + triRef.vertices[i3]);
*/
Basic.vectorSub(triRef.vertices[i1], triRef.vertices[i2], pq);
Basic.vectorSub(triRef.vertices[i3], triRef.vertices[i2], pr);
Basic.vectorProduct(pq, pr, nr);
// System.out.println("pq " + pq + " pr " + pr + " nr " + nr);
x = Math.abs(nr.x);
y = Math.abs(nr.y);
z = Math.abs(nr.z);
if ((z >= x) && (z >= y)) {
// System.out.println("((z >= x) && (z >= y))");
triRef.points[1].x = triRef.vertices[i1].x;
triRef.points[1].y = triRef.vertices[i1].y;
triRef.points[2].x = triRef.vertices[i2].x;
triRef.points[2].y = triRef.vertices[i2].y;
triRef.points[3].x = triRef.vertices[i3].x;
triRef.points[3].y = triRef.vertices[i3].y;
triRef.points[4].x = triRef.vertices[i4].x;
triRef.points[4].y = triRef.vertices[i4].y;
}
else if ((x >= y) && (x >= z)) {
// System.out.println("((x >= y) && (x >= z))");
triRef.points[1].x = triRef.vertices[i1].z;
triRef.points[1].y = triRef.vertices[i1].y;
triRef.points[2].x = triRef.vertices[i2].z;
triRef.points[2].y = triRef.vertices[i2].y;
triRef.points[3].x = triRef.vertices[i3].z;
triRef.points[3].y = triRef.vertices[i3].y;
triRef.points[4].x = triRef.vertices[i4].z;
triRef.points[4].y = triRef.vertices[i4].y;
}
else {
triRef.points[1].x = triRef.vertices[i1].x;
triRef.points[1].y = triRef.vertices[i1].z;
triRef.points[2].x = triRef.vertices[i2].x;
triRef.points[2].y = triRef.vertices[i2].z;
triRef.points[3].x = triRef.vertices[i3].x;
triRef.points[3].y = triRef.vertices[i3].z;
triRef.points[4].x = triRef.vertices[i4].x;
triRef.points[4].y = triRef.vertices[i4].z;
}
triRef.numPoints = 5;
// find a valid diagonal
ori2 = Numerics.orientation(triRef, 1, 2, 3);
ori4 = Numerics.orientation(triRef, 1, 3, 4);
/*
for(int i=0; i<5; i++)
System.out.println("point " + i + ", " + triRef.points[i]);
System.out.println("ori2 : " + ori2 + " ori4 : " + ori4);
*/
if (((ori2 > 0) && (ori4 > 0)) ||
((ori2 < 0) && (ori4 < 0))) {
// i1, i3 is a valid diagonal;
//
// encode as a 2-triangle strip: the triangles are (2, 3, 1)
// and (1, 3, 4).
// triRef.storeTriangle(i1, i2, i3);
// triRef.storeTriangle(i1, i3, i4);
triRef.storeTriangle(ind1, ind2, ind3);
triRef.storeTriangle(ind1, ind3, ind4);
}
else {
// i2, i4 has to be a valid diagonal. (if this is no valid
// diagonal then the corners of the quad form a figure of eight;
// shall we apply any heuristics in order to guess which diagonal
// is more likely to be the better choice? alternatively, we could
// return false and subject it to the standard triangulation
// algorithm. well, let's see how this brute-force solution works.)
// encode as a 2-triangle strip: the triangles are (1, 2, 4)
// and (4, 2, 3).
// triRef.storeTriangle(i2, i3, i4);
// triRef.storeTriangle(i2, i4, i1);
triRef.storeTriangle(ind2, ind3, ind4);
triRef.storeTriangle(ind2, ind4, ind1);
}
return true;
}
return false;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Numerics.java 0000644 0000000 0000000 00000045766 10563126524 030112 0 ustar root root /*
* $RCSfile: Numerics.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:20 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import java.io.*;
import java.util.*;
import javax.vecmath.*;
class Numerics {
static double max3(double a, double b, double c)
{
return (((a) > (b)) ? (((a) > (c)) ? (a) : (c))
: (((b) > (c)) ? (b) : (c)));
}
static double min3(double a, double b, double c)
{
return (((a) < (b)) ? (((a) < (c)) ? (a) : (c))
: (((b) < (c)) ? (b) : (c)));
}
static boolean lt(double a, double eps)
{
return ((a) < -eps);
}
static boolean le(double a, double eps)
{
return (a <= eps);
}
static boolean ge(double a, double eps)
{
return (!((a) <= -eps));
}
static boolean eq(double a, double eps)
{
return (((a) <= eps) && !((a) < -eps));
}
static boolean gt(double a, double eps)
{
return !((a) <= eps);
}
static double baseLength(Tuple2f u, Tuple2f v) {
double x, y;
x = (v).x - (u).x;
y = (v).y - (u).y;
return Math.abs(x) + Math.abs(y);
}
static double sideLength(Tuple2f u, Tuple2f v) {
double x, y;
x = (v).x - (u).x;
y = (v).y - (u).y;
return x * x + y * y;
}
/**
* This checks whether i3, which is collinear with i1, i2, is
* between i1, i2. note that we rely on the lexicographic sorting of the
* points!
*/
static boolean inBetween(int i1, int i2, int i3) {
return ((i1 <= i3) && (i3 <= i2));
}
static boolean strictlyInBetween(int i1, int i2, int i3) {
return ((i1 < i3) && (i3 < i2));
}
/**
* this method computes the determinant det(points[i],points[j],points[k])
* in a consistent way.
*/
static double stableDet2D(Triangulator triRef, int i, int j, int k) {
double det;
Point2f numericsHP, numericsHQ, numericsHR;
// if((triRef.inPointsList(i)==false)||(triRef.inPointsList(j)==false)||
// (triRef.inPointsList(k)==false))
// System.out.println("Numerics.stableDet2D Not inPointsList " + i + " " + j
// + " " + k);
if ((i == j) || (i == k) || (j == k)) {
det = 0.0;
}
else {
numericsHP = triRef.points[i];
numericsHQ = triRef.points[j];
numericsHR = triRef.points[k];
if (i < j) {
if (j < k) /* i < j < k */
det = Basic.det2D(numericsHP, numericsHQ, numericsHR);
else if (i < k) /* i < k < j */
det = -Basic.det2D(numericsHP, numericsHR, numericsHQ);
else /* k < i < j */
det = Basic.det2D(numericsHR, numericsHP, numericsHQ);
}
else {
if (i < k) /* j < i < k */
det = -Basic.det2D(numericsHQ, numericsHP, numericsHR);
else if (j < k) /* j < k < i */
det = Basic.det2D(numericsHQ, numericsHR, numericsHP);
else /* k < j < i */
det = -Basic.det2D(numericsHR, numericsHQ, numericsHP);
}
}
return det;
}
/**
* Returns the orientation of the triangle.
* @return +1 if the points i, j, k are given in CCW order;
* -1 if the points i, j, k are given in CW order;
* 0 if the points i, j, k are collinear.
*/
static int orientation(Triangulator triRef, int i, int j, int k) {
int ori;
double numericsHDet;
numericsHDet = stableDet2D(triRef, i, j, k);
// System.out.println("orientation : numericsHDet " + numericsHDet);
if (lt(numericsHDet, triRef.epsilon)) ori = -1;
else if (gt(numericsHDet, triRef.epsilon)) ori = 1;
else ori = 0;
return ori;
}
/**
* This method checks whether l is in the cone defined by i, j and j, k
*/
static boolean isInCone(Triangulator triRef, int i, int j, int k,
int l, boolean convex) {
boolean flag;
int numericsHOri1, numericsHOri2;
// if((triRef.inPointsList(i)==false)||(triRef.inPointsList(j)==false)||
// (triRef.inPointsList(k)==false)||(triRef.inPointsList(l)==false))
// System.out.println("Numerics.isInCone Not inPointsList " + i + " " + j
// + " " + k + " " + l);
flag = true;
if (convex) {
if (i != j) {
numericsHOri1 = orientation(triRef, i, j, l);
// System.out.println("isInCone : i != j, numericsHOri1 = " + numericsHOri1);
if (numericsHOri1 < 0) flag = false;
else if (numericsHOri1 == 0) {
if (i < j) {
if (!inBetween(i, j, l)) flag = false;
}
else {
if (!inBetween(j, i, l)) flag = false;
}
}
}
if ((j != k) && (flag == true)) {
numericsHOri2 = orientation(triRef, j, k, l);
// System.out.println("isInCone : ((j != k) && (flag == true)), numericsHOri2 = " +
// numericsHOri2);
if (numericsHOri2 < 0) flag = false;
else if (numericsHOri2 == 0) {
if (j < k) {
if (!inBetween(j, k, l)) flag = false;
}
else {
if (!inBetween(k, j, l)) flag = false;
}
}
}
}
else {
numericsHOri1= orientation(triRef, i, j, l);
if (numericsHOri1 <= 0) {
numericsHOri2 = orientation(triRef, j, k, l);
if (numericsHOri2 < 0) flag = false;
}
}
return flag;
}
/**
* Returns convex angle flag.
* @return 0 ... if angle is 180 degrees
* 1 ... if angle between 0 and 180 degrees
* 2 ... if angle is 0 degrees
* -1 ... if angle between 180 and 360 degrees
* -2 ... if angle is 360 degrees
*/
static int isConvexAngle(Triangulator triRef, int i, int j, int k, int ind) {
int angle;
double numericsHDot;
int numericsHOri1;
Point2f numericsHP, numericsHQ;
// if((triRef.inPointsList(i)==false)||(triRef.inPointsList(j)==false)||
// (triRef.inPointsList(k)==false))
// System.out.println("Numerics.isConvexAngle: Not inPointsList " + i + " " + j
// + " " + k);
if (i == j) {
if (j == k) {
// all three vertices are identical; we set the angle to 1 in
// order to enable clipping of j.
return 1;
}
else {
// two of the three vertices are identical; we set the angle to 1
// in order to enable clipping of j.
return 1;
}
}
else if (j == k) {
// two vertices are identical. we could either determine the angle
// by means of yet another lengthy analysis, or simply set the
// angle to -1. using -1 means to err on the safe side, as all the
// incarnations of this vertex will be clipped right at the start
// of the ear-clipping algorithm. thus, eventually there will be no
// other duplicates at this vertex position, and the regular
// classification of angles will yield the correct answer for j.
return -1;
}
else {
numericsHOri1 = orientation(triRef, i, j, k);
// System.out.println("i " + i + " j " + j + " k " + k + " ind " + ind +
// ". In IsConvexAngle numericsHOri1 is " +
// numericsHOri1);
if (numericsHOri1 > 0) {
angle = 1;
}
else if (numericsHOri1 < 0) {
angle = -1;
}
else {
// 0, 180, or 360 degrees.
numericsHP = new Point2f();
numericsHQ = new Point2f();
Basic.vectorSub2D(triRef.points[i], triRef.points[j], numericsHP);
Basic.vectorSub2D(triRef.points[k], triRef.points[j], numericsHQ);
numericsHDot = Basic.dotProduct2D(numericsHP, numericsHQ);
if (numericsHDot < 0.0) {
// 180 degrees.
angle = 0;
}
else {
// 0 or 360 degrees? this cannot be judged locally, and more
// work is needed.
angle = spikeAngle(triRef, i, j, k, ind);
// System.out.println("SpikeAngle return is "+ angle);
}
}
}
return angle;
}
/**
* This method checks whether point i4 is inside of or on the boundary
* of the triangle i1, i2, i3.
*/
static boolean pntInTriangle(Triangulator triRef, int i1, int i2, int i3, int i4) {
boolean inside;
int numericsHOri1;
inside = false;
numericsHOri1 = orientation(triRef, i2, i3, i4);
if (numericsHOri1 >= 0) {
numericsHOri1 = orientation(triRef, i1, i2, i4);
if (numericsHOri1 >= 0) {
numericsHOri1 = orientation(triRef, i3, i1, i4);
if (numericsHOri1 >= 0) inside = true;
}
}
return inside;
}
/**
* This method checks whether point i4 is inside of or on the boundary
* of the triangle i1, i2, i3. it also returns a classification if i4 is
* on the boundary of the triangle (except for the edge i2, i3).
*/
static boolean vtxInTriangle(Triangulator triRef, int i1, int i2, int i3,
int i4, int[] type) {
boolean inside;
int numericsHOri1;
inside = false;
numericsHOri1 = orientation(triRef, i2, i3, i4);
if (numericsHOri1 >= 0) {
numericsHOri1 = orientation(triRef, i1, i2, i4);
if (numericsHOri1 > 0) {
numericsHOri1 = orientation(triRef, i3, i1, i4);
if (numericsHOri1 > 0) {
inside = true;
type[0] = 0;
}
else if (numericsHOri1 == 0) {
inside = true;
type[0] = 1;
}
}
else if (numericsHOri1 == 0) {
numericsHOri1 = orientation(triRef, i3, i1, i4);
if (numericsHOri1 > 0) {
inside = true;
type[0] = 2;
}
else if (numericsHOri1 == 0) {
inside = true;
type[0] = 3;
}
}
}
return inside;
}
/**
* Checks whether the line segments i1, i2 and i3, i4 intersect. no
* intersection is reported if they intersect at a common vertex.
* the function assumes that i1 <= i2 and i3 <= i4. if i3 or i4 lies
* on i1, i2 then an intersection is reported, but no intersection is
* reported if i1 or i2 lies on i3, i4. this function is not symmetric!
*/
static boolean segIntersect(Triangulator triRef, int i1, int i2, int i3,
int i4, int i5) {
int ori1, ori2, ori3, ori4;
// if((triRef.inPointsList(i1)==false)||(triRef.inPointsList(i2)==false)||
// (triRef.inPointsList(i3)==false)||(triRef.inPointsList(i4)==false))
// System.out.println("Numerics.segIntersect Not inPointsList " + i1 + " " + i2
// + " " + i3 + " " + i4);
//
// if((i1 > i2) || (i3 > i4))
// System.out.println("Numerics.segIntersect i1>i2 or i3>i4 " + i1 + " " + i2
// + " " + i3 + " " + i4);
if ((i1 == i2) || (i3 == i4)) return false;
if ((i1 == i3) && (i2 == i4)) return true;
if ((i3 == i5) || (i4 == i5)) ++(triRef.identCntr);
ori3 = orientation(triRef, i1, i2, i3);
ori4 = orientation(triRef, i1, i2, i4);
if (((ori3 == 1) && (ori4 == 1)) ||
((ori3 == -1) && (ori4 == -1))) return false;
if (ori3 == 0) {
if (strictlyInBetween(i1, i2, i3)) return true;
if (ori4 == 0) {
if (strictlyInBetween(i1, i2, i4)) return true;
}
else return false;
}
else if (ori4 == 0) {
if (strictlyInBetween(i1, i2, i4)) return true;
else return false;
}
ori1 = orientation(triRef, i3, i4, i1);
ori2 = orientation(triRef, i3, i4, i2);
if (((ori1 <= 0) && (ori2 <= 0)) ||
((ori1 >= 0) && (ori2 >= 0))) return false;
return true;
}
/**
* this function computes a quality measure of a triangle i, j, k.
* it returns the ratio `base / height', where base is the length of the
* longest side of the triangle, and height is the normal distance
* between the vertex opposite of the base side and the base side. (as
* usual, we again use the l1-norm for distances.)
*/
static double getRatio(Triangulator triRef, int i, int j, int k) {
double area, a, b, c, base, ratio;
Point2f p, q, r;
// if((triRef.inPointsList(i)==false)||(triRef.inPointsList(j)==false)||
// (triRef.inPointsList(k)==false))
// System.out.println("Numerics.getRatio: Not inPointsList " + i + " " + j
// + " " + k);
p = triRef.points[i];
q = triRef.points[j];
r = triRef.points[k];
a = baseLength(p, q);
b = baseLength(p, r);
c = baseLength(r, q);
base = max3(a, b, c);
if ((10.0 * a) < Math.min(b, c)) return 0.1;
area = stableDet2D(triRef, i, j, k);
if (lt(area, triRef.epsilon)) {
area = -area;
}
else if (!gt(area, triRef.epsilon)) {
if (base > a) return 0.1;
else return Double.MAX_VALUE;
}
ratio = base * base / area;
if (ratio < 10.0) return ratio;
else {
if (a < base) return 0.1;
else return ratio;
}
}
static int spikeAngle(Triangulator triRef, int i, int j, int k, int ind) {
int ind1, ind2, ind3;
int i1, i2, i3;
// if((triRef.inPointsList(i)==false)||(triRef.inPointsList(j)==false)||
// (triRef.inPointsList(k)==false))
// System.out.println("Numerics.spikeAngle: Not inPointsList " + i + " " + j
// + " " + k);
ind2 = ind;
i2 = triRef.fetchData(ind2);
// if(i2 != j)
// System.out.println("Numerics.spikeAngle: i2 != j " + i2 + " " + j );
ind1 = triRef.fetchPrevData(ind2);
i1 = triRef.fetchData(ind1);
// if(i1 != i)
// System.out.println("Numerics.spikeAngle: i1 != i " + i1 + " " + i );
ind3 = triRef.fetchNextData(ind2);
i3 = triRef.fetchData(ind3);
// if(i3 != k)
// System.out.println("Numerics.spikeAngle: i3 != k " + i3 + " " + k );
return recSpikeAngle(triRef, i, j, k, ind1, ind3);
}
static int recSpikeAngle(Triangulator triRef, int i1, int i2, int i3,
int ind1, int ind3) {
int ori, ori1, ori2, i0, ii1, ii2;
Point2f pq, pr;
double dot;
if (ind1 == ind3) {
// all points are collinear??? well, then it does not really matter
// which angle is returned. perhaps, -2 is the best bet as my code
// likely regards this contour as a hole.
return -2;
}
if (i1 != i3) {
if (i1 < i2) {
ii1 = i1;
ii2 = i2;
}
else {
ii1 = i2;
ii2 = i1;
}
if (inBetween(ii1, ii2, i3)) {
i2 = i3;
ind3 = triRef.fetchNextData(ind3);
i3 = triRef.fetchData(ind3);
if (ind1 == ind3) return 2;
ori = orientation(triRef, i1, i2, i3);
if (ori > 0) return 2;
else if (ori < 0) return -2;
else return recSpikeAngle(triRef, i1, i2, i3, ind1, ind3);
}
else {
i2 = i1;
ind1 = triRef.fetchPrevData(ind1);
i1 = triRef.fetchData(ind1);
if (ind1 == ind3) return 2;
ori = orientation(triRef, i1, i2, i3);
if (ori > 0) return 2;
else if (ori < 0) return -2;
else return recSpikeAngle(triRef, i1, i2, i3, ind1, ind3);
}
}
else {
i0 = i2;
i2 = i1;
ind1 = triRef.fetchPrevData(ind1);
i1 = triRef.fetchData(ind1);
if (ind1 == ind3) return 2;
ind3 = triRef.fetchNextData(ind3);
i3 = triRef.fetchData(ind3);
if (ind1 == ind3) return 2;
ori = orientation(triRef, i1, i2, i3);
if (ori > 0) {
ori1 = orientation(triRef, i1, i2, i0);
if (ori1 > 0) {
ori2 = orientation(triRef, i2, i3, i0);
if (ori2 > 0) return -2;
}
return 2;
}
else if (ori < 0) {
ori1 = orientation(triRef, i2, i1, i0);
if (ori1 > 0) {
ori2 = orientation(triRef, i3, i2, i0);
if (ori2 > 0) return 2;
}
return -2;
}
else {
pq = new Point2f();
Basic.vectorSub2D(triRef.points[i1], triRef.points[i2], pq);
pr = new Point2f();
Basic.vectorSub2D(triRef.points[i3], triRef.points[i2], pr);
dot = Basic.dotProduct2D(pq, pr);
if (dot < 0.0) {
ori = orientation(triRef, i2, i1, i0);
if (ori > 0) return 2;
else return -2;
}
else {
return recSpikeAngle(triRef, i1, i2, i3, ind1, ind3);
}
}
}
}
/**
* computes the signed angle between p, p1 and p, p2.
*
* warning: this function does not handle a 180-degree angle correctly!
* (this is no issue in our application, as we will always compute
* the angle centered at the mid-point of a valid diagonal.)
*/
static double angle(Triangulator triRef, Point2f p, Point2f p1, Point2f p2) {
int sign;
double angle1, angle2, angle;
Point2f v1, v2;
sign = Basic.signEps(Basic.det2D(p2, p, p1), triRef.epsilon);
if (sign == 0) return 0.0;
v1 = new Point2f();
v2 = new Point2f();
Basic.vectorSub2D(p1, p, v1);
Basic.vectorSub2D(p2, p, v2);
angle1 = Math.atan2(v1.y, v1.x);
angle2 = Math.atan2(v2.y, v2.x);
if (angle1 < 0.0) angle1 += 2.0*Math.PI;
if (angle2 < 0.0) angle2 += 2.0*Math.PI;
angle = angle1 - angle2;
if (angle > Math.PI) angle = 2.0*Math.PI - angle;
else if (angle < -Math.PI) angle = 2.0*Math.PI + angle;
if (sign == 1) {
if (angle < 0.0) return -angle;
else return angle;
}
else {
if (angle > 0.0) return -angle;
else return angle;
}
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/package.html 0000644 0000000 0000000 00000000475 10327775622 027736 0 ustar root root
*
*
* @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 Java 3D API Specification.
*
* @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 Java 3D API Specification.
*
* @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 Java 3D API Specification.
*
* @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 byReference flag
* set to true. 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.
* true if the data access mode for this
* CompressedGeometryData object is by-reference;
* false 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.
*
* size bytes of compressed geometry data are copied from the
* offset indicated by start 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 start field set to 0.
*/
public int start;
/**
* A point that defines the lower bound of the x,
* y, and z 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 x,
* y, and z 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 + " ";
}
}
}
././@LongLink 0000000 0000000 0000000 00000000167 00000000000 011571 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamVertex.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/Compressio0000644 0000000 0000000 00000026610 10563126526 032056 0 ustar root root /*
* $RCSfile: CompressionStreamVertex.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:22 $
* $State: Exp $
*/
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 ;
}
}
././@LongLink 0000000 0000000 0000000 00000000160 00000000000 011562 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedStrip.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/Generalize0000644 0000000 0000000 00000070133 10563126526 032017 0 ustar root root /*
* $RCSfile: GeneralizedStrip.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:22 $
* $State: Exp $
*/
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) ;
}
}
././@LongLink 0000000 0000000 0000000 00000000162 00000000000 011564 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryCompressor.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/GeometryCo0000644 0000000 0000000 00000016565 10563126526 032020 0 ustar root root /*
* $RCSfile: GeometryCompressor.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:22 $
* $State: Exp $
*/
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()) ;
}
}
././@LongLink 0000000 0000000 0000000 00000000152 00000000000 011563 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/MeshBuffer.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/MeshBuffer0000644 0000000 0000000 00000020147 10563126527 031761 0 ustar root root /*
* $RCSfile: MeshBuffer.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:23 $
* $State: Exp $
*/
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)] ;
}
}
././@LongLink 0000000 0000000 0000000 00000000165 00000000000 011567 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/GeneralizedStripFlags.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/Generalize0000644 0000000 0000000 00000007303 10563126526 032016 0 ustar root root /*
* $RCSfile: GeneralizedStripFlags.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:22 $
* $State: Exp $
*/
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) ;
}
././@LongLink 0000000 0000000 0000000 00000000167 00000000000 011571 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamNormal.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/Compressio0000644 0000000 0000000 00000053657 10563126526 032071 0 ustar root root /*
* $RCSfile: CompressionStreamNormal.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:22 $
* $State: Exp $
*/
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") ;
}
}
././@LongLink 0000000 0000000 0000000 00000000170 00000000000 011563 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/CompressionStreamElement.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/compression/Compressio0000644 0000000 0000000 00000026400 10563126526 032053 0 ustar root root /*
* $RCSfile: CompressionStreamElement.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.3 $
* $Date: 2007/02/09 17:20:22 $
* $State: Exp $
*/
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.
*
* The NormalGenerator adds normals to geometry without normals.
*
* Also, the GeometryCompressor can take a set of GeometryInfo objects in a
* CompressionSteam and generate a CompressedGeometry object from the
* geometry.
*
* @see NormalGenerator
* @see Stripifier
* @see com.sun.j3d.utils.compression.CompressionStream
* @see com.sun.j3d.utils.compression.GeometryCompressor
* @see javax.media.j3d.GeometryArray
*/
public class GeometryInfo {
/**
* Send to the constructor to inform that the data will be arranged so
* that each set of three vertices form an independent triangle
*/
public static final int TRIANGLE_ARRAY = 1;
/**
* Send to the constructor to inform that the data will be arranged so
* that each set of four vertices form an independent quad
*/
public static final int QUAD_ARRAY = 2;
/**
* Send to the constructor to inform that the data will be arranged so
* that the stripCounts array indicates how many vertices to use
* for each triangle fan.
*/
public static final int TRIANGLE_FAN_ARRAY = 3;
/**
* Send to the constructor to inform that the data will be arranged so
* that the stripCounts array indicates how many vertices to use
* for each triangle strip.
*/
public static final int TRIANGLE_STRIP_ARRAY = 4;
/**
* Send to the constructor to inform that the data is arranged as
* possibly multi-contour, possible non-planar polygons.
* The stripCounts array indicates how many vertices to use
* for each contour, and the contourCounts array indicates how many
* stripCounts entries to use for each polygon. The first
* contour is the bounding polygon, and subsequent contours are
* "holes." If contourCounts is left null, the default is
* one contour per polygon.
*/
public static final int POLYGON_ARRAY = 5;
private int prim;
// 1 Show indexification details
private static final int DEBUG = 0;
private Point3f coordinates[] = null;
private Color3f colors3[] = null;
private Color4f colors4[] = null;
private Vector3f normals[] = null;
private Object texCoordSets[][] = null;
private int coordinateIndices[] = null;
private int colorIndices[] = null;
private int normalIndices[] = null;
private int texCoordIndexSets[][] = null;
private int[] texCoordSetMap = null;
private int texCoordSetCount = 0;
private int texCoordDim = 0;
private int stripCounts[] = null;
private int contourCounts[] = null;
private Triangulator tr = null;
private NormalGenerator ng = null;
private int oldPrim = 0;
private int oldStripCounts[] = null;
private boolean coordOnly = false;
/**
* Constructor.
* Creates an empty GeometryInfo object.
* @param primitive Tells the GeometryInfo object the type of
* primitive data to be stored
* in it, so it will know the format of the data. It can be one of
* TRIANGLE_ARRAY,
* QUAD_ARRAY, TRIANGLE_FAN_ARRAY, TRIANGLE_STRIP_ARRAY, or POLYGON_ARRAY.
*/
public GeometryInfo(int primitive)
{
if ((primitive >= TRIANGLE_ARRAY) && (primitive <= POLYGON_ARRAY)) {
prim = primitive;
} else {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo0"));
}
} // End of GeometryInfo(int)
/**
* Contructor. Populates the GeometryInfo with the geometry from
* the GeometryArray.
* GeometryInfo gi = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
* // initialize the geometry info here
* // generate normals
* NormalGenerator ng = new NormalGenerator();
* ng.generateNormals(gi);
* // stripify
* Stripifier st = new Stripifier();
* st.stripify(gi);
* GeometryArray result = gi.getGeometryArray();
*
Initial and
* Valid GeometryArray methods (
* setInitialVertexIndex() and setValidVertexCount()
* and their cousins) then only the needed geometry
* is copied into the GeometryInfo.
*/
public GeometryInfo(GeometryArray ga)
{
GeometryInfoGenerator.create(this, ga);
} // End of GeometryInfo(GeometryArray)
/**
* Removes all data from the GeometryInfo and resets the primitive.
* After a call to reset(), the GeometryInfo object will be just like
* it was when it was newly constructed.
* @param primitive Either TRIANGLE_ARRAY, QUAD_ARRAY,
* TRIANGLE_FAN_ARRAY, TRIANGLE_STRIP_ARRAY, or POLYGON_ARRAY.
* Tells the GeometryInfo object the type of primitive data to be stored
* in it, so it will know the format of the data.
*/
public void reset(int primitive)
{
if ((primitive >= TRIANGLE_ARRAY) && (primitive <= POLYGON_ARRAY)) {
prim = primitive;
} else {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo0"));
}
coordinates = null;
colors3 = null;
colors4 = null;
normals = null;
coordinateIndices = null;
colorIndices = null;
normalIndices = null;
stripCounts = null;
contourCounts = null;
oldPrim = 0;
oldStripCounts = null;
texCoordDim = 0;
texCoordSetCount = 0;
texCoordSets = null;
texCoordIndexSets = null;
texCoordSetMap = null;
coordOnly = false;
} // End of reset(int)
/**
* Removes all data from this GeometryInfo and populates it with
* the geometry from the GeometryArray.
*/
public void reset(GeometryArray ga)
{
GeometryInfoGenerator.create(this, ga);
} // End of reset(GeometryArray)
// This method takes an indexed quad array and expands it to
// a list of indexed triangles. It is used for the Coordinate
// indices as well as the color and texture indices.
private int[] expandQuad(int indices[])
{
int triangles[] = new int[indices.length / 4 * 6];
for (int i = 0 ; i < indices.length / 4 ; i++ ) {
triangles[i * 6 + 0] = indices[i * 4];
triangles[i * 6 + 1] = indices[i * 4 + 1];
triangles[i * 6 + 2] = indices[i * 4 + 2];
triangles[i * 6 + 3] = indices[i * 4];
triangles[i * 6 + 4] = indices[i * 4 + 2];
triangles[i * 6 + 5] = indices[i * 4 + 3];
}
return triangles;
} // End of expandQuad
// This method takes an indexed triangle fan and expands it to
// a list of indexed triangles. It is used for the Coordinate
// indices as well as the color and texture indices.
private int[] expandTriFan(int numTris, int indices[])
{
int triangles[] = new int[numTris * 3];
int p = 0;
int base = 0;
for (int f = 0 ; f < stripCounts.length ; f++) {
for (int t = 0 ; t < stripCounts[f] - 2 ; t++) {
triangles[p++] = indices[base];
triangles[p++] = indices[base + t + 1];
triangles[p++] = indices[base + t + 2];
}
base += stripCounts[f];
}
return triangles;
} // End of expandTriFan
// This method takes an indexed triangle strip and expands it to
// a list of indexed triangles. It is used for the Coordinate
// indices as well as the color and texture indices.
private int[] expandTriStrip(int numTris, int indices[])
{
int triangles[] = new int[numTris * 3];
int p = 0;
int base = 0;
for (int s = 0 ; s < stripCounts.length ; s++) {
for (int t = 0 ; t < stripCounts[s] - 2 ; t++) {
// Use a ping-ponging algorithm to reverse order on every other
// triangle to preserve winding
if (t % 2 == 0) {
triangles[p++] = indices[base + t + 0];
triangles[p++] = indices[base + t + 1];
triangles[p++] = indices[base + t + 2];
} else {
triangles[p++] = indices[base + t + 0];
triangles[p++] = indices[base + t + 2];
triangles[p++] = indices[base + t + 1];
}
}
base += stripCounts[s];
}
return triangles;
} // End of expandTriStrip
// Used by the NormalGenerator utility. Informs the GeometryInfo object
// to remember its current primitive and stripCounts arrays so that
// they can be used to convert the object back to its original
// primitive
void rememberOldPrim()
{
oldPrim = prim;
oldStripCounts = stripCounts;
} // End of rememberOldPrim
// The NormalGenerator needs to know the original primitive for
// facet normal generation for quads
int getOldPrim()
{
return oldPrim;
} // End of getOldPrim
// Used by the Utility libraries other than the NormalGenerator.
// Informs the GeometryInfo object that the geometry need not
// be converted back to the original primitive before returning.
// For example, if a list of Fans is sent, converted to Triangles
// for normal generation, and then stripified by the Stripifyer,
// we want to make sure that GeometryInfo doesn't convert the
// geometry *back* to fans before creating the output GeometryArray.
void forgetOldPrim()
{
oldPrim = 0;
oldStripCounts = null;
} // End of forgetOldPrim
// We have changed the user's data from their original primitive
// type to TRIANGLE_ARRAY. If this method is being called, it
// means we need to change it back (to try and hide from the user
// the fact that we've converted). This usually happens when
// the user has used GeometryInfo for generating normals, but
// they are not Stripifying or Triangulating. The function is
// called from getGeometryArray before creating the output data.
private void changeBackToOldPrim()
{
if (oldPrim != 0) {
convertToIndexedTriangles();
if (ng == null) ng = new NormalGenerator();
ng.convertBackToOldPrim(this, oldPrim, oldStripCounts);
oldPrim = 0;
oldStripCounts = null;
}
} // End of changeBackToOldPrim
/**
* Convert the GeometryInfo object to have primitive type TRIANGLE_ARRAY
* and be indexed.
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public void convertToIndexedTriangles()
{
int triangles = 0;
// This calls checkForBadData
indexify();
if (prim == TRIANGLE_ARRAY) return;
switch(prim) {
case QUAD_ARRAY:
coordinateIndices = expandQuad(coordinateIndices);
if (colorIndices != null) colorIndices = expandQuad(colorIndices);
if (normalIndices != null)
normalIndices = expandQuad(normalIndices);
for (int i = 0 ; i < texCoordSetCount ; i++)
texCoordIndexSets[i] = expandQuad(texCoordIndexSets[i]);
break;
case TRIANGLE_FAN_ARRAY:
// Count how many triangles are in the object
for (int i = 0 ; i < stripCounts.length ; i++) {
triangles += stripCounts[i] - 2;
}
coordinateIndices = expandTriFan(triangles, coordinateIndices);
if (colorIndices != null)
colorIndices = expandTriFan(triangles, colorIndices);
if (normalIndices != null)
normalIndices = expandTriFan(triangles, normalIndices);
for (int i = 0 ; i < texCoordSetCount ; i++)
texCoordIndexSets[i] = expandTriFan(triangles,
texCoordIndexSets[i]);
break;
case TRIANGLE_STRIP_ARRAY:
// Count how many triangles are in the object
for (int i = 0 ; i < stripCounts.length ; i++) {
triangles += stripCounts[i] - 2;
}
coordinateIndices = expandTriStrip(triangles, coordinateIndices);
if (colorIndices != null)
colorIndices = expandTriStrip(triangles, colorIndices);
if (normalIndices != null)
normalIndices = expandTriStrip(triangles, normalIndices);
for (int i = 0 ; i < texCoordSetCount ; i++)
texCoordIndexSets[i] = expandTriStrip(triangles,
texCoordIndexSets[i]);
break;
case POLYGON_ARRAY:
if (tr == null) tr = new Triangulator();
tr.triangulate(this);
break;
}
prim = TRIANGLE_ARRAY;
stripCounts = null;
} // End of convertToIndexedTriangles
/**
* Get the current primitive. Some of the utilities may change the
* primitive type of the data stored in the GeometryInfo object
* (for example, the stripifyer will change it to TRIANGLE_STRIP_ARRAY).
*/
public int getPrimitive()
{
return prim;
} // End of getPrimitive()
/**
* Set the current primitive. Some of the utilities may change the
* primitive type of the data stored in the GeometryInfo object
* (for example, the stripifyer will change it to TRIANGLE_STRIP_ARRAY).
* But the user can't change the primitive type - it is set in the
* constructor. Therefore, this method has package scope.
*/
void setPrimitive(int primitive)
{
if ((prim >= TRIANGLE_ARRAY) && (prim <= POLYGON_ARRAY)) {
prim = primitive;
} else {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo0"));
}
} // End of setPrimitive()
/**
* Sets the coordinates array.
* No data copying is done because a reference to user data is used.
*/
public void setCoordinates(Point3f coordinates[])
{
this.coordinates = coordinates;
} // End of setCoordinates
/**
* Sets the coordinates array.
* The points are copied into the GeometryInfo object.
*/
public void setCoordinates(Point3d coordinates[])
{
if (coordinates == null) this.coordinates = null;
else {
this.coordinates = new Point3f[coordinates.length];
for (int i = 0 ; i < coordinates.length ; i++) {
this.coordinates[i] = new Point3f(
(float)(coordinates[i].x),
(float)(coordinates[i].y),
(float)(coordinates[i].z));
}
}
} // End of setCoordinates
/**
* Sets the coordinates array.
* The points are copied into the GeometryInfo object.
*/
public void setCoordinates(float coordinates[])
{
if (coordinates == null) this.coordinates = null;
else {
this.coordinates = new Point3f[coordinates.length / 3];
for (int i = 0 ; i < this.coordinates.length ; i++) {
this.coordinates[i] = new Point3f(coordinates[i * 3],
coordinates[i * 3 + 1],
coordinates[i * 3 + 2]);
}
}
} // End of setCoordinates
/**
* Sets the coordinates array.
* The points are copied into the GeometryInfo object.
*/
public void setCoordinates(double coordinates[])
{
if (coordinates == null) this.coordinates = null;
else {
this.coordinates = new Point3f[coordinates.length / 3];
for (int i = 0 ; i < coordinates.length / 3 ; i++) {
this.coordinates[i] = new Point3f((float)coordinates[i * 3],
(float)coordinates[i * 3 + 1],
(float)coordinates[i * 3 + 2]);
}
}
} // End of setCoordinates
/**
* Retrieves a reference to the coordinate array.
*/
public Point3f[] getCoordinates()
{
return coordinates;
} // End of getCoordinates
/**
* Sets the colors array.
* No data copying is done because a reference to
* user data is used.
*/
public void setColors(Color3f colors[])
{
colors3 = colors;
colors4 = null;
} // End of setColors
/**
* Sets the colors array.
* No data copying is done because a reference to
* user data is used.
*/
public void setColors(Color4f colors[])
{
colors3 = null;
colors4 = colors;
} // End of setColors
/**
* Sets the colors array.
* The points are copied into the GeometryInfo object.
*/
public void setColors(Color3b colors[])
{
if (colors == null) {
colors3 = null;
colors4 = null;
} else {
colors3 = new Color3f[colors.length];
colors4 = null;
for (int i = 0 ; i < colors.length ; i++) {
colors3[i] = new Color3f((float) (colors[i].x & 0xff) / 255.0f,
(float) (colors[i].y & 0xff) / 255.0f,
(float) (colors[i].z & 0xff) / 255.0f);
}
}
} // End of setColors
/**
* Sets the colors array.
* The points are copied into the GeometryInfo object.
*/
public void setColors(Color4b colors[])
{
if (colors == null) {
colors3 = null;
colors4 = null;
} else {
colors3 = null;
colors4 = new Color4f[colors.length];
for (int i = 0 ; i < colors.length ; i++) {
colors4[i] = new Color4f((float) (colors[i].x & 0xff) / 255.0f,
(float) (colors[i].y & 0xff) / 255.0f,
(float) (colors[i].z & 0xff) / 255.0f,
(float) (colors[i].w & 0xff) / 255.0f);
}
}
} // End of setColors
/**
* Sets the colors array.
* The points are copied into the GeometryInfo object, assuming
* 3 components (R, G, and B) per vertex.
*/
public void setColors3(float colors[])
{
if (colors == null) {
colors3 = null;
colors4 = null;
} else {
colors3 = new Color3f[colors.length / 3];
colors4 = null;
for (int i = 0 ; i < colors.length / 3 ; i++) {
colors3[i] = new Color3f(colors[i * 3],
colors[i * 3 + 1],
colors[i * 3 + 2]);
}
}
} // End of setColors3
/**
* Sets the colors array.
* The points are copied into the GeometryInfo object, assuming
* 4 components (R, G, B, and A) per vertex.
*/
public void setColors4(float colors[])
{
if (colors == null) {
colors3 = null;
colors4 = null;
} else {
colors3 = null;
colors4 = new Color4f[colors.length / 4];
for (int i = 0 ; i < colors.length / 4 ; i++) {
colors4[i] = new Color4f(colors[i * 4],
colors[i * 4 + 1],
colors[i * 4 + 2],
colors[i * 4 + 3]);
}
}
} // End of setColors4
/**
* Sets the colors array.
* The points are copied into the GeometryInfo object, assuming
* 3 components (R, G, and B) per vertex.
*/
public void setColors3(byte colors[])
{
if (colors == null) {
colors3 = null;
colors4 = null;
} else {
colors3 = new Color3f[colors.length / 3];
colors4 = null;
for (int i = 0 ; i < colors.length / 3 ; i++) {
colors3[i] =
new Color3f((float)(colors[i * 3] & 0xff) / 255.0f,
(float)(colors[i * 3 + 1] & 0xff) / 255.0f,
(float)(colors[i * 3 + 2] & 0xff) / 255.0f);
}
}
} // End of setColors3
/**
* Sets the colors array.
* The points are copied into the GeometryInfo object, assuming
* 4 components (R, G, B, and A) per vertex.
*/
public void setColors4(byte colors[])
{
if (colors == null) {
colors3 = null;
colors4 = null;
} else {
colors3 = null;
colors4 = new Color4f[colors.length / 4];
for (int i = 0 ; i < colors.length / 4 ; i++) {
colors4[i] =
new Color4f((float)(colors[i * 4] & 0xff) / 255.0f,
(float)(colors[i * 4 + 1] & 0xff) / 255.0f,
(float)(colors[i * 4 + 2] & 0xff) / 255.0f,
(float)(colors[i * 4 + 3] & 0xff) / 255.0f);
}
}
} // End of setColors4
/**
* Retrieves a reference to the colors array. Will be either
* Color3f[] or Color4f[] depending on
* the type of the input data. Call
* getNumColorComponents() to find out which version is returned.
*/
public Object[] getColors()
{
if (colors3 != null) return colors3;
else return colors4;
} // End of getColors
/**
* Returns the number of color data components stored per vertex
* in the current GeometryInfo object (3 for RGB or 4 for RGBA).
* If no colors are currently defined, 0 is returned.
*/
public int getNumColorComponents()
{
if (colors3 != null) return 3;
else if (colors4 != null) return 4;
else return 0;
} // End of getNumColorComponents
/**
* Sets the normals array.
* No data copying is done because a reference to
* user data is used.
*/
public void setNormals(Vector3f normals[])
{
this.normals = normals;
} // End of setNormals
/**
* Sets the normals array.
* The points are copied into the GeometryInfo object.
*/
public void setNormals(float normals[])
{
if (normals == null) this.normals = null;
else {
this.normals = new Vector3f[normals.length / 3];
for (int i = 0 ; i < this.normals.length ; i++) {
this.normals[i] = new Vector3f(normals[i * 3],
normals[i * 3 + 1],
normals[i * 3 + 2]);
}
}
} // End of setNormals(float[])
/**
* Retrieves a reference to the normal array.
*/
public Vector3f[] getNormals()
{
return normals;
} // End of getNormals
/**
* This method is used to specify the number of texture coordinate sets
* and the dimensionality of the texture coordinates.
* The number of texture coordinate sets must be specified to the GeometryInfo
* class before any of the sets are specified. The dimensionality of the
* texture coordinates may be 2, 3, or 4, corresponding to 2D, 3D, or 4D
* texture coordinates respectively.(All sets must have the same
* dimensionality.) The default is zero, 2D texture coordinate sets.
* This method should be called before any texture coordinate sets are
* specified because calling this method will delete all previously
* specified texture coordinate and texture coordinate index arrays
* associated with this GeometryInfo. For example:
*
* The second call to
* geomInfo.setTextureCoordinateParams(2, 3);
* geomInfo.setTextureCoordinates(0, tex0);
* geomInfo.setTextureCoordinates(1, tex1);
* geomInfo.setTextureCoordinateParams(1, 2);
* geomInfo.getTexCoordSetCount();
*
setTextureCoordinateParams will erase all
* the texture coordinate arrays, so the subsequent call to
* getTexCoordSetCount will return 1.
* @param numSets The number of texture coordinate sets that will be
* specified for this GeometryInfo object.
* @param dim The dimensionality of the texture coordinates. Has to be 2, 3
* or 4.
* @throws IllegalArgumentException if the dimensionality of the texture
* coordinates is not one of 2, 3 or 4.
*/
public void setTextureCoordinateParams(int numSets, int dim)
{
if (dim == 2) {
texCoordSets = new TexCoord2f[numSets][];
} else if (dim == 3) {
texCoordSets = new TexCoord3f[numSets][];
} else if (dim == 4) {
texCoordSets = new TexCoord4f[numSets][];
} else {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo9"));
}
texCoordIndexSets = new int[numSets][];
texCoordDim = dim;
texCoordSetCount = numSets;
} // End of setTextureCoordinateParams
/**
* Returns the number of texture coordinate sets in this GeometryInfo.
* This value is set with setTextureCoordinateParams().
* If setTextureCoordinateParams()
* has not been called, 0 is returned unless one of the deprecated
* texture coordinate methods has been called. Calling one of the
* deprecated texture coordinate methods sets the count to 1.
* The deprecated texture coordinate methods are those that don't
* take texCoordSet as the first parameter.
* @return the number of texture coordinate sets in this
* GeometryInfo.
*/
public int getTexCoordSetCount() {
return texCoordSetCount;
}
/**
* Returns the number of texture coordinate components that are stored
* per vertex. Returns 2 for ST (2D), 3 for STR (3D),
* or 4 for STRQ (4D), aslo known as the "dimensionality" of the
* coordinates. This value is set with
* setTextureCoordinateParams(). If setTextureCoordinateParams()
* has not been called, 0 is returned unless one of the deprecated
* texture coordinate methods has been called. Calling one of the
* deprecated texture coordinate methods sets the dimensionality
* explicitly (if you called setTextureCoordinates(Point2f[]) then
* 2 is returned).
* The deprecated texture coordinate methods are those that don't
* take texCoordSet as the first parameter.
*/
public int getNumTexCoordComponents()
{
return texCoordDim;
} // End of getNumTexCoordComponents
/**
* Sets the mapping between texture coordinate sets and texture units.
* See the
*
* GeometryArray constructor for further details.
* texCoordSet < 0 or
* texCoordSet >= texCoordSetCount,
* or the texture coordinate parameters were not previously set by
* calling setTextureCoordinateParams(texCoordSetCount, 2).
*/
public void setTextureCoordinates(int texCoordSet, TexCoord2f texCoords[])
{
if (texCoordDim != 2)
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo15"));
if ((texCoordSet >= texCoordSetCount) || (texCoordSet < 0))
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo18"));
texCoordSets[texCoordSet] = texCoords;
} // End of setTextureCoordinates(int, TexCoord3f[])
/**
* Sets the TextureCoordinates array by copying the data
* into the GeometryInfo object.
* This method sets the number of texture coordinate sets to 1,
* sets the dimensionality of the texture coordinates to 2,
* and sets the coordinates for texture coordinate set 0.
* @deprecated As of Java 3D 1.3 replaced by
* setTextureCoordinates(int texCoordSet, TexCoord2f coords[])
*/
public void setTextureCoordinates(Point2f texCoords[])
{
texCoordSetCount = 1;
texCoordDim = 2;
texCoordSets = new TexCoord2f[1][];
if (texCoords != null) {
TexCoord2f[] tex = new TexCoord2f[texCoords.length];
for (int i = 0 ; i < texCoords.length ; i++)
tex[i] = new TexCoord2f(texCoords[i]);
texCoordSets[0] = tex;
}
} // End of setTextureCoordinates(Point2f[])
/**
* Sets the texture coordinates array for the specified set.
* No data copying is done - a reference to user data is used.
* @param texCoordSet The texture coordinate set for which these coordinates
* are being specified.
* @param texCoords Array of 3D texture coordinates.
* @throws IllegalArgumentException if texCoordSet < 0 or
* texCoordSet >= texCoordSetCount,
* or the texture coordinate parameters were not previously set by
* calling setTextureCoordinateParams(texCoordSetCount, 3).
*/
public void setTextureCoordinates(int texCoordSet, TexCoord3f texCoords[])
{
if (texCoordDim != 3)
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo16"));
if ((texCoordSet >= texCoordSetCount) || (texCoordSet < 0))
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo18"));
texCoordSets[texCoordSet] = texCoords;
} // End of setTextureCoordinates(int, TexCoord3f[])
/**
* Sets the TextureCoordinates array by copying the data
* into the GeometryInfo object.
* This method sets the number of texture coordinate sets to 1,
* sets the dimensionality of the texture coordinates to 3,
* and sets the coordinates for texture coordinate set 0.
* @deprecated As of Java 3D 1.3 replaced by
* setTextureCoordinates(int texCoordSet, TexCoord3f coords[])
*/
public void setTextureCoordinates(Point3f texCoords[])
{
texCoordSetCount = 1;
texCoordDim = 3;
texCoordSets = new TexCoord3f[1][];
if (texCoords != null) {
TexCoord3f[] tex = new TexCoord3f[texCoords.length];
for (int i = 0 ; i < texCoords.length ; i++)
tex[i] = new TexCoord3f(texCoords[i]);
texCoordSets[0] = tex;
}
} // End of setTextureCoordinates(Point3f[])
/**
* Sets the texture coordinates array for the specified set.
* No data copying is done - a reference to user data is used.
* @param texCoordSet The texture coordinate set for which these coordinates
* are being specified.
* @param texCoords Array of 4D texture coordinates.
* @throws IllegalArgumentException if texCoordSet < 0 or
* texCoordSet >= texCoordSetCount,
* or the texture coordinate parameters were not previously set by
* calling setTextureCoordinateParams(texCoordSetCount, 4).
*/
public void setTextureCoordinates(int texCoordSet, TexCoord4f texCoords[]) {
if (texCoordDim != 4)
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo17"));
if ((texCoordSet >= texCoordSetCount) || (texCoordSet < 0))
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo18"));
texCoordSets[texCoordSet] = texCoords;
} // End of setTextureCoordinates(int, TexCoord4f[])
/**
* Sets the texture coordinates array by copying the data into the
* GeometryInfo object. The number of sets and dimensionality of
* the sets must have been set previously with
* setTextureCoordinateParams(texCoordSetCount, dim).
* @param texCoordSet The texture coordinate set for which these coordinates
* are being specified.
* @param texCoords The float array of texture coordinates. For n texture
* coordinates with dimensionality d, there must be d*n floats in the array.
* @throws IllegalArgumentException if texCoordSet < 0 or
* texCoordSet >= texCoordSetCount,
* or the texture coordinate parameters were not previously set by
* calling setTextureCoordinateParams.
*/
public void setTextureCoordinates(int texCoordSet, float texCoords[])
{
if ((texCoords.length % texCoordDim) != 0)
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo2"));
// Copy the texCoords into this GeometryInfo object
if (texCoordDim == 2) {
TexCoord2f tcoords[] = new TexCoord2f[texCoords.length / 2];
for (int i = 0 ; i < tcoords.length ; i++)
tcoords[i] = new TexCoord2f(texCoords[i * 2],
texCoords[i * 2 + 1]);
setTextureCoordinates(texCoordSet, tcoords);
} else if (texCoordDim == 3) {
TexCoord3f tcoords[] = new TexCoord3f[texCoords.length / 3];
for (int i = 0 ; i < tcoords.length ; i++)
tcoords[i] = new TexCoord3f(texCoords[i * 3],
texCoords[i * 3 + 1],
texCoords[i * 3 + 2]);
setTextureCoordinates(texCoordSet, tcoords);
} else if (texCoordDim == 4) {
TexCoord4f tcoords[] = new TexCoord4f[texCoords.length / 4];
for (int i = 0 ; i < tcoords.length ; i++)
tcoords[i] = new TexCoord4f(texCoords[i * 4],
texCoords[i * 4 + 1],
texCoords[i * 4 + 2],
texCoords[i * 4 + 3]);
setTextureCoordinates(texCoordSet, tcoords);
} else {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo21"));
}
} // End of setTextureCoordinates(int, float[])
/**
* Sets the texture coordinates array by copying the data
* into the GeometryInfo object, assuming two numbers
* (S and T) per vertex.
* This method sets the number of texture coordinate sets to 1,
* sets the dimensionality of the texture coordinates to 2,
* and sets the coordinates for texture coordinate set 0.
* @deprecated As of Java 3D 1.3 replaced by
* setTextureCoordinates(int texCoordSet, float texCoords[])
*/
public void setTextureCoordinates2(float texCoords[])
{
texCoordSetCount = 1;
texCoordDim = 2;
texCoordSets = new TexCoord2f[1][];
setTextureCoordinates(0, texCoords);
} // End of setTextureCoordinates2(float[])
/**
* Sets the TextureCoordinates array by copying the data
* into the GeometryInfo object, assuming three numbers
* (S, T, & R) per vertex.
* This method sets the number of texture coordinate sets to 1,
* sets the dimensionality of the texture coordinates to 3,
* and sets the coordinates for texture coordinate set 0.
* @deprecated As of Java 3D 1.3 replaced by
* setTextureCoordinates(int texCoordSet, float texCoords[])
*/
public void setTextureCoordinates3(float texCoords[])
{
texCoordSetCount = 1;
texCoordDim = 3;
texCoordSets = new TexCoord3f[1][];
setTextureCoordinates(0, texCoords);
} // End of setTextureCoordinates3(float[])
/**
* Returns a reference to the indicated texture coordinate array.
* The return type will be TexCoord2f[], TexCoord3f[]
* , or TexCoord4f[] depending on the
* current dimensionality of the texture coordinates in the GeometryInfo
* object. Use getNumTexCoordComponents() to find out which
* version is returned.
* @param texCoordSet The index of the texture coordinate set to
* retrieve.
* @return An array of texture coordinates at the specified index
* @throws IllegalArgumentException If texCoordSet < 0
* or texCoordSet >= texCoordSetCount
*/
public Object[] getTextureCoordinates(int texCoordSet)
{
if ((texCoordSet >= texCoordSetCount) || (texCoordSet < 0))
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo18"));
return texCoordSets[texCoordSet];
} // End of getTextureCoordinates(int)
/**
* Retrieves a reference to texture coordinate set 0.
* The return type will be TexCoord2f[], TexCoord3f[]
* , or TexCoord4f[] depending on the
* current dimensionality of the texture coordinates in the GeometryInfo
* object. Use getNumTexCoordComponents() to find out which
* version is returned. Equivalent to getTextureCoordinates(0).
* @return An array of texture coordinates for set 0.
* @deprecated As of Java 3D 1.3 replaced by
* getTextureCoordinates(int texCoordSet)
*/
public Object[] getTextureCoordinates()
{
return texCoordSets[0];
} // End of getTextureCoordinates()
/**
* Sets the array of indices into the Coordinate array.
* No data copying is done - a reference to user data is used.
*/
public void setCoordinateIndices(int coordinateIndices[])
{
this.coordinateIndices = coordinateIndices;
} // End of setCoordinateIndices
/**
* Retrieves a reference to the array of indices into the
* coordinate array.
texCoordSet < 0 or
* texCoordSet >= texCoordSetCount.
*/
public void setTextureCoordinateIndices(int texCoordSet, int texIndices[])
{
if ((texCoordSet >= texCoordSetCount) || (texCoordSet < 0))
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo18"));
// Texture coordinates are indexed
texCoordIndexSets[texCoordSet] = texIndices;
} // End of setTextureCoordinateIndices(int, int[])
/**
* Sets the array of indices into texture coordinate set 0. Do not
* call this method if you are using more than one set of texture
* coordinates.
* No data is copied - a reference to the user data is used.
* @deprecated As of Java 3D 1.3 replaced by
* setTextureCoordinateIndices(int texCoordSet, int indices[])
* @throws IllegalArgumentException If texCoordSetCount > 1.
*/
public void setTextureCoordinateIndices(int texIndices[])
{
if (texCoordSetCount > 1)
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo1"));
texCoordIndexSets = new int[1][];
texCoordIndexSets[0] = texIndices;
} // End of setTextureCoordinateIndices(int[])
/**
* Retrieves a reference to the specified array of texture
* coordinate indices.* * This method should be considered for advanced users only. * Novice users should just use getGeometryArray() to retrieve * their data so that the internal format of GeometryInfo is * of no concern.
* * Depending on which of the utility routines you've called * on your GeometryInfo object, the results may not be what you * expect. If you've called the Stripifier, your GeometryInfo * object's Primitive has been changed to indexed TRIANGLE_STRIP_ARRAY * and your data will be formatted accordingly. Similarly, if * you've called the Triangulator, your data is in indexed * TRIANGLE_ARRAY format. Generating normals with the NormalGenerator * utility will convert your data to indexed TRIANGLE_ARRAY also, * but if you call getGeometryArray without calling the Stripifier or * Triangulator, your data will be converted back to the original * primitive type when creating the GeometryArray object to pass * back. However, if your creaseAngle was not Math.PI (no creases - * smooth shading), then the introduction of * creases into your model may have split primitives, lengthening * the StripCounts and index arrays from your original data. * @param texCoordSet The texture coordinate index set to be * retrieved. * @return Integer array of the texture coordinate indices for the specified * set. */ public int[] getTextureCoordinateIndices(int texCoordSet) { return texCoordIndexSets[texCoordSet]; } /** * Returns a reference to texture coordinate index set 0. * Equivalent to *getTextureCoordinateIndices(0).
* @deprecated As of Java 3D 1.3 replaced by
* int[] getTextureCoordinateIndices(int texCoordSet)
* @return Integer array of the texture coordinate indices for set 0
*/
public int[] getTextureCoordinateIndices()
{
if (texCoordIndexSets == null) return null;
return texCoordIndexSets[0];
} // End of getTextureCoordinateIndices()
/**
* Sets the array of strip counts. If index lists have been set for
* this GeomteryInfo object then the data is indexed and the stripCounts
* are like stripIndexCounts. If no index lists have been set then
* the data is non-indexed and the stripCounts are like
* stripVertexCounts.
* @see GeometryStripArray#GeometryStripArray(int, int,
* int[] stripVertexCounts)
* @see IndexedGeometryStripArray#IndexedGeometryStripArray(int, int, int,
* int[] stripIndexCounts)
*/
public void setStripCounts(int stripCounts[])
{
this.stripCounts = stripCounts;
} // End of setStripCounts
/**
* Retrieves a reference to the array of stripCounts.
*
* This method should be considered for advanced users only.
* Novice users should just use getGeometryArray() to retrieve
* their data so that the internal format of GeometryInfo is
* of no concern.
*
* Depending on which of the utility routines you've called
* on your GeometryInfo object, the results may not be what you
* expect. If you've called the Stripifier, your GeometryInfo
* object's Primitive has been changed to indexed TRIANGLE_STRIP_ARRAY
* and your data will be formatted accordingly. Similarly, if
* you've called the Triangulator, your data is in indexed
* TRIANGLE_ARRAY format. Generating normals with the NormalGenerator
* utility will convert your data to indexed TRIANGLE_ARRAY also,
* but if you call getGeometryArray without calling the Stripifier or
* Triangulator, your data will be converted back to the original
* primitive type when creating the GeometryArray object to pass
* back. However, if your creaseAngle was not Math.PI (no creases -
* smooth shading), then the introduction of
* creases into your model may have split primitives, lengthening
* the StripCounts and index arrays from your original data.
*/
public int[] getStripCounts()
{
return stripCounts;
} // End of getStripCounts
/**
* Sets the list of contour counts. Only used with the POLYGON_ARRAY
* primitive. Polygons can be made of several vertex lists
* called contours. The first list is the polygon, and
* subsequent lists are "holes" that are removed from the
* polygon. All of the holes must be contained entirely
* within the polygon.
*/
public void setContourCounts(int contourCounts[])
{
this.contourCounts = contourCounts;
} // End of setContourCounts
/**
* Retrieves a reference to the array of contourCounts.
*/
public int[] getContourCounts()
{
return contourCounts;
} // End of getContourCounts
/*
* This routine will return an index list for any array of objects.
*/
int[] getListIndices(Object list[])
{
// Create list of indices to return
int indices[] = new int[list.length];
// Create hash table with initial capacity equal to the number
// of components (assuming about half will be duplicates)
HashMap table = new HashMap(list.length);
Integer idx;
for (int i = 0 ; i < list.length ; i++) {
// Find index associated with this object
idx = (Integer)table.get(list[i]);
if (idx == null) {
// We haven't seen this object before
indices[i] = i;
// Put into hash table and remember the index
table.put(list[i], new Integer(i));
} else {
// We've seen this object
indices[i] = idx.intValue();
}
}
return indices;
} // End of getListIndices
// Class to hash 'size' integers
private class IndexRow {
int[] val;
int size;
private static final int HASHCONST = 0xBABEFACE;
public int hashCode()
{
int bits = 0;
for (int i = 0 ; i < size ; i++) {
bits ^= (bits * HASHCONST) << 2;
}
return bits;
} // End of IndexRow.hashCode
public boolean equals(Object obj)
{
for (int i = 0 ; i < size ; i++) {
if (((IndexRow)obj).get(i) != val[i]) return false;
}
return true;
} // End of IndexRow.equals()
public int get(int index)
{
return val[index];
} // End of IndexRow.get
public void set(int index, int value)
{
val[index] = value;
} // End of IndexRow.set
IndexRow(int numColumns)
{
size = numColumns;
val = new int[size];
} // End of IndexRow constructor
} // End of class IndexRow
/**
* Create index lists for all data lists.
* Identical data entries are guaranteed to
* use the same index value. Does not remove unused data values
* from the object - call compact() to do this.
* @param useCoordIndexOnly Reformat the data into the
* GeometryArray.USE_COORD_INDEX_ONLY format where there is only
* one index list. If the data is already in the USE_COORD_INDEX_ONLY
* format, sending false (or calling indexify()) will change
* it to the normal indexed format.
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public void indexify(boolean useCoordIndexOnly)
{
checkForBadData();
if (useCoordIndexOnly) {
// Return if already in this format
if (coordOnly) return;
// Start from normal indexed format
indexify(false);
// Reformat data to USE_COORD_INDEX_ONLY format
// Need to make an index into the index lists using each
// row of indexes as one value
// First, find out how many index lists there are;
int numLists = 1; // Always have coordinates
if (colorIndices != null) numLists++;
if (normalIndices != null) numLists++;
numLists += texCoordSetCount;
// Make single array containing all indices
int n = coordinateIndices.length;
IndexRow[] ir = new IndexRow[n];
int j;
for (int i = 0 ; i < n ; i++) {
ir[i] = new IndexRow(numLists);
j = 0;
ir[i].set(j++, coordinateIndices[i]);
if (colorIndices != null) ir[i].set(j++, colorIndices[i]);
if (normalIndices != null) ir[i].set(j++, normalIndices[i]);
for (int k = 0 ; k < texCoordSetCount ; k++) {
ir[i].set(j++, texCoordIndexSets[k][i]);
}
}
// Get index into that array
int[] coordOnlyIndices = getListIndices(ir);
// Get rid of duplicate rows
int newInd[] = new int[coordOnlyIndices.length];
ir = (IndexRow[])compactData(coordOnlyIndices, ir, newInd);
coordOnlyIndices = newInd;
// Reformat data lists to correspond to new index
// Allocate arrays to hold reformatted data
Point3f[] newCoords = new Point3f[ir.length];
Color3f[] newColors3 = null;
Color4f[] newColors4 = null;
Vector3f[] newNormals = null;
Object newTexCoordSets[][] = null;
if (colors3 != null) newColors3 = new Color3f[ir.length];
else if (colors4 != null) newColors4 = new Color4f[ir.length];
if (normals != null) newNormals = new Vector3f[ir.length];
for (int i = 0 ; i < texCoordSetCount ; i++) {
if (texCoordDim == 2) {
if (i == 0) newTexCoordSets = new TexCoord2f[texCoordSetCount][];
newTexCoordSets[i] = new TexCoord2f[ir.length];
} else if (texCoordDim == 3) {
if (i == 0) newTexCoordSets = new TexCoord3f[texCoordSetCount][];
newTexCoordSets[i] = new TexCoord3f[ir.length];
} else if (texCoordDim == 4) {
if (i == 0) newTexCoordSets = new TexCoord4f[texCoordSetCount][];
newTexCoordSets[i] = new TexCoord4f[ir.length];
}
}
// Copy data into new arrays
n = ir.length;
for (int i = 0 ; i < n ; i++) {
j = 0;
newCoords[i] = coordinates[(ir[i]).get(j++)];
if (colors3 != null) {
newColors3[i] = colors3[(ir[i]).get(j++)];
} else if (colors4 != null) {
newColors4[i] = colors4[(ir[i]).get(j++)];
}
if (normals != null) newNormals[i] = normals[(ir[i]).get(j++)];
for (int k = 0 ; k < texCoordSetCount ; k++) {
newTexCoordSets[k][i] = texCoordSets[k][(ir[i]).get(j++)];
}
}
// Replace old arrays with new arrays
coordinates = newCoords;
colors3 = newColors3;
colors4 = newColors4;
normals = newNormals;
texCoordSets = newTexCoordSets;
coordinateIndices = coordOnlyIndices;
colorIndices = null;
normalIndices = null;
texCoordIndexSets = new int[texCoordSetCount][];
coordOnly = true;
} else if (coordOnly) {
// Need to change from useCoordIndexOnly format to normal
// indexed format. Should make a more efficient implementation
// later.
int n = coordinateIndices.length;
if ((colors3 != null) || (colors4 != null)) {
colorIndices = new int[n];
for (int i = 0 ; i < n ; i++) colorIndices[i] = coordinateIndices[i];
}
if (normals != null) {
normalIndices = new int[n];
for (int i = 0 ; i < n ; i++) normalIndices[i] = coordinateIndices[i];
}
texCoordIndexSets = new int[texCoordSetCount][];
for (int i = 0 ; i < texCoordSetCount ; i++) {
texCoordIndexSets[i] = new int[n];
for (int j = 0 ; j < n ; j++) {
texCoordIndexSets[i][j] = coordinateIndices[j];
}
}
coordOnly = false;
} else {
// No need to indexify if already indexed
if (coordinateIndices != null) return;
coordinateIndices = getListIndices(coordinates);
if (colors3 != null) colorIndices = getListIndices(colors3);
else if (colors4 != null) colorIndices = getListIndices(colors4);
if (normals != null) normalIndices = getListIndices(normals);
texCoordIndexSets = new int[texCoordSetCount][];
for(int i = 0 ; i < texCoordSetCount ; i++) {
texCoordIndexSets[i] = getListIndices(texCoordSets[i]);
}
coordOnly = false;
}
if ((DEBUG & 1) == 1) {
System.out.println("Coordinate Array:");
for (int i = 0 ; i < coordinates.length ; i++) {
System.out.println(" " + i + " " + coordinates[i] +
" " + coordinates[i].hashCode());
}
System.out.println("Index array:");
for (int i = 0 ; i < coordinateIndices.length ; i++) {
System.out.println(" " + i + " " + coordinateIndices[i]);
}
}
} // End of indexify
public void indexify()
{
indexify(false);
} // End of indexify()
/**
* Allocates an array of the same type as the input type. This allows us to
* use a generic compactData method.
*
* @param data Array of coordinate, color, normal or texture coordinate data
* The data can be in one of the following formats - Point3f, Color3f,
* Color4f, TexCoord2f, TexCoord3f, TexCoord4f.
*
* @param num The size of the array to be allocated
*
* @return An array of size num of the same type as the input type
*
* @exception IllegalArgumentException if the input array is not one of the
* types listed above.
*/
Object[] allocateArray(Object data[], int num) {
Object newData[] = null;
if (data instanceof javax.vecmath.Point3f[]) {
newData = new Point3f[num];
} else if (data instanceof javax.vecmath.Vector3f[]) {
newData = new Vector3f[num];
} else if (data instanceof javax.vecmath.Color3f[]) {
newData = new Color3f[num];
} else if (data instanceof javax.vecmath.Color4f[]) {
newData = new Color4f[num];
} else if (data instanceof javax.vecmath.TexCoord2f[]) {
newData = new TexCoord2f[num];
} else if (data instanceof javax.vecmath.TexCoord3f[]) {
newData = new TexCoord3f[num];
} else if (data instanceof javax.vecmath.TexCoord4f[]) {
newData = new TexCoord4f[num];
} else if (data instanceof IndexRow[]) {
// Hack so we can use compactData for coordIndexOnly
newData = new IndexRow[num];
} else throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo9"));
return newData;
} // End of allocateArray
/**
* Generic method that compacts (ie removes unreferenced/duplicate data)
* any type of indexed data.
* Used to compact coordinate, color, normal and texture coordinate data.
* @param indices Array of indices
* @param data Array of coordinate, color, normal or texture coordinate data
* The data can be in one of the following formats - Point3f, Color3f,
* Color4f, TexCoord2f, TexCoord3f, TexCoord4f.
* @param newInd The new array of indexes after the data has been compacted.
* This must be allocated by the calling method. On return, this array will
* contain the new index data. The size of this array must be equal to
* indices.length
* @return Array of the data with unreferenced and duplicate entries removed.
* The return type will be the same as the type that was passed in data.
*/
// TODO: Remove duplicate entries in data lists.
private Object[] compactData(int indices[], Object data[], int newInd[]) {
Object newData[] = null;
/*
* This is a three step process.
* First, find out how many unique indexes are used. This
* will be the size of the new data array.
*/
int numUnique = 0;
int translationTable[] = new int[data.length];
for (int i = 0 ; i < indices.length ; i++) {
if (translationTable[indices[i]] == 0) {
numUnique++;
translationTable[indices[i]] = 1;
}
}
/*
* Second, build the new data list. Remember the new indexes so
* we can use the table to translate the old indexes to the new
*/
newData = allocateArray(data, numUnique);
int newIdx = 0;
for (int i = 0 ; i < translationTable.length ; i++) {
if (translationTable[i] != 0) {
newData[newIdx] = data[i];
translationTable[i] = newIdx++;
}
}
/*
* Third, make the new index list
*/
for (int i = 0 ; i < indices.length ; i++) {
newInd[i] = translationTable[indices[i]];
}
return newData;
} // End of compactData
/**
* Remove unused data from an indexed dataset.
* Indexed data may contain data entries that are never referenced by
* the dataset. This routine will remove those entries where
* appropriate and renumber the indices to match the new values.
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public void compact()
{
checkForBadData();
// Only usable on indexed data
if (coordinateIndices == null) return;
// USE_COORD_INDEX_ONLY never has unused data
if (coordOnly) return;
int newInd[] = new int[coordinateIndices.length];
coordinates =
(Point3f[])compactData(coordinateIndices, coordinates, newInd);
coordinateIndices = newInd;
if (colorIndices != null) {
newInd = new int[colorIndices.length];
if (colors3 != null)
colors3 = (Color3f[])compactData(colorIndices, colors3, newInd);
else if (colors4 != null)
colors4 = (Color4f[])compactData(colorIndices, colors4, newInd);
colorIndices = newInd;
}
if (normalIndices != null) {
newInd = new int[normalIndices.length];
normals = (Vector3f[])compactData(normalIndices, normals, newInd);
normalIndices = newInd;
}
for (int i = 0 ; i < texCoordSetCount ; i++) {
newInd = new int[texCoordIndexSets[i].length];
texCoordSets[i] = compactData(texCoordIndexSets[i],
texCoordSets[i], newInd);
texCoordIndexSets[i] = newInd;
}
} // End of compact
/**
* Check the data to make sure everything's consistent.
*/
private void checkForBadData() {
boolean badData = false;
//
// Coordinates are required
//
if (coordinates == null) {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo3"));
}
//
// Check for indices with no data
//
if ((colors3 == null) && (colors4 == null) && (colorIndices != null))
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo4"));
if ((normals == null) && (normalIndices != null))
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo11"));
//
// Make sure all TextureCoordinate data is set (indices or not)
//
for (int i = 0 ; i < texCoordSetCount ; i++) {
if (texCoordSets[i] == null)
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo10"));
}
//
// Check for Missing Index lists
//
boolean texInds = false; // Indicates whether we have texcoord indices
if (texCoordIndexSets != null) {
for (int i = 0 ; i < texCoordSetCount ; i++) {
if (texCoordIndexSets[i] != null) texInds = true;
}
}
if ((coordinateIndices != null) ||
(colorIndices != null) ||
(normalIndices != null) ||
texInds) {
// At least one index list is present, so they all must be
// present (unless coordOnly)
if (coordinateIndices == null) badData = true;
else if (coordOnly) {
if ((colorIndices != null) ||
(normalIndices != null) ||
(texInds == true)) {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo20"));
}
} else if (((colors3 != null) || (colors4 != null)) &&
(colorIndices == null)) badData = true;
else if ((normals != null) && (normalIndices == null)) badData = true;
else if ((texCoordSetCount > 0) && !texInds) badData = true;
if (badData) throw new
IllegalArgumentException(J3dUtilsI18N.getString("GeometryInfo19"));
}
//
// Make sure index lists are all the same length
//
if ((coordinateIndices != null) && (!coordOnly)) {
if (((colors3 != null) || (colors4 != null)) &&
(colorIndices.length != coordinateIndices.length))
badData = true;
else if ((normals != null) &&
(normalIndices.length != coordinateIndices.length))
badData = true;
else {
//Check all texCoord indices have the same length
for (int i = 0 ; i < texCoordSetCount ; i++) {
if (texCoordIndexSets[i].length != coordinateIndices.length) {
badData = true;
break;
}
}
}
if (badData) {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo5"));
}
}
//
// For stripped primitives, make sure we have strip counts
//
if ((prim == TRIANGLE_STRIP_ARRAY) ||
(prim == TRIANGLE_FAN_ARRAY) ||
(prim == POLYGON_ARRAY)) {
if (stripCounts == null) badData = true;
} else if (stripCounts != null) badData = true;
if (badData) {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo6"));
}
// Find out how much data we have
int count;
if (coordinateIndices == null) count = coordinates.length;
else count = coordinateIndices.length;
//
// Make sure sum of strip counts equals indexCount (or vertexCount)
// and check to make sure triangles and quads have the right number
// of vertices
//
if ((prim == TRIANGLE_STRIP_ARRAY) ||
(prim == TRIANGLE_FAN_ARRAY) ||
(prim == POLYGON_ARRAY)) {
int sum = 0;
for (int i = 0 ; i < stripCounts.length ; i++) {
sum += stripCounts[i];
}
if (sum != count) {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo7"));
}
} else if (prim == TRIANGLE_ARRAY) {
if (count % 3 != 0) {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo12"));
}
} else if (prim == QUAD_ARRAY) {
if (count % 4 != 0) {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo13"));
}
}
//
// For polygons, make sure the contours add up.
//
if (prim == POLYGON_ARRAY) {
if (contourCounts != null) {
int c = 0;
for (int i = 0 ; i < contourCounts.length ; i++)
c += contourCounts[i];
if (c != stripCounts.length) {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo8"));
}
}
} else {
if (contourCounts != null) {
throw new IllegalArgumentException(
J3dUtilsI18N.getString("GeometryInfo14"));
}
}
} // End of checkForBadData
/**
* Get rid of index lists by reorganizing data into an un-indexed
* format. Does nothing if no index lists are set.
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public void unindexify() {
checkForBadData();
if (coordinateIndices != null) {
// Switch from USE_COORD_INDEX_ONLY format
if (coordOnly) indexify(false);
coordinates =
(Point3f[])unindexifyData(coordinates, coordinateIndices);
coordinateIndices = null;
if (colors3 != null) {
colors3 = (Color3f[])unindexifyData(colors3, colorIndices);
} else if (colors4 != null) {
colors4 = (Color4f[])unindexifyData(colors4, colorIndices);
}
colorIndices = null;
if (normals != null) {
normals = (Vector3f[])unindexifyData(normals, normalIndices);
normalIndices = null;
}
for (int i = 0 ; i < texCoordSetCount ; i++)
texCoordSets[i] = unindexifyData(texCoordSets[i],
texCoordIndexSets[i]);
texCoordIndexSets = new int[texCoordSetCount][];
}
} // End of unindexify
/**
* Generic unindexify method. Can unindex data in any of the following
* formats Point3f, Color3f, Color4f, Vector3f, TexCoord2f, TexCoord3f,
* TexCoord4f.
*/
private Object[] unindexifyData(Object data[], int index[])
{
Object newData[] = allocateArray(data, index.length);
for (int i = 0 ; i < index.length ; i++) {
newData[i] = data[index[i]];
}
return newData;
} // End of unindexifyData
/**
* Calculate vertexFormat based on data.
*/
private int getVertexFormat()
{
int vertexFormat = GeometryArray.COORDINATES;
if (colors3 != null) vertexFormat |= GeometryArray.COLOR_3;
else if (colors4 != null) vertexFormat |= GeometryArray.COLOR_4;
if (normals != null) vertexFormat |= GeometryArray.NORMALS;
if (texCoordDim == 2)
vertexFormat |= GeometryArray.TEXTURE_COORDINATE_2;
else if (texCoordDim == 3)
vertexFormat |= GeometryArray.TEXTURE_COORDINATE_3;
else if (texCoordDim == 4)
vertexFormat |= GeometryArray.TEXTURE_COORDINATE_4;
return vertexFormat;
} // End of getVertexFormat
/**
* Calculate vertexCount based on data
*/
private int getVertexCount()
{
int vertexCount = coordinates.length;
if (colors3 != null) {
if (colors3.length > vertexCount) vertexCount = colors3.length;
} else if (colors4 != null) {
if (colors4.length > vertexCount) vertexCount = colors4.length;
}
if (normals != null) {
if (normals.length > vertexCount) vertexCount = normals.length;
}
// Find max length tex coord set
for (int i = 0 ; i < texCoordSetCount ; i++) {
if (texCoordSets[i].length > vertexCount)
vertexCount = texCoordSets[i].length;
}
return vertexCount;
} // End of getVertexCount
/**
* Converts an array of Tuple2f, Tuple3f, or Tuple4f values into
* an array of floats. Assumes array is not null. Returns null
* if array is not Tuple2f, Tuple3f, or Tuple4f. Used by fillIn()
* for BY_REFERENCE not INTERLEAVED geometry.
*/
private float[] vecmathToFloat(Object[] ar)
{
if (ar[0] instanceof Tuple2f) {
float[] p = new float[ar.length * 2];
Tuple2f[] a = (Tuple2f[])ar;
for (int i = 0 ; i < ar.length ; i++) {
p[i * 2] = a[i].x;
p[i * 2 + 1] = a[i].y;
}
return p;
} else if (ar[0] instanceof Tuple3f) {
float[] p = new float[ar.length * 3];
Tuple3f[] a = (Tuple3f[])ar;
for (int i = 0 ; i < ar.length ; i++) {
p[i * 3] = a[i].x;
p[i * 3 + 1] = a[i].y;
p[i * 3 + 2] = a[i].z;
}
return p;
} else if (ar[0] instanceof Tuple4f) {
float[] p = new float[ar.length * 4];
Tuple4f[] a = (Tuple4f[])ar;
for (int i = 0 ; i < ar.length ; i++) {
p[i * 4] = a[i].x;
p[i * 4 + 1] = a[i].y;
p[i * 4 + 2] = a[i].z;
p[i * 4 + 3] = a[i].w;
}
return p;
}
return null;
} // End of vecmathToFloat
/**
* Fill in the GeometryArray object. Used by getGeometryArray and
* getIndexedGeometryArray. checkForBadData has already been called.
*/
private void fillIn(GeometryArray ga, boolean byRef, boolean interleaved,
boolean nio)
{
if (interleaved) {
// Calculate number of words per vertex
int wpv = 3; // Always have coordinate data
if (normals != null) wpv += 3;
if (colors3 != null) wpv += 3;
else if (colors4 != null) wpv += 4;
wpv += (texCoordSetCount * texCoordDim);
// Build array of interleaved data
float[] d = new float[wpv * coordinates.length];
// Fill in the array
int offset = 0;
for (int i = 0 ; i < coordinates.length ; i++) {
if (texCoordDim == 2) {
for (int j = 0 ; j < texCoordSetCount ; j++) {
d[offset++] = ((TexCoord2f)texCoordSets[j][i]).x;
d[offset++] = ((TexCoord2f)texCoordSets[j][i]).y;
}
} else if (texCoordDim == 3) {
for (int j = 0 ; j < texCoordSetCount ; j++) {
d[offset++] = ((TexCoord3f)texCoordSets[j][i]).x;
d[offset++] = ((TexCoord3f)texCoordSets[j][i]).y;
d[offset++] = ((TexCoord3f)texCoordSets[j][i]).z;
}
} else if (texCoordDim == 4) {
for (int j = 0 ; j < texCoordSetCount ; j++) {
d[offset++] = ((TexCoord4f)texCoordSets[j][i]).x;
d[offset++] = ((TexCoord4f)texCoordSets[j][i]).y;
d[offset++] = ((TexCoord4f)texCoordSets[j][i]).z;
d[offset++] = ((TexCoord4f)texCoordSets[j][i]).w;
}
}
if (colors3 != null) {
d[offset++] = colors3[i].x;
d[offset++] = colors3[i].y;
d[offset++] = colors3[i].z;
} else if (colors4 != null) {
d[offset++] = colors4[i].x;
d[offset++] = colors4[i].y;
d[offset++] = colors4[i].z;
d[offset++] = colors4[i].w;
}
if (normals != null) {
d[offset++] = normals[i].x;
d[offset++] = normals[i].y;
d[offset++] = normals[i].z;
}
d[offset++] = coordinates[i].x;
d[offset++] = coordinates[i].y;
d[offset++] = coordinates[i].z;
}
// Register reference to array of interleaved data
if (nio) {
ByteBufferWrapper b = ByteBufferWrapper.allocateDirect(d.length * 4);
FloatBufferWrapper f =
b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
f.put(d);
ga.setInterleavedVertexBuffer(f.getJ3DBuffer());
} else ga.setInterleavedVertices(d);
} else if (nio) {
ByteBufferWrapper b =
ByteBufferWrapper.allocateDirect(coordinates.length * 4 * 3);
FloatBufferWrapper f =
b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
f.put(vecmathToFloat(coordinates));
ga.setCoordRefBuffer(f.getJ3DBuffer());
if (colors3 != null) {
b = ByteBufferWrapper.allocateDirect(colors3.length * 4 * 3);
f = b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
f.put(vecmathToFloat(colors3));
ga.setColorRefBuffer(f.getJ3DBuffer());
} else if (colors4 != null) {
b = ByteBufferWrapper.allocateDirect(colors4.length * 4 * 4);
f = b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
f.put(vecmathToFloat(colors4));
ga.setColorRefBuffer(f.getJ3DBuffer());
}
if (normals != null) {
b = ByteBufferWrapper.allocateDirect(normals.length * 4 * 3);
f = b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
f.put(vecmathToFloat(normals));
ga.setNormalRefBuffer(f.getJ3DBuffer());
}
for (int i = 0 ; i < texCoordSetCount ; i++) {
b = ByteBufferWrapper.allocateDirect(
texCoordSets[i].length * 4 * texCoordDim);
f = b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
f.put(vecmathToFloat(texCoordSets[i]));
ga.setTexCoordRefBuffer(i, f.getJ3DBuffer());
}
} else if (byRef) {
// Need to copy the data into float arrays - GeometryArray
// prefers them over the vecmath types
ga.setCoordRefFloat(vecmathToFloat(coordinates));
if (colors3 != null) ga.setColorRefFloat(vecmathToFloat(colors3));
else if (colors4 != null) ga.setColorRefFloat(vecmathToFloat(colors4));
if (normals != null) ga.setNormalRefFloat(vecmathToFloat(normals));
for (int i = 0 ; i < texCoordSetCount ; i++) {
ga.setTexCoordRefFloat(i, vecmathToFloat(texCoordSets[i]));
}
} else {
ga.setCoordinates(0, coordinates);
if (colors3 != null) ga.setColors(0, colors3);
else if (colors4 != null) ga.setColors(0, colors4);
if (normals != null) ga.setNormals(0, normals);
for (int i = 0 ; i < texCoordSetCount ; i++) {
if (texCoordDim == 2) {
ga.setTextureCoordinates(i, 0, (TexCoord2f[])texCoordSets[i]);
} else if (texCoordDim == 3) {
ga.setTextureCoordinates(i, 0, (TexCoord3f[])texCoordSets[i]);
} else if (texCoordDim == 4) {
ga.setTextureCoordinates(i, 0, (TexCoord4f[])texCoordSets[i]);
}
}
}
if (coordinateIndices != null) {
IndexedGeometryArray iga = null;
iga = (IndexedGeometryArray)ga;
iga.setCoordinateIndices(0, coordinateIndices);
if (!coordOnly) {
if (colorIndices != null) iga.setColorIndices(0, colorIndices);
if (normalIndices != null) iga.setNormalIndices(0, normalIndices);
for (int i = 0 ; i < texCoordSetCount ; i++)
iga.setTextureCoordinateIndices(i, 0, texCoordIndexSets[i]);
}
}
} // End of fillIn
/**
* Redo indexes to guarantee connection information.
* Use this routine if your original data is in indexed format, but
* you don't trust that the indexing is correct. After this
* routine it is guaranteed that two points with the same
* position will have the same coordinate index (for example).
* Try this if you see
* glitches in your normals or stripification, to rule out
* bad indexing as the source of the problem. Works with normal
* indexed format or USE_COORD_INDEX_ONLY format.
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public void recomputeIndices()
{
boolean remember = coordOnly;
// Can make more efficient implementation later
unindexify();
indexify(remember);
} // End of recomputeIndices
/**
* Reverse the order of an array of ints (computer class homework
* problem).
*/
private void reverseList(int list[])
{
int t;
if (list == null) return;
for (int i = 0 ; i < list.length / 2 ; i++) {
t = list[i];
list[i] = list[list.length - i - 1];
list[list.length - i - 1] = t;
}
} // End of reverseList
/**
* Reverse the order of all lists. If your polygons are formatted with
* clockwise winding, you will always see the back and never the front.
* (Java 3D always wants vertices specified with a counter-clockwise
* winding.)
* This method will (in effect) reverse the winding of your data by
* inverting all of the index lists and the stripCounts
* and contourCounts lists.
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public void reverse()
{
indexify();
reverseList(stripCounts);
reverseList(oldStripCounts);
reverseList(contourCounts);
reverseList(coordinateIndices);
reverseList(colorIndices);
reverseList(normalIndices);
for (int i = 0 ; i < texCoordSetCount ; i++)
reverseList(texCoordIndexSets[i]);
} // End of reverse
/**
* Returns true if the data in this GeometryInfo is currently
* formatted in the USE_COORD_INDEX_ONLY format where a single
* index list is used to index into all data lists.
* @see GeometryInfo#indexify(boolean)
* @see GeometryInfo#getIndexedGeometryArray(boolean, boolean, boolean,
* boolean, boolean)
*/
public boolean getUseCoordIndexOnly()
{
return coordOnly;
} // End of getUseCoordIndexOnly
/**
* Tells the GeometryInfo that its data is formatted in the
* USE_COORD_INDEX_ONLY format with a single index list
* (the coordinate index list) that indexes into all data
* lists (coordinates, normals, colors, and texture
* coordinates). NOTE: this will not convert the data
* for you. This method is for when you are sending in
* data useng the setCoordinates, setNormals, setColors,
* and/or setTextureCoordinates methods, and you are only
* setting one index using setCoordinateIndices(). If
* you want GeometryInfo to convert your data to the
* USE_COORD_INDEX_ONLY format, use indexify(true) or
* getIndexedGeometryArray with the useCoordIndexOnly
* parameter set to true.
* @see GeometryInfo#indexify(boolean)
* @see GeometryInfo#getIndexedGeometryArray(boolean, boolean, boolean,
* boolean, boolean)
*/
public void setUseCoordIndexOnly(boolean useCoordIndexOnly)
{
coordOnly = useCoordIndexOnly;
} // End of setUseCoordIndexOnly
/**
* Creates and returns a non-indexed Java 3D GeometryArray object
* based on the data in the GeometryInfo object. This object is
* suitable to be attached to a Shape3D node for rendering.
* @param byRef Use geometry BY_REFERENCE
* @param interleaved Use INTERLEAVED geometry. Implies byRef is
* true as well.
* @param nio Create GeometryArray using java.nio.Buffer for
* geometry arrays. Only usable on JDK 1.4 or higher. Implies
* byRef is true as well.
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public GeometryArray getGeometryArray(boolean byRef, boolean interleaved,
boolean nio)
{
checkForBadData();
if (prim == POLYGON_ARRAY) {
if (tr == null) tr = new Triangulator();
tr.triangulate(this);
} else changeBackToOldPrim();
unindexify();
int vertexFormat = getVertexFormat();
if (nio) vertexFormat |= (GeometryArray.BY_REFERENCE |
GeometryArray.USE_NIO_BUFFER);
if (interleaved) vertexFormat |= (GeometryArray.BY_REFERENCE |
GeometryArray.INTERLEAVED);
if (byRef) vertexFormat |= GeometryArray.BY_REFERENCE;
int vertexCount = coordinates.length;
// If the texCoordSetMap hasn't been set, assume one set of
// texture coordinates only and one texture state unit
if ((texCoordSetCount > 0) && (texCoordSetMap == null)) {
texCoordSetCount = 1;
texCoordSetMap = new int[1];
texCoordSetMap[0] = 0;
}
// Create the GeometryArray object
GeometryArray ga = null;
switch (prim) {
case TRIANGLE_ARRAY:
TriangleArray ta = new TriangleArray(vertexCount, vertexFormat,
texCoordSetCount, texCoordSetMap);
ga = (GeometryArray)ta;
break;
case QUAD_ARRAY:
QuadArray qa = new QuadArray(vertexCount, vertexFormat,
texCoordSetCount, texCoordSetMap);
ga = (GeometryArray)qa;
break;
case TRIANGLE_STRIP_ARRAY:
TriangleStripArray tsa = new TriangleStripArray(vertexCount,
vertexFormat, texCoordSetCount, texCoordSetMap,
stripCounts);
ga = (GeometryArray)tsa;
break;
case TRIANGLE_FAN_ARRAY:
TriangleFanArray tfa = new TriangleFanArray(vertexCount,
vertexFormat, texCoordSetCount, texCoordSetMap,
stripCounts);
ga = (GeometryArray)tfa;
break;
}
fillIn(ga, byRef, interleaved, nio);
return ga;
} // End of getGeometryArray(int, int)
/**
* Creates and returns a non-indexed Java 3D GeometryArray object
* based on the data in the GeometryInfo object. This object is
* suitable to be attached to a Shape3D node for rendering.
* The geometry is not created using data BY_REFERENCE,
* INTERLEAVED, or USE_NIO_BUFFER.
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public GeometryArray getGeometryArray()
{
return getGeometryArray(false, false, false);
} // End of getGeometryArray()
/**
* Creates and returns a IndexedGeometryArray
* based on the data in the GeometryInfo object. This object is
* suitable to be attached to a Shape3D node for rendering.
* @param compact Remove Coordinates, Colors, Normals, and
* TextureCoordinates that aren't referenced by any indices.
* @param byRef Create the IndexedGeometryArray using geometry
* BY_REFERENCE.
* @param interleaved Use INTERLEAVED geometry. Implies byRef is
* true as well.
* @param nio Create GeometryArray using java.nio.Buffer for
* geometry arrays. Only usable on JDK 1.4 or higher. Implies
* byRef is true as well.
* @param useCoordIndexOnly Create the IndexedGeometryArray using
* USE_COORD_INDEX_ONLY. Values from the coordinate index array
* are used as a single set of indices into all vertex
* component arrays (coord, color, normal, and texCoord).
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public IndexedGeometryArray getIndexedGeometryArray(boolean compact,
boolean byRef,
boolean interleaved,
boolean useCoordIndexOnly,
boolean nio)
{
indexify(useCoordIndexOnly);
if (compact) compact();
if (prim == POLYGON_ARRAY) {
if (tr == null) tr = new Triangulator();
tr.triangulate(this);
} else changeBackToOldPrim();
if (useCoordIndexOnly && coordOnly == false) {
// Check to see if we can optimize for USE_COORD_INDEX_ONLY
int i, j;
boolean canUseCoordIndexOnly = true;
if (coordinateIndices != null) {
// See if all the array lengths are the same
if (colorIndices != null &&
colorIndices.length != coordinateIndices.length) {
canUseCoordIndexOnly = false;
}
if (normalIndices != null &&
normalIndices.length != coordinateIndices.length) {
canUseCoordIndexOnly = false;
}
for (i = 0 ; i < texCoordSetCount ; i++) {
if (texCoordIndexSets[i] != null &&
texCoordIndexSets[i].length != coordinateIndices.length) {
canUseCoordIndexOnly = false;
break;
}
}
if (canUseCoordIndexOnly &&
((colorIndices != null) ||
(normalIndices != null) ||
(texCoordSetCount > 0))) {
// All array lengths are the same. Check their contents
for (i=0; igetIndexedGeometryArray(compact, false,
* false, false, false).
* @param compact Remove Coordinates, Colors, Normals, and
* TextureCoordinates that aren't referenced by any indices.
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public IndexedGeometryArray getIndexedGeometryArray(boolean compact)
{
return getIndexedGeometryArray(compact, false, false, false, false);
} // End of getIndexedGeometryArray(boolean)
/**
* Creates and returns an IndexedGeometryArray
* based on the data in the GeometryInfo object. This object is
* suitable to be attached to a Shape3D node for rendering.
* Equivalent to getIndexedGeometryArray(false, false,
* false, false, false).
* @throws IllegalArgumentException if coordinate data is missing,
* if the index lists aren't all the
* same length, if an index list is set and the corresponding data
* list isn't set, if a data list is set and the corresponding
* index list is unset (unless all index lists are unset or in
* USE_COORD_INDEX_ONLY format),
* if StripCounts or ContourCounts is inconsistent with the current
* primitive, if the sum of the contourCounts array doesn't equal
* the length of the StripCounts array, or if the number of vertices
* isn't a multiple of three (for triangles) or four (for quads).
*/
public IndexedGeometryArray getIndexedGeometryArray()
{
return getIndexedGeometryArray(false, false, false, false, false);
} // End of getIndexedGeometryArray()
} // End of class GeometryInfo
// End of file GeometryInfo.java
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/ListNode.java 0000644 0000000 0000000 00000006034 10563126523 030026 0 ustar root root /*
* $RCSfile: ListNode.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:19 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
class ListNode {
int index;
int prev;
int next;
int convex;
int vcntIndex; // Vertex, Color, Normal, Texture Index
ListNode(int ind) {
index = ind;
prev = -1;
next = -1;
convex = 0;
vcntIndex = -1;
}
void setCommonIndex(int comIndex) {
vcntIndex = comIndex;
}
int getCommonIndex() {
return vcntIndex;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Basic.java 0000644 0000000 0000000 00000012166 10563126521 027327 0 ustar root root /*
* $RCSfile: Basic.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:17 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import java.io.*;
import java.util.*;
import javax.vecmath.*;
class Basic {
static final double D_RND_MAX = 2147483647.0;
static double detExp(double u_x, double u_y, double u_z,
double v_x, double v_y, double v_z,
double w_x, double w_y, double w_z) {
return ((u_x) * ((v_y) * (w_z) - (v_z) * (w_y)) -
(u_y) * ((v_x) * (w_z) - (v_z) * (w_x)) +
(u_z) * ((v_x) * (w_y) - (v_y) * (w_x)));
}
static double det3D(Tuple3f u, Tuple3f v, Tuple3f w) {
return ((u).x * ((v).y * (w).z - (v).z * (w).y) -
(u).y * ((v).x * (w).z - (v).z * (w).x) +
(u).z * ((v).x * (w).y - (v).y * (w).x));
}
static double det2D(Tuple2f u, Tuple2f v, Tuple2f w) {
return (((u).x - (v).x) * ((v).y - (w).y) + ((v).y - (u).y) * ((v).x - (w).x));
}
static double length2(Tuple3f u) {
return (((u).x * (u).x) + ((u).y * (u).y) + ((u).z * (u).z));
}
static double lengthL1(Tuple3f u) {
return (Math.abs((u).x) + Math.abs((u).y) + Math.abs((u).z));
}
static double lengthL2(Tuple3f u) {
return Math.sqrt(((u).x * (u).x) + ((u).y * (u).y) + ((u).z * (u).z));
}
static double dotProduct(Tuple3f u, Tuple3f v) {
return (((u).x * (v).x) + ((u).y * (v).y) + ((u).z * (v).z));
}
static double dotProduct2D(Tuple2f u, Tuple2f v) {
return (((u).x * (v).x) + ((u).y * (v).y));
}
static void vectorProduct(Tuple3f p, Tuple3f q, Tuple3f r) {
(r).x = (p).y * (q).z - (q).y * (p).z;
(r).y = (q).x * (p).z - (p).x * (q).z;
(r).z = (p).x * (q).y - (q).x * (p).y;
}
static void vectorAdd( Tuple3f p, Tuple3f q, Tuple3f r) {
(r).x = (p).x + (q).x;
(r).y = (p).y + (q).y;
(r).z = (p).z + (q).z;
}
static void vectorSub( Tuple3f p, Tuple3f q, Tuple3f r) {
(r).x = (p).x - (q).x;
(r).y = (p).y - (q).y;
(r).z = (p).z - (q).z;
}
static void vectorAdd2D( Tuple2f p, Tuple2f q, Tuple2f r) {
(r).x = (p).x + (q).x;
(r).y = (p).y + (q).y;
}
static void vectorSub2D( Tuple2f p, Tuple2f q, Tuple2f r) {
(r).x = (p).x - (q).x;
(r).y = (p).y - (q).y;
}
static void invertVector(Tuple3f p) {
(p).x = -(p).x;
(p).y = -(p).y;
(p).z = -(p).z;
}
static void divScalar(double scalar, Tuple3f u) {
(u).x /= scalar;
(u).y /= scalar;
(u).z /= scalar;
}
static void multScalar2D(double scalar, Tuple2f u) {
(u).x *= scalar;
(u).y *= scalar;
}
static int signEps(double x, double eps) {
return ((x <= eps) ? ((x < -eps) ? -1 : 0) : 1);
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Sphere.java 0000644 0000000 0000000 00000037451 10613450623 027537 0 ustar root root /*
* $RCSfile: Sphere.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.6 $
* $Date: 2007/04/24 18:50:59 $
* $State: Exp $
*/
package com.sun.j3d.utils.geometry;
import com.sun.j3d.utils.geometry.*;
import java.io.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.math.*;
/**
* Sphere is a geometry primitive created with a given radius and resolution.
* It is centered at the origin.
* * When a texture is applied to a Sphere, it is mapped CCW from the back * of the sphere. *
* By default all primitives with the same parameters share their
* geometry (e.g., you can have 50 shperes in your scene, but the
* geometry is stored only once). A change to one primitive will
* effect all shared nodes. Another implication of this
* implementation is that the capabilities of the geometry are shared,
* and once one of the shared nodes is live, the capabilities cannot
* be set. Use the GEOMETRY_NOT_SHARED flag if you do not wish to
* share geometry among primitives with the same parameters.
*/
public class Sphere extends Primitive {
/**
* Sphere shape identifier, used by getShape.
*
* @see Sphere#getShape
*/
public static final int BODY = 0;
static final int MID_REZ_DIV = 16;
float radius;
int divisions;
/**
* Constructs a Sphere of a given radius. Normals are generated
* by default, texture coordinates are not. The resolution defaults to
* 15 divisions along sphere's axes. Appearance defaults to white.
* @param radius Radius
*/
public Sphere (float radius) {
this(radius, GENERATE_NORMALS, MID_REZ_DIV);
}
/**
* Constructs a default Sphere of radius of 1.0. Normals are generated
* by default, texture coordinates are not.
* Resolution defaults to 15 divisions. Appearance defaults to white.
*/
public Sphere() {
this(1.0f, GENERATE_NORMALS, MID_REZ_DIV);
}
/**
* Constructs a Sphere of a given radius and appearance.
* Normals are generated by default, texture coordinates are not.
* @param radius Radius
* @param ap Appearance
*/
public Sphere (float radius, Appearance ap) {
this(radius, GENERATE_NORMALS, MID_REZ_DIV, ap);
}
/**
* Constructs a Sphere of a given radius and appearance with
* additional parameters specified by the Primitive flags.
* @param radius Radius
* @param primflags
* @param ap appearance
*/
public Sphere(float radius, int primflags, Appearance ap) {
this(radius, primflags, MID_REZ_DIV, ap);
}
/**
* Constructs a Sphere of a given radius and number of divisions
* with additional parameters specified by the Primitive flags.
* Appearance defaults to white.
* @param radius Radius
* @param divisions Divisions
* @param primflags Primflags
*/
public Sphere(float radius, int primflags, int divisions) {
this(radius, primflags, divisions, null);
}
/**
* Obtains Sphere's shape node that contains the geometry.
* This allows users to modify the appearance or geometry.
* @param partId The part to return (must be BODY for Spheres)
* @return The Shape3D object associated with the partId. If an
* invalid partId is passed in, null is returned.
*/
public Shape3D getShape(int partId) {
if (partId != BODY) return null;
// return (Shape3D)((Group)getChild(0)).getChild(BODY);
return (Shape3D)getChild(BODY);
}
/** Obtains Sphere's shape node that contains the geometry.
*/
public Shape3D getShape() {
// return (Shape3D)((Group)getChild(0)).getChild(BODY);
return (Shape3D)getChild(BODY);
}
/** Sets appearance of the Sphere.
*/
public void setAppearance(Appearance ap) {
// ((Shape3D)((Group)getChild(0)).getChild(BODY)).setAppearance(ap);
((Shape3D)getChild(BODY)).setAppearance(ap);
}
/**
* Gets the appearance of the specified part of the sphere.
*
* @param partId identifier for a given subpart of the sphere
*
* @return The appearance object associated with the partID. If an
* invalid partId is passed in, null is returned.
*
* @since Java 3D 1.2.1
*/
public Appearance getAppearance(int partId) {
if (partId != BODY) return null;
return getShape(partId).getAppearance();
}
/**
* Constructs a customized Sphere of a given radius,
* number of divisions, and appearance, with additional parameters
* specified by the Primitive flags. The resolution is defined in
* terms of number of subdivisions along the sphere's axes. More
* divisions lead to more finely tesselated objects.
*
* If the appearance is null, the sphere defaults to a white appearance.
*/
public Sphere(float radius, int primflags, int divisions, Appearance ap) {
super();
int sign;
int n, nstep;
this.radius = radius;
this.divisions = divisions;
/*
* The sphere algorithm evaluates spherical angles along regular
* units. For each spherical coordinate, (theta, rho), a (x,y,z) is
* evaluated (along with the normals and texture coordinates).
*
* The spherical angles theta varies from 0 to 2pi and rho from 0
* to pi. Sample points depends on the number of divisions.
*/
flags = primflags;
boolean texCoordYUp = (flags & GENERATE_TEXTURE_COORDS_Y_UP) != 0;
//Depending on whether normal inward bit is set.
if ((flags & GENERATE_NORMALS_INWARD) != 0) {
sign = -1;
} else {
sign = 1;
}
if (divisions < 4) {
nstep = 1;
n = 4;
} else {
int mod = divisions % 4;
if (mod == 0) {
n = divisions;
} else {
n = divisions + (4 - mod);
}
nstep = n/4;
}
GeomBuffer cache = getCachedGeometry(Primitive.SPHERE,
radius, 0.0f, 0.0f,
divisions, 0, primflags);
Shape3D shape;
if (cache != null) {
shape = new Shape3D(cache.getComputedGeometry());
numVerts += cache.getNumVerts();
numTris += cache.getNumTris();
} else {
// buffer size = 8*(1 + 2E{i} + (nstep+1))
// where E{i} = sum of i = 2 ... nstep
GeomBuffer gbuf = new GeomBuffer(8*nstep*(nstep+2));
for (int i=0; i < 4; i++) {
buildQuadrant(gbuf, i*Math.PI/2, (i+1)*Math.PI/2, sign, nstep, n, true);
buildQuadrant(gbuf, i*Math.PI/2, (i+1)*Math.PI/2, sign, nstep, n, false);
}
// Fix to Issue 411. Java 3D prefers images used for texture mapping to be Y-up
if (texCoordYUp) {
TexCoord2f[] texCoords = gbuf.getTexCoords();
if (texCoords != null) {
for (int ii=0; ii
* For any NodeComponent objects
* contained by the object being duplicated, each NodeComponent
* object's
* By default all primitives with the same parameters share their
* geometry (e.g., you can have 50 shperes in your scene, but the
* geometry is stored only once). A change to one primitive will
* effect all shared nodes. Another implication of this
* implementation is that the capabilities of the geometry are shared,
* and once one of the shared nodes is live, the capabilities cannot
* be set. Use the GEOMETRY_NOT_SHARED flag if you do not wish to
* share geometry among primitives with the same parameters.
*/
public class Box extends Primitive {
/**
* Used to designate the front side of the box when using
* getShape().
*
* @see Box#getShape
*/
public static final int FRONT = 0;
/**
* Used to designate the back side of the box when using
* getShape().
*
* @see Box#getShape
*/
public static final int BACK = 1;
/**
* Used to designate the right side of the box when using
* getShape().
*
* @see Box#getShape
*/
public static final int RIGHT = 2;
/**
* Used to designate the left side of the box when using
* getShape().
*
* @see Box#getShape
*/
public static final int LEFT = 3;
/**
* Used to designate the top side of the box when using
* getShape().
*
* @see Box#getShape
*/
public static final int TOP = 4;
/**
* Used to designate the bottom side of the box when using
* getShape().
*
* @see Box#getShape
*/
public static final int BOTTOM = 5;
float xDim, yDim, zDim;
int numTexUnit = 1;
/**
* Constructs a default box of 1.0 in all dimensions.
* Normals are generated by default, texture coordinates are not.
*/
public Box()
{
this(1.0f, 1.0f, 1.0f, GENERATE_NORMALS, null);
}
/**
* Constructs a box of a given dimension and appearance.
* Normals are generated by default, texture coordinates are not.
*
* @param xdim X-dimension size.
* @param ydim Y-dimension size.
* @param zdim Z-dimension size.
* @param ap Appearance
*/
public Box(float xdim, float ydim, float zdim, Appearance ap)
{
this(xdim, ydim, zdim, GENERATE_NORMALS, ap);
}
/**
* Constructs a box of a given dimension, flags, and appearance.
*
* @param xdim X-dimension size.
* @param ydim Y-dimension size.
* @param zdim Z-dimension size.
* @param primflags primitive flags.
* @param ap Appearance
*/
public Box(float xdim, float ydim, float zdim, int primflags,
Appearance ap, int numTexUnit) {
int i;
double sign;
xDim = xdim;
yDim = ydim;
zDim = zdim;
flags = primflags;
numTexUnit = numTexUnit;
boolean texCoordYUp = (flags & GENERATE_TEXTURE_COORDS_Y_UP) != 0;
//Depending on whether normal inward bit is set.
if ((flags & GENERATE_NORMALS_INWARD) != 0)
sign = -1.0;
else sign = 1.0;
// TransformGroup objTrans = new TransformGroup();
// objTrans.setCapability(ALLOW_CHILDREN_READ);
// this.addChild(objTrans);
Shape3D shape[] = new Shape3D[6];
GeomBuffer cache = null;
for (i = FRONT; i <= BOTTOM; i++){
cache = getCachedGeometry(Primitive.BOX, xdim, ydim, zdim, i, i,
primflags);
if (cache != null) {
// System.out.println("using cached geometry i = " + i);
shape[i] = new Shape3D(cache.getComputedGeometry());
numVerts += cache.getNumVerts();
numTris += cache.getNumTris();
}
else {
GeomBuffer gbuf = new GeomBuffer(4, numTexUnit);
gbuf.begin(GeomBuffer.QUAD_STRIP);
for (int j = 0; j < 2; j++){
gbuf.normal3d( (double) normals[i].x*sign,
(double) normals[i].y*sign,
(double) normals[i].z*sign);
if (texCoordYUp) {
gbuf.texCoord2d(tcoords[i*8 + j*2], 1.0 - tcoords[i*8 + j*2 + 1]);
}
else {
gbuf.texCoord2d(tcoords[i*8 + j*2], tcoords[i*8 + j*2 + 1]);
}
gbuf.vertex3d( (double) verts[i*12 + j*3]*xdim,
(double) verts[i*12+ j*3 + 1]*ydim,
(double) verts[i*12+ j*3 + 2]*zdim );
}
for (int j = 3; j > 1; j--){
gbuf.normal3d( (double) normals[i].x*sign,
(double) normals[i].y*sign,
(double) normals[i].z*sign);
if (texCoordYUp) {
gbuf.texCoord2d(tcoords[i*8 + j*2], 1.0 - tcoords[i*8 + j*2 + 1]);
}
else {
gbuf.texCoord2d(tcoords[i*8 + j*2], tcoords[i*8 + j*2 + 1]);
}
gbuf.vertex3d( (double) verts[i*12 + j*3]*xdim,
(double) verts[i*12+ j*3 + 1]*ydim,
(double) verts[i*12+ j*3 + 2]*zdim );
}
gbuf.end();
shape[i] = new Shape3D(gbuf.getGeom(flags));
numVerts = gbuf.getNumVerts();
numTris = gbuf.getNumTris();
if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
cacheGeometry(Primitive.BOX, xdim, ydim, zdim, i, i,
primflags, gbuf);
}
}
if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
(shape[i]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
(shape[i]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
}
if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
(shape[i]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
}
// objTrans.addChild(shape[i]);
this.addChild(shape[i]);
}
if (ap == null){
setAppearance();
}
else setAppearance(ap);
}
public Box(float xdim, float ydim, float zdim, int primflags,
Appearance ap) {
this(xdim, ydim, zdim, primflags, ap, 1);
}
/**
* Gets one of the faces (Shape3D) from the box that contains the
* geometry and appearance. This allows users to modify the
* appearance or geometry of individual parts.
* @param partId The part to return.
* @return The Shape3D object associated with the partID. If an
* invalid partId is passed in, null is returned.
*/
public Shape3D getShape(int partId) {
if ((partId >= FRONT) && (partId <= BOTTOM))
// return (Shape3D)(((Group)getChild(0)).getChild(partId));
return (Shape3D)getChild(partId);
return null;
}
/**
* Sets appearance of the box. This will set each face of the
* box to the same appearance. To set each face's appearance
* separately, use getShape(partId) to get the
* individual shape and call shape.setAppearance(ap).
*/
public void setAppearance(Appearance ap){
// ((Shape3D)((Group)getChild(0)).getChild(TOP)).setAppearance(ap);
// ((Shape3D)((Group)getChild(0)).getChild(LEFT)).setAppearance(ap);
// ((Shape3D)((Group)getChild(0)).getChild(RIGHT)).setAppearance(ap);
// ((Shape3D)((Group)getChild(0)).getChild(FRONT)).setAppearance(ap);
// ((Shape3D)((Group)getChild(0)).getChild(BACK)).setAppearance(ap);
// ((Shape3D)((Group)getChild(0)).getChild(BOTTOM)).setAppearance(ap);
((Shape3D)getChild(TOP)).setAppearance(ap);
((Shape3D)getChild(LEFT)).setAppearance(ap);
((Shape3D)getChild(RIGHT)).setAppearance(ap);
((Shape3D)getChild(FRONT)).setAppearance(ap);
((Shape3D)getChild(BACK)).setAppearance(ap);
((Shape3D)getChild(BOTTOM)).setAppearance(ap);
}
/**
* Gets the appearance of the specified part of the box.
*
* @param partId identifier for a given subpart of the box
*
* @return The appearance object associated with the partID. If an
* invalid partId is passed in, null is returned.
*
* @since Java 3D 1.2.1
*/
public Appearance getAppearance(int partId) {
if (partId > BOTTOM || partId < FRONT) return null;
return getShape(partId).getAppearance();
}
private static final float[] verts = {
// front face
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
// back face
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
// right face
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
// left face
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
// top face
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
// bottom face
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
};
private static final double[] tcoords = {
// front
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
// back
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
//right
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
// left
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
// top
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
// bottom
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0
};
private static final Vector3f[] normals = {
new Vector3f( 0.0f, 0.0f, 1.0f), // front face
new Vector3f( 0.0f, 0.0f, -1.0f), // back face
new Vector3f( 1.0f, 0.0f, 0.0f), // right face
new Vector3f(-1.0f, 0.0f, 0.0f), // left face
new Vector3f( 0.0f, 1.0f, 0.0f), // top face
new Vector3f( 0.0f, -1.0f, 0.0f), // bottom face
};
/**
* Used to create a new instance of the node. This routine is called
* by
* For any NodeComponent objects
* contained by the object being duplicated, each NodeComponent
* object's
* Here is a sample code that use this utility to create some quads.
*
* Currently, you are limited to one primitive type per geom buffer. Future
* versions will add support for mixed primitive types.
*
**/
class GeomBuffer extends Object{
//Supported Primitives
static final int QUAD_STRIP = 0x01;
static final int TRIANGLES = 0x02;
static final int QUADS = 0x04;
static final int TRIANGLE_FAN = 0x10;
static final int TRIANGLE_STRIP = 0x20;
private int flags;
Point3f[] pts = null;
Vector3f[] normals = null;
TexCoord2f[] tcoords = null;
int currVertCnt;
int currPrimCnt;
int[] currPrimType = null,
currPrimStartVertex = null,
currPrimEndVertex = null;
GeometryArray geometry;
int numVerts = 0;
int numTris = 0;
int numTexUnit = 1;
int texCoordSetMap[] = null;
static final int debug = 0;
/** Creates a geometry buffer of given number of vertices
* @param numVerts total number of vertices to allocate by this buffer.
* This is an upper bound estimate.
*/
GeomBuffer(int numVerts, int numTexUnit)
{
this.numTexUnit = numTexUnit;
pts = new Point3f[numVerts];
normals = new Vector3f[numVerts];
tcoords = new TexCoord2f[numVerts];
// max primitives is numV/3
currPrimType = new int[numVerts/3];
currPrimStartVertex = new int[numVerts/3];
currPrimEndVertex = new int[numVerts/3];
currVertCnt = 0;
currPrimCnt = 0;
texCoordSetMap = new int[numTexUnit];
for (int i = 0; i < numTexUnit; i++)
texCoordSetMap[i] = 0;
}
GeomBuffer(int numVerts)
{
this(numVerts, 1);
}
/*
* Returns a Java 3D geometry array from the geometry buffer. You need to
* call begin, vertex3d, end, etc. before calling this, of course.
*
* @param format vertex format.
*/
GeometryArray getGeom(int format)
{
GeometryArray obj = null;
flags = format;
numTris = 0;
//Switch based on first primitive.
switch (currPrimType[0]){
case TRIANGLES:
obj = processTriangles();
break;
case QUADS:
obj = processQuads();
break;
case QUAD_STRIP:
case TRIANGLE_STRIP:
obj = processQuadStrips();
break;
case TRIANGLE_FAN:
obj = processTriangleFan();
break;
}
if ((obj != null) && ((flags & Primitive.ENABLE_GEOMETRY_PICKING) != 0)) {
obj.setCapability(Geometry.ALLOW_INTERSECT);
obj.setCapability(GeometryArray.ALLOW_FORMAT_READ);
obj.setCapability(GeometryArray.ALLOW_COUNT_READ);
obj.setCapability(GeometryArray.ALLOW_COORDINATE_READ);
}
return obj;
}
/**
* Begins a new primitive given the primitive type.
*
* @param prim the primitive type (listed above).
*
**/
void begin(int prim)
{
if (debug >= 1) System.out.println("quad");
currPrimType[currPrimCnt] = prim;
currPrimStartVertex[currPrimCnt] = currVertCnt;
}
/**
* End of primitive.
*
*
**/
void end()
{
if (debug >= 1) System.out.println("end");
currPrimEndVertex[currPrimCnt] = currVertCnt;
currPrimCnt++;
}
void vertex3d(double x, double y, double z)
{
if (debug >= 2) System.out.println("v " + x + " " +
y + " " +
z);
pts[currVertCnt] = new Point3f((float)x, (float)y, (float)z);
currVertCnt++;
}
void normal3d(double x, double y, double z)
{
if (debug >= 2) System.out.println("n " + x + " " +
y + " " + z);
double sum = x*x+y*y+z*z;
if (Math.abs(sum - 1.0) > 0.001){
if (debug >= 2) System.out.println("normalizing");
double root = Math.sqrt(sum);
if (root > 0.000001) {
x /= root;
y /= root;
z /= root;
} else {
y = z = 0.0; x = 1.0;
}
}
normals[currVertCnt] = new Vector3f((float)x, (float)y, (float)z);
}
void texCoord2d(double s, double t)
{
if (debug >= 2) System.out.println("t " +
s + " " +
t);
tcoords[currVertCnt] = new TexCoord2f((float)s, (float)t);
}
// Return a reference to the texture coordinates of this geom buffer.
TexCoord2f[] getTexCoords() {
return tcoords;
}
/**
* Returns the Java 3D geometry gotten from calling getGeom.
*
**/
GeometryArray getComputedGeometry()
{
return geometry;
}
int getNumTris()
{
return numTris;
}
int getNumVerts()
{
return numVerts;
}
private GeometryArray processQuadStrips()
{
GeometryArray obj = null;
int i;
int totalVerts = 0;
// Calculate how many vertices needed to hold all of the individual quads
int stripCounts[] = new int[currPrimCnt];
for (i = 0; i < currPrimCnt; i++){
stripCounts[i] = currPrimEndVertex[i] - currPrimStartVertex[i];
totalVerts += stripCounts[i];
}
if (debug >= 1) System.out.println("totalVerts " + totalVerts);
int tsaFlags = TriangleStripArray.COORDINATES;
if ((flags & Primitive.GENERATE_NORMALS) != 0)
tsaFlags |= TriangleStripArray.NORMALS;
if ((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0)
tsaFlags |= TriangleStripArray.TEXTURE_COORDINATE_2;
// Create GeometryArray to pass back
obj = new TriangleStripArray(totalVerts, tsaFlags,
1, texCoordSetMap, stripCounts);
// Allocate space to store new vertex info
Point3f[] newpts = new Point3f[totalVerts];
Vector3f[] newnormals = new Vector3f[totalVerts];
TexCoord2f[] newtcoords = new TexCoord2f[totalVerts];
int currVert = 0;
// Repeat for each Quad Strip
for (i = 0; i < currPrimCnt; i++){
// Output order for these quad arrays same as java 3d triangle strips
for (int j = currPrimStartVertex[i] ; j < currPrimEndVertex[i] ; j++){
outVertex(newpts, newnormals, newtcoords, currVert++,
pts, normals, tcoords, j);
}
}
numVerts = currVert;
numTris += totalVerts - currPrimCnt * 2;
obj.setCoordinates(0, newpts);
if ((flags & Primitive.GENERATE_NORMALS) != 0)
obj.setNormals(0, newnormals);
if ((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0)
obj.setTextureCoordinates(0, 0, newtcoords);
geometry = obj;
return obj;
}
private GeometryArray processQuads()
{
GeometryArray obj = null;
int i;
int totalVerts = 0;
for (i = 0; i < currPrimCnt; i++){
totalVerts += currPrimEndVertex[i] - currPrimStartVertex[i];
}
if (debug >= 1) System.out.println("totalVerts " + totalVerts);
if (((flags & Primitive.GENERATE_NORMALS) != 0) &&
((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0)){
obj = new QuadArray(totalVerts,
QuadArray.COORDINATES |
QuadArray.NORMALS |
QuadArray.TEXTURE_COORDINATE_2,
1, texCoordSetMap);
}
else
if (((flags & Primitive.GENERATE_NORMALS) == 0) &&
((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0)){
obj = new QuadArray(totalVerts,
QuadArray.COORDINATES |
QuadArray.TEXTURE_COORDINATE_2,
1, texCoordSetMap);
}
else
if (((flags & Primitive.GENERATE_NORMALS) != 0) &&
((flags & Primitive.GENERATE_TEXTURE_COORDS) == 0)){
obj = new QuadArray(totalVerts,
QuadArray.COORDINATES |
QuadArray.NORMALS);
}
else {
obj = new QuadArray(totalVerts,
QuadArray.COORDINATES);
}
Point3f[] newpts = new Point3f[totalVerts];
Vector3f[] newnormals = new Vector3f[totalVerts];
TexCoord2f[] newtcoords = new TexCoord2f[totalVerts];
int currVert = 0;
if (debug > 1) System.out.println("total prims " + currPrimCnt);
for (i = 0; i < currPrimCnt; i++){
if (debug > 1) System.out.println("start " + currPrimStartVertex[i] +
" end " + currPrimEndVertex[i]);
for (int j = currPrimStartVertex[i]; j < currPrimEndVertex[i] - 3;j+=4){
outVertex(newpts, newnormals, newtcoords, currVert++,
pts, normals, tcoords, j);
outVertex(newpts, newnormals, newtcoords, currVert++,
pts, normals, tcoords, j + 1);
outVertex(newpts, newnormals, newtcoords, currVert++,
pts, normals, tcoords, j + 2);
outVertex(newpts, newnormals, newtcoords, currVert++,
pts, normals, tcoords, j + 3);
numTris += 2;
}
}
numVerts = currVert;
obj.setCoordinates(0, newpts);
if ((flags & Primitive.GENERATE_NORMALS) != 0)
obj.setNormals(0, newnormals);
if ((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0)
obj.setTextureCoordinates(0, 0, newtcoords);
geometry = obj;
return obj;
}
private GeometryArray processTriangles()
{
GeometryArray obj = null;
int i;
int totalVerts = 0;
for (i = 0; i < currPrimCnt; i++){
totalVerts += currPrimEndVertex[i] - currPrimStartVertex[i];
}
if (debug >= 1) System.out.println("totalVerts " + totalVerts);
if (((flags & Primitive.GENERATE_NORMALS) != 0) &&
((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0)){
obj = new TriangleArray(totalVerts,
TriangleArray.COORDINATES |
TriangleArray.NORMALS |
TriangleArray.TEXTURE_COORDINATE_2,
1, texCoordSetMap);
}
else
if (((flags & Primitive.GENERATE_NORMALS) == 0) &&
((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0)){
obj = new TriangleArray(totalVerts,
TriangleArray.COORDINATES |
TriangleArray.TEXTURE_COORDINATE_2,
1, texCoordSetMap);
}
else
if (((flags & Primitive.GENERATE_NORMALS) != 0) &&
((flags & Primitive.GENERATE_TEXTURE_COORDS) == 0)){
obj = new TriangleArray(totalVerts,
TriangleArray.COORDINATES |
TriangleArray.NORMALS);
}
else {
obj = new TriangleArray(totalVerts,
TriangleArray.COORDINATES);
}
Point3f[] newpts = new Point3f[totalVerts];
Vector3f[] newnormals = new Vector3f[totalVerts];
TexCoord2f[] newtcoords = new TexCoord2f[totalVerts];
int currVert = 0;
for (i = 0; i < currPrimCnt; i++){
for (int j = currPrimStartVertex[i]; j < currPrimEndVertex[i] - 2;j+=3){
outVertex(newpts, newnormals, newtcoords, currVert++,
pts, normals, tcoords, j);
outVertex(newpts, newnormals, newtcoords, currVert++,
pts, normals, tcoords, j + 1);
outVertex(newpts, newnormals, newtcoords, currVert++,
pts, normals, tcoords, j + 2);
numTris += 1;
}
}
numVerts = currVert;
obj.setCoordinates(0, newpts);
if ((flags & Primitive.GENERATE_NORMALS) != 0)
obj.setNormals(0, newnormals);
if ((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0)
obj.setTextureCoordinates(0, 0, newtcoords);
geometry = obj;
return obj;
}
private GeometryArray processTriangleFan() {
if (debug > 0) System.out.println("processTriangleFan");
GeometryArray obj = null;
int i;
int totalVerts = 0;
int stripCounts[] = new int[currPrimCnt];
// figure out how many vertices we need to hold the individual fans
for (i = 0; i < currPrimCnt; i++) {
stripCounts[i] = currPrimEndVertex[i] - currPrimStartVertex[i];
totalVerts += stripCounts[i];
}
// figure out what flags we need
int tfFlags = TriangleFanArray.COORDINATES;
if ((flags & Primitive.GENERATE_NORMALS) != 0) {
tfFlags |= TriangleFanArray.NORMALS;
}
if ((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0) {
tfFlags |= TriangleFanArray.TEXTURE_COORDINATE_2;
}
// create the TriangleFanArray
obj = new TriangleFanArray(totalVerts, tfFlags, 1, texCoordSetMap,
stripCounts);
// allocate space for vertex info
Point3f[] newpts = new Point3f[totalVerts];
Vector3f[] newnormals = new Vector3f[totalVerts];
TexCoord2f[] newtcoords = new TexCoord2f[totalVerts];
int currVert = 0;
// repeat for each fan
for (i = 0; i < currPrimCnt; i++) {
for (int j = currPrimStartVertex[i]; j < currPrimEndVertex[i]; j++) {
outVertex(newpts, newnormals, newtcoords, currVert++, pts,
normals, tcoords, j);
}
}
for (i = 0; i < newpts.length; i++) {
if (debug > 1) System.out.println("i = " + i + " " + newpts[i]);
}
numVerts = currVert;
numTris = totalVerts - currPrimCnt * 2;
// set the coordinates on the GeometryArray
obj.setCoordinates(0, newpts);
// set the normals and tex coords if necessary
if ((flags & Primitive.GENERATE_NORMALS) != 0) {
obj.setNormals(0, newnormals);
}
if ((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0) {
obj.setTextureCoordinates(0, 0, newtcoords);
}
geometry = obj;
return obj;
}
void outVertex(Point3f[] dpts, Vector3f[] dnormals, TexCoord2f[] dtcoords,
int dloc,
Point3f[] spts, Vector3f[] snormals, TexCoord2f[] stcoords,
int sloc)
{
if (debug >= 1) System.out.println("v " + spts[sloc].x + " " +
spts[sloc].y + " " +
spts[sloc].z);
// PSP: Do we really need new points here?
dpts[dloc] = new Point3f(spts[sloc]);
if ((flags & Primitive.GENERATE_NORMALS) != 0){
dnormals[dloc] = new Vector3f(snormals[sloc]);
}
if ((flags & Primitive.GENERATE_TEXTURE_COORDS) != 0){
if (debug >= 2) System.out.println("final out tcoord");
dtcoords[dloc] = new TexCoord2f(stcoords[sloc]);
}
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Stripifier.java 0000644 0000000 0000000 00000203237 10563126524 030432 0 ustar root root /*
* $RCSfile: Stripifier.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:20 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import com.sun.j3d.utils.geometry.GeometryInfo;
import java.util.LinkedList;
import java.util.ArrayList;
import com.sun.j3d.internal.J3dUtilsI18N;
/**
* The Stripifier utility will change the primitive of the GeometryInfo
* object to Triangle Strips. The strips are made by analyzing the
* triangles in the original data and connecting them together.
*
* Normal Generation should be performed on the GeometryInfo object
* before Stripification, for best results. Example:
*
*
*
* 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.
*
* 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.
*
* 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() ;
}
././@LongLink 0000000 0000000 0000000 00000000155 00000000000 011566 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamColor.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamCo0000644 0000000 0000000 00000021715 10563126520 032032 0 ustar root root /*
* $RCSfile: CompressionStreamColor.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/02/09 17:20:16 $
* $State: Exp $
*/
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
* 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.
*
* 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.
*
* @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") ;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CommandStream.java 0000644 0000000 0000000 00000020621 10563126520 031540 0 ustar root root /*
* $RCSfile: CommandStream.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/02/09 17:20:16 $
* $State: Exp $
*/
package com.sun.j3d.utils.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.
*
* 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.
*
* 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.
*
* 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 ;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/HuffmanTable.java 0000644 0000000 0000000 00000037425 10563126521 031355 0 ustar root root /*
* $RCSfile: HuffmanTable.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/02/09 17:20:17 $
* $State: Exp $
*/
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
* HuffmanNode objects. A HuffmanNode contains a tag describing the
* associated token's data length, right shift value, and absolute/relative
* status.
*
* 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) ;
}
}
././@LongLink 0000000 0000000 0000000 00000000155 00000000000 011566 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryFile.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryF0000644 0000000 0000000 00000077311 10563126520 032024 0 ustar root root /*
* $RCSfile: CompressedGeometryFile.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/02/09 17:20:16 $
* $State: Exp $
*/
package com.sun.j3d.utils.compression;
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
// 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.
*
* @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 ;
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.
CompressedGeometryHeader 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 CompressedGeometry node component, or null if the last object
* has been read
* @exception IOException if read fails
*/
public CompressedGeometry 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 CompressedGeometry node components.
* @exception IOException if read fails
*/
public CompressedGeometry[] read() throws IOException {
long startTime = 0 ;
CompressedGeometry cg[] = new CompressedGeometry[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 CompressedGeometry node component
* @exception IndexOutOfBoundsException if object index is
* out of range
* @exception IOException if read fails
*/
public CompressedGeometry 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(CompressedGeometry cg) throws IOException {
CompressedGeometryHeader cgh = new CompressedGeometryHeader() ;
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 CompressedGeometryHeader object describing the data.
* @param geometry the compressed geometry data
* @exception IOException if write fails
*/
public void write(CompressedGeometryHeader 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 CompressedGeometryHeader.POINT_BUFFER:
geomDataType = TYPE_POINT ;
break ;
case CompressedGeometryHeader.LINE_BUFFER:
geomDataType = TYPE_LINE ;
break ;
case CompressedGeometryHeader.TRIANGLE_BUFFER:
geomDataType = TYPE_TRIANGLE ;
break ;
}
if ((cgh.bufferDataPresent &
CompressedGeometryHeader.NORMAL_IN_BUFFER) != 0)
geomDataType |= NORMAL_PRESENT_MASK ;
if ((cgh.bufferDataPresent &
CompressedGeometryHeader.COLOR_IN_BUFFER) != 0)
geomDataType |= COLOR_PRESENT_MASK ;
if ((cgh.bufferDataPresent &
CompressedGeometryHeader.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 CompressedGeometryHeader() ;
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.
//
CompressedGeometry 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.
//
CompressedGeometry newCG(int geomSize,
int geomStart,
int geomDataType) {
cgh.size = geomSize ;
cgh.start = geomStart ;
if ((geomDataType & TYPE_MASK) == TYPE_POINT)
cgh.bufferType = CompressedGeometryHeader.POINT_BUFFER ;
else if ((geomDataType & TYPE_MASK) == TYPE_LINE)
cgh.bufferType = CompressedGeometryHeader.LINE_BUFFER ;
else if ((geomDataType & TYPE_MASK) == TYPE_TRIANGLE)
cgh.bufferType = CompressedGeometryHeader.TRIANGLE_BUFFER ;
cgh.bufferDataPresent = 0 ;
if ((geomDataType & NORMAL_PRESENT_MASK) != 0)
cgh.bufferDataPresent |=
CompressedGeometryHeader.NORMAL_IN_BUFFER ;
if ((geomDataType & COLOR_PRESENT_MASK) != 0)
cgh.bufferDataPresent |=
CompressedGeometryHeader.COLOR_IN_BUFFER ;
if ((geomDataType & ALPHA_PRESENT_MASK) != 0)
cgh.bufferDataPresent |=
CompressedGeometryHeader.ALPHA_IN_BUFFER ;
return new CompressedGeometry(cgh, cgBuffer) ;
}
/**
* Release file resources when this object is garbage collected.
*/
protected void finalize() {
close() ;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/package.html 0000644 0000000 0000000 00000000606 10507260104 030420 0 ustar root root
Deprecated: Use
*
* At 1 bit of quantization it is not possible to express positive
* absolute or delta positions.
*/
int positionQuant ;
/**
* Current color component (R, G, B, A) quantization value. This can
* range from 2 to 16 bits and has a default of 9.
*
* A color component is represented with a signed fixed-point value in
* order to be able express negative deltas; the default of 9 bits
* corresponds to the 8-bit color component range of the graphics hardware
* commonly available. Colors must be non-negative, so the lower limit of
* quantization is 2 bits.
*/
int colorQuant ;
/**
* Current normal component (U and V) quantization value. This can range
* from 0 to 6 bits and has a default of 6.
*
* At 0 bits of quantization normals are represented only as 6 bit
* sextant/octant pairs and 14 specially encoded normals (the 6 axis
* normals and the 8 octant midpoint normals); since U and V can only be 0
* at the minimum quantization, the totally number of unique normals is
* 12 + 14 = 26.
*/
int normalQuant ;
/**
* Flag indicating position quantization change.
*/
boolean positionQuantChanged ;
/**
* Flag indicating color quantization change.
*/
boolean colorQuantChanged ;
/**
* Flag indicating normal quantization change.
*/
boolean normalQuantChanged ;
/**
* Last quantized position.
*/
int lastPosition[] = new int[3] ;
/**
* Last quantized color.
*/
int lastColor[] = new int[4] ;
/**
* Last quantized normal's sextant.
*/
int lastSextant ;
/**
* Last quantized normal's octant.
*/
int lastOctant ;
/**
* Last quantized normal's U encoding parameter.
*/
int lastU ;
/**
* Last quantized normal's V encoding parameter.
*/
int lastV ;
/**
* Flag indicating last normal used a special encoding.
*/
boolean lastSpecialNormal ;
/**
* Flag indicating the first position in this stream.
*/
boolean firstPosition ;
/**
* Flag indicating the first color in this stream.
*/
boolean firstColor ;
/**
* Flag indicating the first normal in this stream.
*/
boolean firstNormal ;
/**
* The total number of bytes used to create the uncompressed geometric
* elements in this stream, useful for performance analysis. This
* excludes mesh buffer references.
*/
int byteCount ;
/**
* The number of vertices created for this stream, excluding mesh buffer
* references.
*/
int vertexCount ;
/**
* The number of mesh buffer references created for this stream.
*/
int meshReferenceCount ;
/**
* Mesh buffer mirror used for computing deltas during quantization pass
* and a limited meshing algorithm for unstripped data.
*/
MeshBuffer meshBuffer = new MeshBuffer() ;
// Collection which holds the elements of this stream.
private Collection stream ;
// True if preceding stream elements were colors or normals. Used to flag
// color and normal mesh buffer substitution when computing deltas during
// quantization pass.
private boolean lastElementColor = false ;
private boolean lastLastElementColor = false ;
private boolean lastElementNormal = false ;
private boolean lastLastElementNormal = false ;
// Some convenient temporary holding variables.
private Point3f p3f = new Point3f() ;
private Color3f c3f = new Color3f() ;
private Color4f c4f = new Color4f() ;
private Vector3f n3f = new Vector3f() ;
// Private constructor for common initializations.
private CompressionStream() {
this.stream = new LinkedList() ;
byteCount = 0 ;
vertexCount = 0 ;
meshReferenceCount = 0 ;
mcBounds[0] = new Point3d(Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY) ;
mcBounds[1] = new Point3d(Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY) ;
qcBounds[0] = new Point3i(Integer.MAX_VALUE,
Integer.MAX_VALUE,
Integer.MAX_VALUE) ;
qcBounds[1] = new Point3i(Integer.MIN_VALUE,
Integer.MIN_VALUE,
Integer.MIN_VALUE) ;
/* normalized bounds computed from quantized bounds */
ncBounds[0] = new Point3d() ;
ncBounds[1] = new Point3d() ;
}
/**
* Creates a new CompressionStream for the specified geometry type and
* vertex format.
*
* @param streamType type of data in this stream, either
* CompressedGeometryHeader.POINT_BUFFER,
* CompressedGeometryHeader.LINE_BUFFER, or
* CompressedGeometryHeader.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 CompressedGeometryHeader.TRIANGLE_BUFFER ;
else if (ga instanceof LineArray ||
ga instanceof IndexedLineArray ||
ga instanceof LineStripArray ||
ga instanceof IndexedLineStripArray)
return CompressedGeometryHeader.LINE_BUFFER ;
else
return CompressedGeometryHeader.POINT_BUFFER ;
}
/**
* Iterates across all compression stream elements and applies
* quantization parameters, encoding consecutive vertices as delta values
* whenever possible. Each geometric element is mapped to a HuffmanNode
* object containing its resulting bit length, right shift (trailing 0
* count), and absolute or relative status.
*
* Positions are normalized to span a unit cube via an offset and a
* uniform scale factor that maps the midpoint of the object extents along
* each dimension to the origin, and the longest dimension of the object to
* the open interval (-1.0 .. +1.0). The geometric endpoints along that
* dimension are both one quantum away from unity; for example, at a
* position quantization of 6 bits, an object would be normalized so that
* its most negative dimension is at (-1 + 1/64) and the most positive is
* at (1 - 1/64).
*
* Normals are assumed to be of unit length. Color components are clamped
* to the [0..1) range, where the right endpoint is one quantum less
* than 1.0.
*
* @param huffmanTable Table which will map geometric compression stream
* elements to HuffmanNode objects describing each element's data
* representation. This table can then be processed with Huffman's
* algorithm to optimize the bit length of descriptor tags according to
* the number of geometric elements mapped to each tag.
*/
void quantize(HuffmanTable huffmanTable) {
// Set up default initial quantization parameters. The position and
// color parameters specify the number of bits for each X, Y, Z, R, G,
// B, or A component. The normal quantization parameter specifies the
// number of bits for each U and V component.
positionQuant = 16 ;
colorQuant = 9 ;
normalQuant = 6 ;
// Compute position center and scaling for normalization to the unit
// cube. This is a volume bounded by the open intervals (-1..1) on
// each axis.
center[0] = (mcBounds[1].x + mcBounds[0].x) / 2.0 ;
center[1] = (mcBounds[1].y + mcBounds[0].y) / 2.0 ;
center[2] = (mcBounds[1].z + mcBounds[0].z) / 2.0 ;
double xRange = mcBounds[1].x - mcBounds[0].x ;
double yRange = mcBounds[1].y - mcBounds[0].y ;
double zRange = mcBounds[1].z - mcBounds[0].z ;
if (xRange > yRange)
positionRangeMaximum = xRange ;
else
positionRangeMaximum = yRange ;
if (zRange > positionRangeMaximum)
positionRangeMaximum = zRange ;
// Adjust the range of the unit cube to match the default
// quantization.
//
// This scale factor along with the center values computed above will
// produce 16-bit integer representations of the floating point
// position coordinates ranging symmetrically about 0 from -32767 to
// +32767. -32768 is not used and the normalized floating point
// position coordinates of -1.0 as well as +1.0 will not be
// represented.
//
// Applications which wish to seamlessly stitch together compressed
// objects will need to be aware that the range of normalized
// positions will be one quantum away from the [-1..1] endpoints of
// the unit cube and should adjust scale factors accordingly.
scale = (2.0 / positionRangeMaximum) * (32767.0 / 32768.0) ;
// Flag quantization change.
positionQuantChanged = colorQuantChanged = normalQuantChanged = true ;
// Flag first position, color, and normal.
firstPosition = firstColor = firstNormal = true ;
// Apply quantization.
Iterator i = stream.iterator() ;
while (i.hasNext()) {
Object o = i.next() ;
if (o instanceof CompressionStreamElement) {
((CompressionStreamElement)o).quantize(this, huffmanTable) ;
// Keep track of whether last two elements were colors or
// normals for mesh buffer component substitution semantics.
lastLastElementColor = lastElementColor ;
lastLastElementNormal = lastElementNormal ;
lastElementColor = lastElementNormal = false ;
if (o instanceof CompressionStreamColor)
lastElementColor = true ;
else if (o instanceof CompressionStreamNormal)
lastElementNormal = true ;
}
}
// Compute the bounds in normalized coordinates.
ncBounds[0].x = (double)qcBounds[0].x / 32768.0 ;
ncBounds[0].y = (double)qcBounds[0].y / 32768.0 ;
ncBounds[0].z = (double)qcBounds[0].z / 32768.0 ;
ncBounds[1].x = (double)qcBounds[1].x / 32768.0 ;
ncBounds[1].y = (double)qcBounds[1].y / 32768.0 ;
ncBounds[1].z = (double)qcBounds[1].z / 32768.0 ;
}
/**
* Iterates across all compression stream elements and builds the
* compressed geometry command stream output.
*
* @param huffmanTable Table which maps geometric elements in this stream
* to tags describing the encoding parameters (length, shift, and
* absolute/relative status) to be used for their representations in the
* compressed output. All tags must be 6 bits or less in length, and the
* sum of the number of bits in the tag plus the number of bits in the
* data it describes must be at least 6 bits in length.
*
* @param outputBuffer CommandStream to use for collecting the compressed
* bits.
*/
void outputCommands(HuffmanTable huffmanTable, CommandStream outputBuffer) {
//
// The first command output is setState to indicate what data is
// bundled with each vertex. Although the semantics of geometry
// decompression allow setState to appear anywhere in the stream, this
// cannot be handled by the current Java 3D software decompressor,
// which internally decompresses an entire compressed buffer into a
// single retained object sharing a single consistent vertex format.
// This limitation may be removed in subsequent releases of Java 3D.
//
int bnv = (vertexNormals? 1 : 0) ;
int bcv = ((vertexColor3 || vertexColor4)? 1 : 0) ;
int cap = (vertexColor4? 1 : 0) ;
int command = CommandStream.SET_STATE | bnv ;
long data = (bcv << 2) | (cap << 1) ;
// Output the setState command.
outputBuffer.addCommand(command, 8, data, 3) ;
// Output the Huffman table commands.
huffmanTable.outputCommands(outputBuffer) ;
// Output each compression stream element's data.
Iterator i = stream.iterator() ;
while (i.hasNext()) {
Object o = i.next() ;
if (o instanceof CompressionStreamElement)
((CompressionStreamElement)o).outputCommand(huffmanTable,
outputBuffer) ;
}
// Finish the header-forwarding interleave and long-word align.
outputBuffer.end() ;
}
/**
* Retrieve the total size of the uncompressed geometric data in bytes,
* excluding mesh buffer references.
* @return uncompressed byte count
*/
int getByteCount() {
return byteCount ;
}
/**
* Retrieve the the number of vertices created for this stream, excluding
* mesh buffer references.
* @return vertex count
*/
int getVertexCount() {
return vertexCount ;
}
/**
* Retrieve the number of mesh buffer references created for this stream.
* @return mesh buffer reference count
*/
int getMeshReferenceCount() {
return meshReferenceCount ;
}
/**
* Stream element that sets position quantization during quantize pass.
*/
private class PositionQuant extends CompressionStreamElement {
int value ;
PositionQuant(int value) {
this.value = value ;
}
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.
*
* Each Shape3D in the array must be of the same dimensionality (point,
* line, or surface) and have the same vertex format as the others.
* Texture coordinates are ignored.
*
* If a color is specified in the material attributes for a Shape3D then
* that color is added to the CompressionStream as the current global
* color. Subsequent colors as well as any colors bundled with vertices
* will override it. Only the material diffuse colors are used; all other
* appearance attributes are ignored.
*
* @param positionQuant
* number of bits to quantize each position's X, Y,
* and Z components, ranging from 1 to 16
*
* @param colorQuant
* number of bits to quantize each color's R, G, B, and
* alpha components, ranging from 2 to 16
*
* @param normalQuant
* number of bits for quantizing each normal's U and V components, ranging
* from 0 to 6
*
* @param shapes
* an array of Shape3D scene graph objects containing
* GeometryArray objects, all with the same vertex format and
* dimensionality
*
* @exception IllegalArgumentException if any Shape3D has an inconsistent
* dimensionality or vertex format, or if any Shape3D contains a geometry
* component that is not a GeometryArray
*
* @see Shape3D
* @see GeometryArray
* @see GeometryCompressor
*/
public CompressionStream(int positionQuant, int colorQuant,
int normalQuant, Shape3D shapes[]) {
this() ;
if (debug) System.out.println("CompressionStream(Shape3D[]):") ;
if (shapes == null)
throw new IllegalArgumentException("null Shape3D array") ;
if (shapes.length == 0)
throw new IllegalArgumentException("zero-length Shape3D array") ;
if (shapes[0] == null)
throw new IllegalArgumentException("Shape3D at index 0 is null") ;
long startTime = 0 ;
if (benchmark) startTime = System.currentTimeMillis() ;
Geometry g = shapes[0].getGeometry() ;
if (! (g instanceof GeometryArray))
throw new IllegalArgumentException
("Shape3D at index 0 is not a GeometryArray") ;
GeometryArray ga = (GeometryArray)g ;
this.streamType = getStreamType(ga) ;
this.vertexComponents = getVertexComponents(ga.getVertexFormat()) ;
// Add global quantization parameters to the start of the stream.
addPositionQuantization(positionQuant) ;
addColorQuantization(colorQuant) ;
addNormalQuantization(normalQuant) ;
// Loop through all shapes.
for (int s = 0 ; s < shapes.length ; s++) {
if (debug) System.out.println("\nShape3D " + s + ":") ;
g = shapes[s].getGeometry() ;
if (! (g instanceof GeometryArray))
throw new IllegalArgumentException
("Shape3D at index " + s + " is not a GeometryArray") ;
// Check for material color and add it to the stream if it exists.
Appearance a = shapes[s].getAppearance() ;
if (a != null) {
Material m = a.getMaterial() ;
if (m != null) {
m.getDiffuseColor(c3f) ;
if (vertexColor4) {
c4f.set(c3f.x, c3f.y, c3f.z, 1.0f) ;
addColor(c4f) ;
} else
addColor(c3f) ;
}
}
// Add the geometry array to the stream.
addGeometryArray((GeometryArray)g) ;
}
if (benchmark) {
long t = System.currentTimeMillis() - startTime ;
System.out.println
("\nCompressionStream:\n" + shapes.length + " shapes in " +
(t / 1000f) + " sec") ;
}
}
/**
* Creates a CompressionStream from an array of Shape3D scene graph
* objects. These Shape3D objects may only consist of a GeometryArray
* component and an optional Appearance component. The resulting stream
* may be used as input to the GeometryCompressor methods.
*
* Each Shape3D in the array must be of the same dimensionality (point,
* line, or surface) and have the same vertex format as the others.
* Texture coordinates are ignored.
*
* If a color is specified in the material attributes for a Shape3D then
* that color is added to the CompressionStream as the current global
* color. Subsequent colors as well as any colors bundled with vertices
* will override it. Only the material diffuse colors are used; all other
* appearance attributes are ignored.
*
* Defaults of 16, 9, and 6 bits are used as the quantization values for
* positions, colors, and normals respectively. These are the maximum
* resolution values defined for positions and normals; the default of 9
* for color is the equivalent of the 8 bits of RGBA component resolution
* commonly available in graphics frame buffers.
*
* @param shapes
* an array of Shape3D scene graph objects containing
* GeometryArray objects, all with the same vertex format and
* dimensionality.
*
* @exception IllegalArgumentException if any Shape3D has an inconsistent
* dimensionality or vertex format, or if any Shape3D contains a geometry
* component that is not a GeometryArray
*
* @see Shape3D
* @see GeometryArray
* @see GeometryCompressor
*/
public CompressionStream(Shape3D shapes[]) {
this(16, 9, 6, shapes) ;
}
/**
* Creates a CompressionStream from an array of GeometryInfo objects. The
* resulting stream may be used as input to the GeometryCompressor
* methods.
*
* Each GeometryInfo in the array must be of the same dimensionality
* (point, line, or surface) and have the same vertex format as the
* others. Texture coordinates are ignored.
*
* @param positionQuant
* number of bits to quantize each position's X, Y,
* and Z components, ranging from 1 to 16
*
* @param colorQuant
* number of bits to quantize each color's R, G, B, and
* alpha components, ranging from 2 to 16
*
* @param normalQuant
* number of bits for quantizing each normal's U and V components, ranging
* from 0 to 6
*
* @param geometry
* an array of GeometryInfo objects, all with the same
* vertex format and dimensionality
*
* @exception IllegalArgumentException if any GeometryInfo object has an
* inconsistent dimensionality or vertex format
*
* @see GeometryInfo
* @see GeometryCompressor
*/
public CompressionStream(int positionQuant, int colorQuant,
int normalQuant, GeometryInfo geometry[]) {
this() ;
if (debug) System.out.println("CompressionStream(GeometryInfo[])") ;
if (geometry == null)
throw new IllegalArgumentException("null GeometryInfo array") ;
if (geometry.length == 0)
throw new IllegalArgumentException
("zero-length GeometryInfo array") ;
if (geometry[0] == null)
throw new IllegalArgumentException
("GeometryInfo at index 0 is null") ;
long startTime = 0 ;
if (benchmark) startTime = System.currentTimeMillis() ;
GeometryArray ga = geometry[0].getGeometryArray() ;
this.streamType = getStreamType(ga) ;
this.vertexComponents = getVertexComponents(ga.getVertexFormat()) ;
// Add global quantization parameters to the start of the stream.
addPositionQuantization(positionQuant) ;
addColorQuantization(colorQuant) ;
addNormalQuantization(normalQuant) ;
// Loop through all GeometryInfo objects and add them to the stream.
for (int i = 0 ; i < geometry.length ; i++) {
if (debug) System.out.println("\nGeometryInfo " + i + ":") ;
addGeometryArray(geometry[i].getGeometryArray()) ;
}
if (benchmark) {
long t = System.currentTimeMillis() - startTime ;
System.out.println
("\nCompressionStream:\n" + geometry.length +
" GeometryInfo objects in " + (t / 1000f) + " sec") ;
}
}
/**
* Creates a CompressionStream from an array of GeometryInfo objects. The
* resulting stream may be used as input to the GeometryCompressor
* methods.
*
* Each GeometryInfo in the array must be of the same dimensionality
* (point, line, or surface) and have the same vertex format as the
* others. Texture coordinates are ignored.
*
* Defaults of 16, 9, and 6 bits are used as the quantization values for
* positions, colors, and normals respectively. These are the maximum
* resolution values defined for positions and normals; the default of 9
* for color is the equivalent of the 8 bits of RGBA component resolution
* commonly available in graphics frame buffers.
*
* @param geometry
* an array of GeometryInfo objects, all with the same
* vertex format and dimensionality
*
* @exception IllegalArgumentException if any GeometryInfo object has an
* inconsistent dimensionality or vertex format
*
* @see GeometryInfo
* @see GeometryCompressor
*/
public CompressionStream(GeometryInfo geometry[]) {
this(16, 9, 6, geometry) ;
}
/**
* Get the original bounds of the coordinate data, in modeling coordinates.
* Coordinate data is positioned and scaled to a normalized cube after
* compression.
*
* @return Point3d array of length 2, where the 1st Point3d is the lower
* bounds and the 2nd Point3d is the upper bounds.
* @since Java 3D 1.3
*/
public Point3d[] getModelBounds() {
Point3d[] bounds = new Point3d[2] ;
bounds[0] = new Point3d(mcBounds[0]) ;
bounds[1] = new Point3d(mcBounds[1]) ;
return bounds ;
}
/**
* Get the bounds of the compressed object in normalized coordinates.
* These have an maximum bounds by [-1.0 .. +1.0] across each axis.
*
* @return Point3d array of length 2, where the 1st Point3d is the lower
* bounds and the 2nd Point3d is the upper bounds.
* @since Java 3D 1.3
*/
public Point3d[] getNormalizedBounds() {
Point3d[] bounds = new Point3d[2] ;
bounds[0] = new Point3d(ncBounds[0]) ;
bounds[1] = new Point3d(ncBounds[1]) ;
return bounds ;
}
}
././@LongLink 0000000 0000000 0000000 00000000156 00000000000 011567 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVertex.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVe0000644 0000000 0000000 00000026577 10563126520 032056 0 ustar root root /*
* $RCSfile: CompressionStreamVertex.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/02/09 17:20:16 $
* $State: Exp $
*/
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
* 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 ;
}
}
././@LongLink 0000000 0000000 0000000 00000000151 00000000000 011562 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.0000644 0000000 0000000 00000017014 10563126520 032016 0 ustar root root /*
* $RCSfile: GeometryCompressor.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/02/09 17:20:16 $
* $State: Exp $
*/
package com.sun.j3d.utils.compression;
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
* 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 CompressedGeometry node component or appended
* to a CompressedGeometryFile.
*
* @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 ;
private static final boolean printStream = false ;
private static final boolean printHuffman = false ;
private HuffmanTable huffmanTable ;
private CommandStream outputBuffer ;
private CompressedGeometryHeader cgHeader ;
private long startTime ;
public GeometryCompressor() {
// Create a compressed geometry header.
cgHeader = new CompressedGeometryHeader() ;
// 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 CompressedGeometry node component.
*
* @param stream CompressionStream containing the geometry to be compressed
* @return a CompressedGeometry node component
*/
public CompressedGeometry compress(CompressionStream stream) {
CompressedGeometry cg ;
compressStream(stream) ;
cg = new CompressedGeometry(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 CompressedGeometryHeader 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 |=
CompressedGeometryHeader.NORMAL_IN_BUFFER ;
if (stream.vertexColor3 || stream.vertexColor4)
cgHeader.bufferDataPresent |=
CompressedGeometryHeader.COLOR_IN_BUFFER ;
if (stream.vertexColor4)
cgHeader.bufferDataPresent |=
CompressedGeometryHeader.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()) ;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/MeshBuffer.java 0000644 0000000 0000000 00000020136 10563126521 031036 0 ustar root root /*
* $RCSfile: MeshBuffer.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/02/09 17:20:17 $
* $State: Exp $
*/
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
* 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)] ;
}
}
././@LongLink 0000000 0000000 0000000 00000000156 00000000000 011567 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNormal.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNo0000644 0000000 0000000 00000053646 10563126520 032055 0 ustar root root /*
* $RCSfile: CompressionStreamNormal.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/02/09 17:20:16 $
* $State: Exp $
*/
package com.sun.j3d.utils.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") ;
}
}
././@LongLink 0000000 0000000 0000000 00000000157 00000000000 011570 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamElement.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamEl0000644 0000000 0000000 00000026367 10563126520 032041 0 ustar root root /*
* $RCSfile: CompressionStreamElement.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/02/09 17:20:16 $
* $State: Exp $
*/
package com.sun.j3d.utils.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.
*
* @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.
*
* @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 ;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/ 0000755 0000000 0000000 00000000000 11240226656 025724 5 ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/ 0000755 0000000 0000000 00000000000 11240226646 026332 5 ustar root root ././@LongLink 0000000 0000000 0000000 00000000155 00000000000 011566 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileReader.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileRea0000644 0000000 0000000 00000017516 10563126533 031537 0 ustar root root /*
* $RCSfile: SceneGraphFileReader.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:27 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io;
import java.io.File;
import java.io.IOException;
import javax.media.j3d.VirtualUniverse;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.SceneGraphObject;
import javax.media.j3d.Canvas3D;
import com.sun.j3d.utils.universe.ConfiguredUniverse;
import com.sun.j3d.utils.scenegraph.io.retained.RandomAccessFileControl;
/**
* Read Java3D BranchGraphs and/or Universe from a file. Individual branchgraphs or an
* entire Universe can be read and references (shared nodes and components)
* between the graphs are handled correctly.
*/
public class SceneGraphFileReader extends java.lang.Object {
private RandomAccessFileControl fileControl;
/**
* Creates new SceneGraphFileReader.
*/
public SceneGraphFileReader( java.io.File file ) throws IOException {
fileControl = new RandomAccessFileControl();
fileControl.openFile( file );
}
/**
* Create and return a ConfiguredUniverse with the PlatformGeometry, ViewerAvatar,
* and Locales saved in the file. The MultiTransformGroup between the ViewingPlatform
* and the View is also restored. Universe configuration information is retrieved
* via
* If the file does not contain universe information, null is returned.
*
* @param attachBranchGraphs load and attach all the branchgraphs
* to the universe.
* @see ConfiguredUniverse#getConfigURL
*/
public ConfiguredUniverse readUniverse(boolean attachBranchGraphs ) throws IOException {
return fileControl.readUniverse( attachBranchGraphs, null );
}
/**
* Set the ClassLoader used to load the scene graph objects and
* deserialize user data
*/
public void setClassLoader( ClassLoader classLoader ) {
fileControl.setClassLoader( classLoader );
}
/**
* Get the ClassLoader used to load the scene graph objects and
* deserialize user data
*/
public ClassLoader getClassLoader() {
return fileControl.getClassLoader();
}
/**
* Create and return a ConfiguredUniverse with the PlatformGeometry, ViewerAvatar,
* and Locales saved in the file. The MultiTransformGroup between the ViewingPlatform
* and the View is also restored.
* If the file does not contain universe information, null is returned.
*
* @param attachBranchGraphs load and attach all the branchgraphs
* to the universe.
* @param canvas The canvas to be associated with the Universe.
*/
public ConfiguredUniverse readUniverse(boolean attachBranchGraphs,
Canvas3D canvas) throws IOException {
return fileControl.readUniverse( attachBranchGraphs, canvas );
}
/**
* Get the UserData in the File header
*/
public Object readUserData() throws IOException {
return fileControl.getUserData();
}
/**
* Get the Description of this file's contents
*/
public String readDescription() throws IOException {
return fileControl.readFileDescription();
}
/**
* Return the number of BranchGraphs in the file
*/
public int getBranchGraphCount() {
return fileControl.getBranchGraphCount();
}
/**
* Read the BranchGraph at index in the file. If the graph
* contains references to nodes in other BranchGraphs that have not already been
* loaded, they will also be loaded and returned.
*
* The requested graph will always be the first element in the array.
*
* The file index of all the Graphs can be discovered using
*
* @param index The index of the Graph in the file. First graph is at index 0
*
* @see #getBranchGraphPosition( BranchGroup graph )
*
*/
public BranchGroup[] readBranchGraph(int index) throws IOException {
return fileControl.readBranchGraph( index );
}
/**
* Read and return all the branchgraphs in the file
*/
public BranchGroup[] readAllBranchGraphs() throws IOException {
return fileControl.readAllBranchGraphs();
}
/**
* Remove the IO system's reference to this branchgraph and all its nodes.
*
* References to all loaded graphs are maintained by the IO system in
* order to facilitate node and component sharing between the graphs.
*
* This call removes the references to graph
*
* NOT CURRENTLY IMPLEMENTED
*/
public void dereferenceBranchGraph( BranchGroup graph ) {
throw new RuntimeException("Not implemented");
}
/**
* Given a BranchGraph that has been loaded return the index of the
* graph in the file. The the Branchgroup isn't found, -1 is returned.
*/
public int getBranchGraphPosition( BranchGroup graph ) {
return fileControl.getBranchGraphPosition( graph );
}
/**
* Read the userdata for the branchgraph at 'index' in the file
*
* @param index the index of the graph in the file
*/
public Object readBranchGraphUserData( int index ) throws IOException {
return fileControl.readBranchGraphUserData( index );
}
/**
* Return the names of all the named objects
*/
public String[] getNames() {
return fileControl.getNames();
}
/**
* Return the named object.
*
* @param name The name of the object
*
* @exception NamedObjectException is thrown if the name is not known to the system
* @exception ObjectNotLoadedException is thrown if the named object has not been loaded yet
*/
public SceneGraphObject getNamedObject( String name ) throws NamedObjectException, ObjectNotLoadedException {
return fileControl.getNamedObject( name );
}
/**
* Close the file and cleanup internal data structures
*/
public void close() throws IOException {
fileControl.close();
}
}
././@LongLink 0000000 0000000 0000000 00000000145 00000000000 011565 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphIO.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphIO.java0000644 0000000 0000000 00000010546 10563126533 031453 0 ustar root root /*
* $RCSfile: SceneGraphIO.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:27 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io;
/**
* Implement this interface in any classes that subclass a Java3D SceneGraphObject
* in order to have your class handled correctly by scenegraph.io.
*
* More information and example code is provided here.
*
* Classes that implement this interface MUST have a no-arg constructor
*/
public interface SceneGraphIO {
/**
* The method is called before writeSGObject and gives the user the chance
* to create references to other Nodes and NodeComponents.
*
* References take the form of a nodeID, of type integer. Every SceneGraphObject
* is assigned a unique ID.
*
* The user must save the reference information in writeSGObject
*
* @param ref provides methods to create references to a SceneGraphObject
*/
public void createSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref );
/**
* Within this method the user should restore references to the SceneGraphObjects
* whose nodeID's were created with createSceneGraphObjectReferences
* This method is called once the all objects in the scenegraph have been loaded.
*
*
* @param ref provides methods to resolve references to a SceneGraphObject
*/
public void restoreSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref );
/**
* This method should store all the local state of the object and any references
* to other SceneGraphObjects into
* Using this class to write to a FileOutputStream is not recommended. Use
* SceneGraphFileWriter instead to achieve maximum performance and flexibility.
*/
public class SceneGraphStreamWriter extends java.lang.Object {
private StreamControl control;
private DataOutputStream out;
/** Creates new SceneGraphStreamWriter that will write to the supplied stream */
public SceneGraphStreamWriter(java.io.OutputStream outputStream ) throws IOException {
this.out = new java.io.DataOutputStream( outputStream );
control = new StreamControl( out );
control.writeStreamHeader();
}
/**
* Write
*
* If
*
* If
*
* @param universe The universe to write
* @param writeContent Flag enabling the BranchGraphs to be written
*
* @exception IOException
* @exception UnsupportedUniverseException Thrown if the universe class is not
* supported by this implementation
*/
public void writeUniverse( SimpleUniverse universe, boolean writeContent ) throws IOException, UnsupportedUniverseException {
control.writeUniverse( out, universe, writeContent );
}
/**
* Write the entire graph to the stream.
*
* The API will correctly handle NodeComponents that are shared
* between seperate graphs. However Nodes cannot be referenced
* in other Graphs.
*
* If a reference to a Node in another graph is encountered a
* DanglingReferenceException will be thrown.
*
* This package provides a Java3D SceneGraph IO capability.
The API supports IO of a scenegraph to and from a Java Stream and/or
RandomAccessFile. The features offered for these two io systems are
somewhat different. The SceneGraphFileReader and SceneGraphFileWriter classes provide
IO to and from a RandomAccessFile. They allow a universe and/or
multiple BranchGraphs to be written to the file with Node's and
NodeComponent's shared between the separate graphs. The graphs can be
read in any order. SceneGraphStreamReader and SceneGraphStreamWriter classes provide
IO to and from a Stream. These classes allow a universe and/or
multiple BranchGraphs to be passed over stream. In contrast to the
FileReader/Writer sharing of Node's is NOT supported between graphs
by the API. Sharing of node components is supported. If your
application requires references to Nodes in other graphs (such as
SharedGroups) the application must handle the references using the
namedObjects constructs. Note : If you use SceneGraphStreamWriter class to write to a
FileOutputStream the resulting file cannot be read using the
SceneGraphFileReader, the converse is also true, you can not use a
FileInputStream to load a file written by SceneGraphFileWriter.
The package supports the IO of all the Java3D 1.3 core classes
and many of the utilities. It also includes interfaces which can be
implemented to allow user defined subclasses of SceneGraphObjects to
be stored. Information on the extensibility can be found
here
The package has a number of properties which can be used to control the IO
behavior
j3d.io.ImageCompression this can be set to None, GZIP, JPEG and tells the
IO system to compress images in the .j3f file using the prescribed technique. In
the future this will be extended to support all the formats available in
javax.imageio in JDK 1.4.
cloneNode should be overridden by any user subclassed
* objects. All subclasses must have their cloneNode
* method consist of the following lines:
*
* @param forceDuplicate when set to
* public Node cloneNode(boolean forceDuplicate) {
* UserSubClass usc = new UserSubClass();
* usc.duplicateNode(this, forceDuplicate);
* return usc;
* }
* true, causes the
* duplicateOnCloneTree flag to be ignored. When
* false, the value of each node's
* duplicateOnCloneTree variable determines whether
* NodeComponent data is duplicated or copied.
*
* @see Node#cloneTree
* @see Node#duplicateNode
* @see NodeComponent#setDuplicateOnCloneTree
*/
public Node cloneNode(boolean forceDuplicate) {
Sphere s = new Sphere(radius, flags, divisions, getAppearance());
s.duplicateNode(this, forceDuplicate);
return s;
}
/**
* Copies all node information from originalNode into
* the current node. This method is called from the
* cloneNode method which is, in turn, called by the
* cloneTree method.
* duplicateOnCloneTree value is used to determine
* whether the NodeComponent should be duplicated in the new node
* or if just a reference to the current node should be placed in the
* new node. This flag can be overridden by setting the
* forceDuplicate parameter in the cloneTree
* method to true.
*
* @param originalNode the original node to duplicate.
* @param forceDuplicate when set to true, causes the
* duplicateOnCloneTree flag to be ignored. When
* false, the value of each node's
* duplicateOnCloneTree variable determines whether
* NodeComponent data is duplicated or copied.
*
* @see Node#cloneTree
* @see Node#cloneNode
* @see NodeComponent#setDuplicateOnCloneTree
*/
public void duplicateNode(Node originalNode, boolean forceDuplicate) {
super.duplicateNode(originalNode, forceDuplicate);
}
/**
* Returns the radius of the sphere
*
* @since Java 3D 1.2.1
*/
public float getRadius() {
return radius;
}
/**
* Returns the number of divisions
*
* @since Java 3D 1.2.1
*/
public int getDivisions() {
return divisions;
}
void buildQuadrant(GeomBuffer gbuf, double startDelta, double endDelta,
int sign, int nstep, int n, boolean upperSphere)
{
double ds, dt, theta, delta;
int i, j, index, i2;
double h, r, vx, vz;
Point3f pt;
Vector3f norm;
TexCoord2f texCoord;
double starth;
double t;
boolean leftToRight;
if (upperSphere) {
dt = Math.PI/(2*nstep);
theta = dt;
starth = 1;
leftToRight = (sign > 0);
} else {
dt = -Math.PI/(2*nstep);
theta = Math.PI + dt;
starth = -1;
leftToRight = (sign < 0);
}
for (i = 1; i <= nstep; i++) {
h = Math.cos(theta);
r = Math.sin(theta);
if (sign > 0) {
t = 1 - theta/Math.PI;
} else {
t = theta/Math.PI;
}
i2 = i << 1;
// subdivision decreases towards the pole
ds = (endDelta - startDelta) / i;
gbuf.begin(GeomBuffer.TRIANGLE_STRIP);
if (leftToRight) {
// Build triangle strips from left to right
delta = startDelta;
for (j=0; j < i; j++) {
vx = r*Math.cos(delta);
vz = r*Math.sin(delta);
gbuf.normal3d( vx*sign, h*sign, vz*sign );
gbuf.texCoord2d(0.75 - delta/(2*Math.PI), t);
gbuf.vertex3d( vx*radius, h*radius, vz*radius );
if (i > 1) {
// get previous vertex from buffer
index = gbuf.currVertCnt - i2;
pt = gbuf.pts[index];
norm = gbuf.normals[index];
texCoord = gbuf.tcoords[index];
// connect with correspondent vertices from previous row
gbuf.normal3d(norm.x, norm.y, norm.z);
gbuf.texCoord2d(texCoord.x, texCoord.y);
gbuf.vertex3d(pt.x, pt.y, pt.z);
} else {
gbuf.normal3d(0, sign*starth, 0);
if (sign > 0) {
gbuf.texCoord2d(0.75 - (startDelta + endDelta)/(4*Math.PI),
1.0 - (theta - dt)/Math.PI);
} else {
gbuf.texCoord2d(0.75 - (startDelta + endDelta)/(4*Math.PI),
(theta - dt)/Math.PI);
}
gbuf.vertex3d( 0, starth*radius, 0);
}
delta += ds;
}
// Put the last vertex in that row,
// for numerical accuracy we don't use delta
// compute from above.
delta = endDelta;
vx = r*Math.cos(delta);
vz = r*Math.sin(delta);
gbuf.normal3d( vx*sign, h*sign, vz*sign );
gbuf.texCoord2d(0.75 - delta/(2*Math.PI), t);
gbuf.vertex3d( vx*radius, h*radius, vz*radius);
} else {
delta = endDelta;
// Build triangle strips from right to left
for (j=i; j > 0; j--) {
vx = r*Math.cos(delta);
vz = r*Math.sin(delta);
gbuf.normal3d( vx*sign, h*sign, vz*sign );
// Convert texture coordinate back to one
// set in previous version
gbuf.texCoord2d(0.75 - delta/(2*Math.PI), t);
gbuf.vertex3d( vx*radius, h*radius, vz*radius );
if (i > 1) {
// get previous vertex from buffer
index = gbuf.currVertCnt - i2;
pt = gbuf.pts[index];
norm = gbuf.normals[index];
texCoord = gbuf.tcoords[index];
gbuf.normal3d(norm.x, norm.y, norm.z);
gbuf.texCoord2d(texCoord.x, texCoord.y);
gbuf.vertex3d(pt.x, pt.y, pt.z);
} else {
gbuf.normal3d(0, sign*starth, 0);
if (sign > 0) {
gbuf.texCoord2d(0.75 - (startDelta + endDelta)/(4*Math.PI),
1.0 - (theta - dt)/Math.PI);
} else {
gbuf.texCoord2d(0.75 - (startDelta + endDelta)/(4*Math.PI),
(theta - dt)/Math.PI);
}
gbuf.vertex3d( 0, starth*radius, 0);
}
delta -= ds;
}
// Put the last vertex in that row,
// for numerical accuracy we don't use delta
// compute from above.
delta = startDelta;
vx = r*Math.cos(delta);
vz = r*Math.sin(delta);
gbuf.normal3d( vx*sign, h*sign, vz*sign );
gbuf.texCoord2d(0.75 - delta/(2*Math.PI), t);
gbuf.vertex3d( vx*radius, h*radius, vz*radius );
}
gbuf.end();
if (i < nstep) {
theta += dt;
} else { // take care of numerical imprecision
theta = Math.PI/2;
}
}
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Box.java 0000644 0000000 0000000 00000035005 10613450623 027032 0 ustar root root /*
* $RCSfile: Box.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.6 $
* $Date: 2007/04/24 18:50:59 $
* $State: Exp $
*/
package com.sun.j3d.utils.geometry;
import com.sun.j3d.utils.geometry.*;
import javax.media.j3d.*;
import javax.vecmath.*;
/**
* Box is a geometry primitive created with a given length, width, and height.
* It is centered at the origin. By default, it lies within the bounding
* box, [-1,-1,-1] and [1,1,1].
*
* When a texture is applied to a box, it is map CCW like on a Cylinder.
* A texture is mapped CCW from the back of the
* body. The top and bottom faces are mapped such that the texture appears
* front facing when the faces are rotated 90 toward the viewer.
* cloneTree to duplicate the current node.
* cloneNode should be overridden by any user subclassed
* objects. All subclasses must have their cloneNode
* method consist of the following lines:
*
* @param forceDuplicate when set to
* public Node cloneNode(boolean forceDuplicate) {
* UserSubClass usc = new UserSubClass();
* usc.duplicateNode(this, forceDuplicate);
* return usc;
* }
* true, causes the
* duplicateOnCloneTree flag to be ignored. When
* false, the value of each node's
* duplicateOnCloneTree variable determines whether
* NodeComponent data is duplicated or copied.
*
* @see Node#cloneTree
* @see Node#duplicateNode
* @see NodeComponent#setDuplicateOnCloneTree
*/
public Node cloneNode(boolean forceDuplicate) {
Box b = new Box(xDim, yDim, zDim, flags, getAppearance());
b.duplicateNode(this, forceDuplicate);
return b;
}
/**
* Copies all node information from originalNode into
* the current node. This method is called from the
* cloneNode method which is, in turn, called by the
* cloneTree method.
* duplicateOnCloneTree value is used to determine
* whether the NodeComponent should be duplicated in the new node
* or if just a reference to the current node should be placed in the
* new node. This flag can be overridden by setting the
* forceDuplicate parameter in the cloneTree
* method to true.
*
* @param originalNode the original node to duplicate.
* @param forceDuplicate when set to true, causes the
* duplicateOnCloneTree flag to be ignored. When
* false, the value of each node's
* duplicateOnCloneTree variable determines whether
* NodeComponent data is duplicated or copied.
*
* @see Node#cloneTree
* @see Node#cloneNode
* @see NodeComponent#setDuplicateOnCloneTree
*/
public void duplicateNode(Node originalNode, boolean forceDuplicate) {
super.duplicateNode(originalNode, forceDuplicate);
}
/**
* Returns the X-dimension size of the Box
*
* @since Java 3D 1.2.1
*/
public float getXdimension() {
return xDim;
}
/**
* Returns the Y-dimension size of the Box
*
* @since Java 3D 1.2.1
*/
public float getYdimension() {
return yDim;
}
/**
* Returns the Z-dimension size of the Box
*
* @since Java 3D 1.2.1
*/
public float getZdimension() {
return zDim;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Project.java 0000644 0000000 0000000 00000017743 10563126524 027725 0 ustar root root /*
* $RCSfile: Project.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:20 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import javax.vecmath.*;
import java.io.*;
import java.util.*;
class Project {
/**
* This function projects the vertices of the polygons referenced by
* loops[i1,..,i2-1] to an approximating plane.
*/
static void projectFace(Triangulator triRef, int loopMin, int loopMax) {
Vector3f normal, nr;
int i, j;
double d;
normal = new Vector3f();
nr = new Vector3f();
// determine the normal of the plane onto which the points get projected
determineNormal(triRef, triRef.loops[loopMin], normal);
j = loopMin + 1;
if (j < loopMax) {
for (i = j; i < loopMax; ++i) {
determineNormal(triRef, triRef.loops[i], nr);
if (Basic.dotProduct(normal, nr) < 0.0) {
Basic.invertVector(nr);
}
Basic.vectorAdd(normal, nr, normal);
}
d = Basic.lengthL2(normal);
if (Numerics.gt(d, Triangulator.ZERO)) {
Basic.divScalar(d, normal);
}
else {
// System.out.println("*** ProjectFace: zero-length normal vector!? ***\n");
normal.x = normal.y = 0.0f;
normal.z = 1.0f;
}
}
// project the points onto this plane. the projected points are stored in
// the array `points[0,..,numPoints]'
// System.out.println("loopMin " + loopMin + " loopMax " + loopMax);
projectPoints(triRef, loopMin, loopMax, normal);
}
/**
* This function computes the average of all normals defined by triples of
* successive vertices of the polygon. we'll see whether this is a good
* heuristic for finding a suitable plane normal...
*/
static void determineNormal(Triangulator triRef, int ind, Vector3f normal) {
Vector3f nr, pq, pr;
int ind0, ind1, ind2;
int i0, i1, i2;
double d;
ind1 = ind;
i1 = triRef.fetchData(ind1);
ind0 = triRef.fetchPrevData(ind1);
i0 = triRef.fetchData(ind0);
ind2 = triRef.fetchNextData(ind1);
i2 = triRef.fetchData(ind2);
pq = new Vector3f();
Basic.vectorSub((Tuple3f) triRef.vertices[i0], (Tuple3f) triRef.vertices[i1], (Vector3f) pq);
pr = new Vector3f();
Basic.vectorSub((Tuple3f) triRef.vertices[i2], (Tuple3f) triRef.vertices[i1], (Vector3f) pr);
nr = new Vector3f();
Basic.vectorProduct(pq, pr, nr);
d = Basic.lengthL2(nr);
if (Numerics.gt(d, Triangulator.ZERO)) {
Basic.divScalar(d, nr);
normal.set(nr);
}
else {
normal.x = normal.y = normal.z = 0.0f;
}
pq.set(pr);
ind1 = ind2;
ind2 = triRef.fetchNextData(ind1);
i2 = triRef.fetchData(ind2);
while (ind1 != ind) {
Basic.vectorSub((Tuple3f) triRef.vertices[i2], (Tuple3f) triRef.vertices[i1], pr);
Basic.vectorProduct(pq, pr, nr);
d = Basic.lengthL2(nr);
if (Numerics.gt(d, Triangulator.ZERO)) {
Basic.divScalar(d, nr);
if (Basic.dotProduct(normal, nr) < 0.0) {
Basic.invertVector(nr);
}
Basic.vectorAdd(normal, nr, normal);
}
pq.set(pr);
ind1 = ind2;
ind2 = triRef.fetchNextData(ind1);
i2 = triRef.fetchData(ind2);
}
d = Basic.lengthL2(normal);
if (Numerics.gt(d, Triangulator.ZERO)) {
Basic.divScalar(d, normal);
}
else {
//System.out.println("*** DetermineNormal: zero-length normal vector!? ***\n");
normal.x = normal.y = 0.0f; normal.z = 1.0f;
}
}
/**
* This function maps the vertices of the polygon referenced by `ind' to the
* plane n3.x * x + n3.y * y + n3.z * z = 0. every mapped vertex (x,y,z)
* is then expressed in terms of (x',y',z'), where z'=0. this is
* achieved by transforming the original vertices into a coordinate system
* whose z-axis coincides with n3, and whose two other coordinate axes n1
* and n2 are orthonormal on n3. note that n3 is supposed to be of unit
* length!
*/
static void projectPoints(Triangulator triRef, int i1, int i2, Vector3f n3) {
Matrix4f matrix = new Matrix4f();
Point3f vtx = new Point3f();
Vector3f n1, n2;
double d;
int ind, ind1;
int i, j1;
n1 = new Vector3f();
n2 = new Vector3f();
// choose n1 and n2 appropriately
if ((Math.abs(n3.x) > 0.1) || (Math.abs(n3.y) > 0.1)) {
n1.x = -n3.y;
n1.y = n3.x;
n1.z = 0.0f;
}
else {
n1.x = n3.z;
n1.z = -n3.x;
n1.y = 0.0f;
}
d = Basic.lengthL2(n1);
Basic.divScalar(d, n1);
Basic.vectorProduct(n1, n3, n2);
d = Basic.lengthL2(n2);
Basic.divScalar(d, n2);
// initialize the transformation matrix
matrix.m00 = n1.x;
matrix.m01 = n1.y;
matrix.m02 = n1.z;
matrix.m03 = 0.0f; // translation of the coordinate system
matrix.m10 = n2.x;
matrix.m11 = n2.y;
matrix.m12 = n2.z;
matrix.m13 = 0.0f; // translation of the coordinate system
matrix.m20 = n3.x;
matrix.m21 = n3.y;
matrix.m22 = n3.z;
matrix.m23 = 0.0f; // translation of the coordinate system
matrix.m30 = 0.0f;
matrix.m31 = 0.0f;
matrix.m32 = 0.0f;
matrix.m33 = 1.0f;
// transform the vertices and store the transformed vertices in the array
// `points'
triRef.initPnts(20);
for (i = i1; i < i2; ++i) {
ind = triRef.loops[i];
ind1 = ind;
j1 = triRef.fetchData(ind1);
matrix.transform((Point3f)triRef.vertices[j1], vtx);
j1 = triRef.storePoint(vtx.x, vtx.y);
triRef.updateIndex(ind1, j1);
ind1 = triRef.fetchNextData(ind1);
j1 = triRef.fetchData(ind1);
while (ind1 != ind) {
matrix.transform(triRef.vertices[j1], vtx);
j1 = triRef.storePoint(vtx.x, vtx.y);
triRef.updateIndex(ind1, j1);
ind1 = triRef.fetchNextData(ind1);
j1 = triRef.fetchData(ind1);
}
}
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/NoHash.java 0000644 0000000 0000000 00000020772 10563126523 027472 0 ustar root root /*
* $RCSfile: NoHash.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:19 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import java.io.*;
import java.util.*;
import javax.vecmath.*;
class NoHash {
static final int NIL = -1;
static void insertAfterVtx(Triangulator triRef, int iVtx) {
int size;
if (triRef.vtxList == null) {
size = Math.max(triRef.numVtxList+1, 100);
triRef.vtxList = new PntNode[size];
} else if (triRef.numVtxList >= triRef.vtxList.length) {
size = Math.max(triRef.numVtxList+1,
triRef.vtxList.length + 100);
PntNode old[] = triRef.vtxList;
triRef.vtxList = new PntNode[size];
System.arraycopy(old, 0, triRef.vtxList, 0, old.length);
}
triRef.vtxList[triRef.numVtxList] = new PntNode();
triRef.vtxList[triRef.numVtxList].pnt = iVtx;
triRef.vtxList[triRef.numVtxList].next = triRef.reflexVertices;
triRef.reflexVertices = triRef.numVtxList;
++triRef.numVtxList;
++triRef.numReflex;
}
static void deleteFromList(Triangulator triRef, int i) {
int indPnt, indPnt1;
int indVtx;
if(triRef.numReflex == 0) {
// System.out.println("NoHash:deleteFromList. numReflex is 0.");
return;
}
indPnt = triRef.reflexVertices;
if(inVtxList(triRef, indPnt)==false)
System.out.println("NoHash:deleteFromList. Problem :Not is InVtxList ..." +
indPnt);
indVtx = triRef.vtxList[indPnt].pnt;
if (indVtx == i) {
triRef.reflexVertices = triRef.vtxList[indPnt].next;
--triRef.numReflex;
}
else {
indPnt1 = triRef.vtxList[indPnt].next;
while (indPnt1 != NIL) {
if(inVtxList(triRef, indPnt1)==false)
System.out.println("NoHash:deleteFromList. Problem :Not is InVtxList ..."+
indPnt1);
indVtx = triRef.vtxList[indPnt1].pnt;
if (indVtx == i) {
triRef.vtxList[indPnt].next = triRef.vtxList[indPnt1].next;
indPnt1 = NIL;
--triRef.numReflex;
}
else {
indPnt = indPnt1;
indPnt1 = triRef.vtxList[indPnt].next;
}
}
}
}
static boolean inVtxList(Triangulator triRef, int vtx) {
return ((0 <= vtx) && (vtx < triRef.numVtxList));
}
static void freeNoHash(Triangulator triRef) {
triRef.noHashingEdges = false;
triRef.noHashingPnts = false;
triRef.numVtxList = 0;
}
static void prepareNoHashEdges(Triangulator triRef,
int currLoopMin, int currLoopMax) {
triRef.loopMin = currLoopMin;
triRef.loopMax = currLoopMax;
triRef.noHashingEdges = true;
return;
}
static void prepareNoHashPnts(Triangulator triRef, int currLoopMin) {
int ind, ind1;
int i1;
triRef.numVtxList = 0;
triRef.reflexVertices = NIL;
// insert the reflex vertices into a list
ind = triRef.loops[currLoopMin];
ind1 = ind;
triRef.numReflex = 0;
i1 = triRef.fetchData(ind1);
do {
if (triRef.getAngle(ind1) < 0)
insertAfterVtx(triRef, ind1);
ind1 = triRef.fetchNextData(ind1);
i1 = triRef.fetchData(ind1);
} while (ind1 != ind);
triRef.noHashingPnts = true;
}
static boolean noHashIntersectionExists(Triangulator triRef, int i1, int ind1,
int i2, int i3, BBox bb) {
int indVtx, ind5;
int indPnt;
int i4, i5;
int type[] = new int[1];
boolean flag;
double y;
if(triRef.noHashingPnts==false)
System.out.println("NoHash:noHashIntersectionExists noHashingPnts is false");
// assert(InPointsList(i1));
// assert(InPointsList(i2));
// assert(InPointsList(i3));
if (triRef.numReflex <= 0) return false;
// first, let's extend the BBox of the line segment i2, i3 to a BBox
// of the entire triangle.
if (i1 < bb.imin) bb.imin = i1;
else if (i1 > bb.imax) bb.imax = i1;
y = triRef.points[i1].y;
if (y < bb.ymin) bb.ymin = y;
else if (y > bb.ymax) bb.ymax = y;
// check whether the triangle i1, i2, i3 contains any reflex vertex; we
// assume that i2, i3 is the new diagonal, and that the triangle is
// oriented CCW.
indPnt = triRef.reflexVertices;
flag = false;
do {
// assert(InVtxList(ind_pnt));
indVtx = triRef.vtxList[indPnt].pnt;
// assert(InPolyList(ind_vtx));
i4 = triRef.fetchData(indVtx);
if (bb.pntInBBox(triRef, i4)) {
// only if the reflex vertex lies inside the BBox of the triangle.
ind5 = triRef.fetchNextData(indVtx);
i5 = triRef.fetchData(ind5);
if ((indVtx != ind1) && (indVtx != ind5)) {
// only if this node isn't i1, and if it still belongs to the
// polygon
if (i4 == i1) {
if (Degenerate.handleDegeneracies(triRef, i1, ind1, i2, i3, i4, indVtx))
return true;
}
else if ((i4 != i2) && (i4 != i3)) {
flag = Numerics.vtxInTriangle(triRef, i1, i2, i3, i4, type);
if (flag) return true;
}
}
}
indPnt = triRef.vtxList[indPnt].next;
} while (indPnt != NIL);
return false;
}
static void deleteReflexVertex(Triangulator triRef, int ind) {
// assert(InPolyList(ind));
deleteFromList(triRef, ind);
}
static boolean noHashEdgeIntersectionExists(Triangulator triRef, BBox bb, int i1,
int i2, int ind5, int i5) {
int ind, ind2;
int i, i3, i4;
BBox bb1;
if(triRef.noHashingEdges==false)
System.out.println("NoHash:noHashEdgeIntersectionExists noHashingEdges is false");
triRef.identCntr = 0;
// check the boundary segments.
for (i = triRef.loopMin; i < triRef.loopMax; ++i) {
ind = triRef.loops[i];
ind2 = ind;
i3 = triRef.fetchData(ind2);
do {
ind2 = triRef.fetchNextData(ind2);
i4 = triRef.fetchData(ind2);
// check this segment. we first compute its bounding box.
bb1 = new BBox(triRef, i3, i4);
if (bb.BBoxOverlap(bb1)) {
if (Numerics.segIntersect(triRef, bb.imin, bb.imax, bb1.imin, bb1.imax, i5))
return true;
}
i3 = i4;
} while (ind2 != ind);
}
// oops! this segment shares one endpoint with at least four other
// boundary segments! oh well, yet another degenerate situation...
if (triRef.identCntr >= 4) {
if (BottleNeck.checkBottleNeck(triRef, i5, i1, i2, ind5))
return true;
else
return false;
}
return false;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/GeomBuffer.java 0000644 0000000 0000000 00000037541 10613450623 030332 0 ustar root root /*
* $RCSfile: GeomBuffer.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.6 $
* $Date: 2007/04/24 18:50:59 $
* $State: Exp $
*/
package com.sun.j3d.utils.geometry;
import com.sun.j3d.utils.geometry.*;
import java.io.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.math.*;
/**
* GeomBuffer allows OpenGL-like input of geometry data. It outputs
* Java 3D geometry array objects. This utility is to simplify porting
* of OpenGL programs to Java 3D.
*
* Notice, that you only need to specify some upperbound on the number of
* points you'll use at the beginning (100 in this case).
*
*
* GeomBuffer gbuf = new GeomBuffer(100);
* gbuf.begin(GeomBuffer.QUADS);
*
* for (int i = 0; i < 5; i++){
* gbuf.normal3d(0.0, 1.0, 0.0);
* gbuf.vertex3d(1.0, 1.0, 0.0);
*
* gbuf.normal3d(0.0, 1.0, 0.0);
* gbuf.vertex3d(0.0, 1.0, 0.0);
*
* gbuf.normal3d(0.0, 1.0, 0.0);
* gbuf.vertex3d(0.0, 0.0, 0.0);
*
* gbuf.normal3d(0.0, 1.0, 0.0);
* gbuf.vertex3d(1.0, 0.0, 0.0);
* }
* gbuf.end();
* Shape3D shape = new Shape3D(gbuf.getGeom(GeomBuffer.GENERATE_NORMALS));
*
* GeometryInfo gi = new GeometryInfo(TRIANGLE_ARRAY);
* gi.setCoordinates(coordinateData);
*
* NormalGenerator ng = new NormalGenerator();
* ng.generateNormals(gi);
*
* Stripifier st = new Stripifier()
* st.stripify(gi);
*
* Shape3D part = new Shape3D();
* part.setAppearance(appearance);
* part.setGeometry(gi.getGeometryArray());
*
*/
public class Stripifier {
final boolean DEBUG = false;
final boolean CHECK_ORIENT = false;
static final int EMPTY = -1;
boolean hasNormals = false;
boolean hasTextures = false;
int texSetCount = 0;
boolean hasColors = false;
boolean colorStrips = false;
StripifierStats stats;
int[] numNhbrs;
/**
* Indicates to the stripifier to collect statistics on the data
*/
public static final int COLLECT_STATS = 0x01;
/**
* Creates the Stripifier object.
*/
public Stripifier() {
}
/**
* Creates the Stripifier object.
* @param flags Flags
* @since Java 3D 1.2.1
*/
public Stripifier(int flags) {
if ((flags & COLLECT_STATS) != 0) {
stats = new StripifierStats();
}
}
/**
* Converts the geometry contained in the GeometryInfo object into an
* array of triangle strips.
*/
public void stripify(GeometryInfo gi) {
// System.out.println("stripify");
long time = System.currentTimeMillis();
// setup
gi.convertToIndexedTriangles();
gi.forgetOldPrim();
// write out the gi object
// System.out.println("write out the object");
// gi.writeObj();
Face[] faces = createFaceArray(gi);
Edge[] edges = createEdgeArray(faces);
buildAdjacencies(edges, faces);
// print out the adjacency information
if (DEBUG) {
for (int i = 0; i < faces.length; i++) {
faces[i].printVertices();
}
System.out.println("");
for (int i = 0; i < faces.length; i++) {
faces[i].printAdjacency();
}
System.out.println("");
}
Node[] faceNodes = new Node[faces.length];
// Node[] queue = hybridSearch(faces, faceNodes);
Node[] queue = dfSearch(faces, faceNodes);
// print out the queue
if (DEBUG) {
for (int i = 0; i < queue.length; i++) {
queue[i].print();
}
System.out.println("");
}
// int "pointers" for the numbers of strips and patches from
// hamiliton
int[] ns = new int[1];
int[] np = new int[1];
ArrayList hamiltons = hamilton(queue, ns, np);
int numStrips = ns[0];
int numPatches = np[0];
// print out the hamiltonians
if (DEBUG) {
for (int i = 0; i < hamiltons.size(); i++) {
System.out.println("Hamiltonian: " + i);
ArrayList list = (ArrayList)hamiltons.get(i);
for (int j = 0; j < list.size(); j++) {
Face face = (Face)list.get(j);
face.printVertices();
}
System.out.println("");
}
}
// now make strips out of the hamiltonians
ArrayList strips = stripe(hamiltons);
// print out the strips
if (DEBUG) {
for (int i = 0; i < strips.size(); i++) {
System.out.println("Strip: " + i);
Istream istream = (Istream)strips.get(i);
for (int j = 0; j < istream.length; j++) {
System.out.println("vertex: " + istream.istream[j].index);
}
System.out.println("");
}
}
// concatenate the strips
concatenate(strips, faces);
// print out the new strips
if (DEBUG) {
System.out.println("");
System.out.println("concatenated strips: (" +
(strips.size()) + ")");
System.out.println("");
for (int i = 0; i < strips.size(); i++) {
System.out.println("Strip: " + i);
Istream istream = (Istream)strips.get(i);
for (int j = 0; j < istream.length; j++) {
System.out.println("vertex: " + istream.istream[j].index);
}
System.out.println("");
}
}
// put the stripified data into the GeometryInfo object
putBackData(gi, strips);
// System.out.println("time: " + (System.currentTimeMillis()-time));
// System.out.println("");
// add to stats
if (stats != null) {
stats.updateInfo(System.currentTimeMillis()-time, strips,
faces.length);
}
// Stat.printInfo();
// print out strip count info
// System.out.println("numStrips = " + strips.size());
// System.out.println("stripCounts:");
// int avg = 0;
// for (int i = 0; i < strips.size(); i++) {
// System.out.print(((Istream)strips.get(i)).length + " ");
// avg += ((Istream)strips.get(i)).length;
// }
// System.out.println("Avg: " + ((double)avg/(double)strips.size()));
}
/**
* Prints out statistical information for the stripifier: the number of
* original triangles, the number of original vertices, the number of
* strips created, the number of vertices, the total number of triangles,
* the minimum strip length (in # of tris) the maximum strip length
* (in number of tris), the average strip length (in # of tris), the
* average number of vertices per triangle, the total time it took to
* stripify, and the strip length (how many strips of a given length.
* The data is cumulative over all the times the stripifier is called
* until the stats are printed, and then they are reset.
*/
// public static void printStats() {
// // stats.toString();
// }
/**
* Returns the stripifier stats object.
* @exception IllegalStateException if the Stripfier has not
* been constructed
* with the COLLECT_STATS flag
* @since Java 3D 1.2.1
*/
public StripifierStats getStripifierStats() {
if (stats == null) {
throw new IllegalStateException(J3dUtilsI18N.getString("Stripifier0"));
}
return stats;
}
/**
* Creates an array of faces from the geometry in the GeometryInfo object.
*/
Face[] createFaceArray(GeometryInfo gi) {
int[] vertices = gi.getCoordinateIndices();
int[] normals = gi.getNormalIndices();
int[][] textures = null;
int[] t1 = null;
int[] t2 = null;
int[] t3 = null;
texSetCount = gi.getTexCoordSetCount();
if (texSetCount > 0) {
hasTextures = true;
textures = new int[texSetCount][];
for (int i = 0; i < texSetCount; i++) {
textures[i] = gi.getTextureCoordinateIndices(i);
}
t1 = new int[texSetCount];
t2 = new int[texSetCount];
t3 = new int[texSetCount];
} else hasTextures = false;
int[] colors = gi.getColorIndices();
Face[] faces = new Face[vertices.length/3];
int n1, n2, n3, c1, c2, c3;
Vertex v1, v2, v3;
int count = 0;
for (int i = 0; i < vertices.length;) {
if (normals != null) {
// System.out.println("hasNormals");
hasNormals = true;
n1 = normals[i];
n2 = normals[i+1];
n3 = normals[i+2];
}
else {
// System.out.println("doesn't have normals");
hasNormals = false;
n1 = EMPTY;
n2 = EMPTY;
n3 = EMPTY;
}
if (hasTextures) {
for (int j = 0; j < texSetCount; j++) {
t1[j] = textures[j][i];
t2[j] = textures[j][(i+1)];
t3[j] = textures[j][(i+2)];
}
}
if (colors != null) {
hasColors = true;
c1 = colors[i];
c2 = colors[i+1];
c3 = colors[i+2];
}
else {
hasColors = false;
c1 = EMPTY;
c2 = EMPTY;
c3 = EMPTY;
}
v1 = new Vertex(vertices[i], n1, texSetCount, t1, c1);
v2 = new Vertex(vertices[i+1], n2, texSetCount, t2, c2);
v3 = new Vertex(vertices[i+2], n3, texSetCount, t3, c3);
if (!v1.equals(v2) && !v2.equals(v3) && !v3.equals(v1)) {
faces[count] = new Face(count, v1, v2, v3);
count++;
}
i+=3;
}
if (faces.length > count) {
Face[] temp = faces;
faces = new Face[count];
System.arraycopy(temp, 0, faces, 0, count);
}
return faces;
}
/**
* Creates an array of edges from the Face array.
*/
Edge[] createEdgeArray(Face[] faces) {
Edge[] edges = new Edge[faces.length*3];
Face face;
for (int i = 0; i < faces.length; i++) {
face = faces[i];
edges[i*3] = new Edge(face.verts[0], face.verts[1], face.key);
edges[i*3+1] = new Edge(face.verts[1], face.verts[2], face.key);
edges[i*3+2] = new Edge(face.verts[2], face.verts[0], face.key);
}
return edges;
}
/**
* Builds the adjacency graph by finding the neighbors of the edges
*/
void buildAdjacencies(Edge[] edges, Face[] faces) {
// sortEdges(edges);
quickSortEdges(edges, 0, edges.length-1);
// int i = 1;
// set up the edge list of each face
Edge edge;
Face face;
Vertex[] verts;
boolean flag;
int k;
for (int i = 0; i < edges.length; i++) {
// edges are kept in order s.t. the ith edge is the opposite
// edge of the ith vertex
edge = edges[i];
face = faces[edge.face];
verts = face.verts;
flag = true;
if ((!verts[0].equals(edge.v1)) && (!verts[0].equals(edge.v2))) {
face.edges[0] = edge;
face.numNhbrs--;
flag = false;
}
else if ((!verts[1].equals(edge.v1)) &&
(!verts[1].equals(edge.v2))) {
face.edges[1] = edge;
face.numNhbrs--;
flag = false;
}
else if ((!verts[2].equals(edge.v1)) &&
(!verts[2].equals(edge.v2))) {
face.edges[2] = edge;
face.numNhbrs--;
flag = false;
}
else {
if (DEBUG) System.out.println("error!!! Stripifier.buildAdj");
}
// handle degenerencies
if (flag) {
Vertex i1;
// triangle degenerated to a point
if ((edge.v1).equals(edge.v2)) {
face.edges[--face.numNhbrs] = edge;
}
// triangle degenerated to an edge
else {
if (verts[0].equals(verts[1])) {
i1 = verts[1];
}
else {
i1 = verts[2];
}
if (verts[0].equals(i1) && face.edges[0] == null) {
face.edges[0] = edge;
face.numNhbrs--;
}
else if (verts[1].equals(i1) && face.edges[1] == null) {
face.edges[1] = edge;
face.numNhbrs--;
}
else {
face.edges[2] = edge;
face.numNhbrs--;
}
}
}
}
// build the adjacency information by pairing up every two triangles
// that share the same edge
int i = 0; int j = 0;
int j1, j2;
while (i < (edges.length-1)) {
j = i+1;
if (edges[i].equals(edges[j])) {
// determine the orientations of the common edge in the two
// adjacent triangles. Only set them to be adjacent if they
// are opposite
j1 = edges[i].face;
j2 = edges[j].face;
if (j1 != j2) { // set up the two faces as neighbors
edge = edges[i];
face = faces[j1];
k = face.getEdgeIndex(edge);
if ((edge.v1.equals(face.verts[(k+1)%3])) &&
(edge.v2.equals(face.verts[(k+2)%3]))) {
flag = false;
}
else flag = true;
edge = edges[j];
face = faces[j2];
k = face.getEdgeIndex(edge);
if ((edge.v1.equals(face.verts[(k+1)%3])) &&
(edge.v2.equals(face.verts[(k+2)%3]))) {
flag = flag;
}
else flag = (!flag);
if (flag) {
edges[i].face = j2;
edges[j].face = j1;
(faces[j1].numNhbrs)++;
(faces[j2].numNhbrs)++;
j++;
}
else edges[i].face = EMPTY;
}
else edges[i].face = EMPTY;
}
else edges[i].face = EMPTY;
i=j;
}
if (i <= (edges.length-1)) edges[i].face = EMPTY;
// check, for each face, if it is duplicated. For a face that
// neighbors its duplicate in the adjacency graph, it's possible
// that two or more of its neighbors are the same (the duplicate).
// This will be corrected to avoid introducing redundant faces
// later on
for (i = 0; i < faces.length; i++) {
face = faces[i];
if (face.numNhbrs == 3) {
if ((j1 = face.edges[1].face) == face.edges[0].face) {
face.edges[1].face = EMPTY;
face.numNhbrs--;
faces[j1].counterEdgeDel(face.edges[1]);
}
if ((j2 = face.edges[2].face) == face.edges[0].face) {
face.edges[2].face = EMPTY;
face.numNhbrs--;
faces[j2].counterEdgeDel(face.edges[2]);
}
if ((face.edges[1].face != EMPTY) && (j1 == j2)) {
face.edges[2].face = EMPTY;
face.numNhbrs--;
faces[j1].counterEdgeDel(face.edges[2]);
}
}
}
}
/**
* Sorts the edges using BubbleSort
*/
void sortEdges(Edge[] edges) {
int i = edges.length;
boolean sorted = false;
Edge temp = null;
while ((i > 1) && !sorted) {
sorted = true;
for (int j = 1; j < i; j++) {
if (edges[j].lessThan(edges[j-1])) {
temp = edges[j-1];
edges[j-1] = edges[j];
edges[j] = temp;
sorted = false;
}
}
i--;
}
}
/**
* uses quicksort to sort the edges
*/
void quickSortEdges(Edge[] edges, int l, int r) {
if (edges.length > 0) {
int i = l;
int j = r;
Edge k = edges[(l+r) / 2];
do {
while (edges[i].lessThan(k)) i++;
while (k.lessThan(edges[j])) j--;
if (i <= j) {
Edge tmp = edges[i];
edges[i] = edges[j];
edges[j] = tmp;
i++;
j--;
}
} while (i <= j);
if (l < j) quickSortEdges(edges, l, j);
if (l < r) quickSortEdges(edges, i, r);
}
}
/**
* Takes a list of faces as input and performs a hybrid search, a
* variated depth first search that returns to the highest level node
* not yet fully explored. Returns an array of pointers to the faces
* found in order from the search. The faceNodes parameter is an
* array of the Nodes created for the faces.
*/
Node[] hybridSearch(Face[] faces, Node[] faceNodes) {
int numFaces = faces.length;
int i = 0, j = 0, k = 0, ind = 0;
// keep # of faces with certain # of neighbors
int[] count = {0, 0, 0, 0};
// faces sorted by number of neighbors
int[] index = new int[numFaces];
// the index of a certain face in the sorted array
int[] rindex = new int[numFaces];
// Control list pop up operation
boolean popFlag = false;
// queue of pointers to faces found in search
Node[] queue = new Node[numFaces];
// root of depth first tree
Node source;
// for the next node
Node nnode;
// a face
Face face;
// starting position for insertion into the list
int start = 0;
// list for search
SortedList dlist;
// count how many faces have a certain # of neighbors and
// create a Node for each face
for (i = 0; i < numFaces; i++) {
j = faces[i].numNhbrs;
count[j]++;
faceNodes[i] = new Node(faces[i]);
}
// to help with sorting
for (i = 1; i < 4; i++) {
count[i] += count[i-1];
}
// decreasing i to make sorting stable
for (i = numFaces - 1; i >= 0; i--) {
j = faces[i].numNhbrs;
count[j]--;
index[count[j]] = i;
rindex[i] = count[j];
}
// start the hybrid search
for (i = 0; i < numFaces; i++) {
if (index[i] != EMPTY) {
dlist = new SortedList();
source = faceNodes[index[i]];
source.setRoot();
queue[ind] = source;
ind++;
index[i] = EMPTY;
while (source != null) {
nnode = null;
// use the first eligible for continuing search
face = source.face;
for (j = 0; j < 3; j++) {
k = face.getNeighbor(j);
if ((k != EMPTY) &&
(faceNodes[k].notAccessed())) {
nnode = faceNodes[k];
break;
}
}
if (nnode != null) {
// insert the new node
nnode.insert(source);
if (!popFlag) {
start = dlist.sortedInsert(source, start);
}
else popFlag = false;
source = nnode;
queue[ind] = source;
ind++;
index[rindex[k]] = EMPTY;
}
else {
source.processed();
source = dlist.pop();
popFlag = true;
start = 0;
}
} // while -- does popFlag need to be set to false here?
}
}
return queue;
}
Node[] dfSearch(Face[] faces, Node[] faceNodes) {
int numFaces = faces.length;
int i = 0, j = 0, k = 0, ind = 0;
// keep certain # of faces with certain # of neighbors
int[] count = {0, 0, 0, 0};
// faces sorted by # of neighbors
int[] index = new int[numFaces];
// index of a certain face in the sorted array
int[] rindex = new int[numFaces];
// queue of pointers to faces found in the search
Node[] queue = new Node[numFaces];
// root of the depth first tree
Node source;
// the current node
Node node;
// for the next Node
Node nnode;
// a face
Face face;
// count how many faces have a certain # of neighbors and create
// a Node for each face
for (i = 0; i < numFaces; i++) {
j = faces[i].numNhbrs;
count[j]++;
faceNodes[i] = new Node(faces[i]);
}
// to help with sorting
for (i = 1; i < 4; i++) count[i] += count[i-1];
// dec i to make sorting stable
for (i = numFaces-1; i >= 0; i--) {
j = faces[i].numNhbrs;
count[j]--;
index[count[j]] = i;
rindex[i] = count[j];
}
setNumNhbrs(faces);
// start the dfs
for (i = 0; i < numFaces; i++) {
if (index[i] != EMPTY) {
source = faceNodes[index[i]];
source.setRoot();
queue[ind] = source;
ind++;
index[i] = EMPTY;
node = source;
do {
// if source has been done, stop
if ((node == source) && (node.right != null)) break;
nnode = null;
face = node.face;
// for (j = 0; j < 3; j++) {
// if (((k = face.getNeighbor(j)) != EMPTY) &&
// (faceNodes[k].notAccessed())) {
// nnode = faceNodes[k];
// break;
// }
// }
k = findNext(node, faceNodes, faces);
if (k != EMPTY) nnode = faceNodes[k];
if (nnode != null) updateNumNhbrs(nnode);
if (nnode != null) {
// insert new node
nnode.insert(node);
node = nnode;
queue[ind] = node;
ind++;
index[rindex[k]] = EMPTY;
}
else {
node.processed();
node = node.parent;
}
} while (node != source.parent);
}
}
freeNhbrTable();
return queue;
}
int findNext(Node node, Node[] faceNodes, Face[] faces) {
Face face = node.face;
// this face has no neighbors so return
if (face.numNhbrs == 0) return EMPTY;
int i, j, count;
int[] n = new int[3]; // num neighbors of neighboring face
int[] ind = {-1, -1, -1}; // neighboring faces
// find the number of neighbors for each neighbor
count = 0;
for (i = 0; i < 3; i++) {
if (((j = face.getNeighbor(i)) != EMPTY) &&
(faceNodes[j].notAccessed())) {
ind[count] = j;
n[count] = numNhbrs[j];
count++;
}
}
// this face has no not accessed faces
if (count == 0) return EMPTY;
// this face has only one neighbor
if (count == 1) return ind[0];
if (count == 2) {
// if the number of neighbors are the same, try reseting
if ((n[0] == n[1]) && (n[0] != 0)) {
n[0] = resetNhbr(ind[0], faces, faceNodes);
n[1] = resetNhbr(ind[1], faces, faceNodes);
}
// if one neighbor has fewer neighbors, return that neighbor
if (n[0] < n[1]) return ind[0];
if (n[1] < n[0]) return ind[1];
// neighbors tie. pick the sequential one
Node pnode, ppnode;
Face pface, ppface;
if ((pnode = node.parent) != null) {
pface = pnode.face;
i = pface.findSharedEdge(face.key);
if ((ppnode = pnode.parent) != null) {
ppface = ppnode.face;
if (pface.getNeighbor((i+1)%3) == ppface.key) {
j = pface.verts[(i+2)%3].index;
}
else {
j = pface.verts[(i+1)%3].index;
}
}
else {
j = pface.verts[(i+1)%3].index;
}
i = face.findSharedEdge(ind[0]);
if (face.verts[i].index == j) return ind[0];
else return ind[1];
}
else return ind[0];
}
// three neighbors
else {
if ((n[0] < n[1]) && (n[0] < n[2])) return ind[0];
else if ((n[1] < n[0]) && (n[1] < n[2])) return ind[1];
else if ((n[2] < n[0]) && (n[2] < n[1])) return ind[2];
else if ((n[0] == n[1]) && (n[0] < n[2])) {
if (n[0] != 0) {
n[0] = resetNhbr(ind[0], faces, faceNodes);
n[1] = resetNhbr(ind[1], faces, faceNodes);
}
if (n[0] <= n[1]) return ind[0];
else return ind[1];
}
else if ((n[1] == n[2]) && n[1] < n[0]) {
if (n[1] != 0) {
n[1] = resetNhbr(ind[1], faces, faceNodes);
n[2] = resetNhbr(ind[2], faces, faceNodes);
}
if (n[1] <= n[2]) return ind[1];
else return ind[2];
}
else if ((n[2] == n[0]) && (n[2] < n[1])) {
if (n[0] != 0) {
n[0] = resetNhbr(ind[0], faces, faceNodes);
n[2] = resetNhbr(ind[2], faces, faceNodes);
}
if (n[0] <= n[2]) return ind[0];
else return ind[2];
}
else {
if (n[0] != 0) {
n[0] = resetNhbr(ind[0], faces, faceNodes);
n[1] = resetNhbr(ind[1], faces, faceNodes);
n[2] = resetNhbr(ind[2], faces, faceNodes);
}
if ((n[0] <= n[1]) && (n[0] <= n[2])) return ind[0];
else if (n[1] <= n[2]) return ind[1];
else return ind[2];
}
}
}
void setNumNhbrs(Face[] faces) {
int numFaces = faces.length;
numNhbrs = new int[numFaces];
for (int i = 0; i < numFaces; i++) {
numNhbrs[i] = faces[i].numNhbrs;
}
}
void freeNhbrTable() {
numNhbrs = null;
}
void updateNumNhbrs(Node node) {
Face face = node.face;
int i;
if ((i = face.getNeighbor(0)) != EMPTY) numNhbrs[i]--;
if ((i = face.getNeighbor(1)) != EMPTY) numNhbrs[i]--;
if ((i = face.getNeighbor(2)) != EMPTY) numNhbrs[i]--;
}
int resetNhbr(int y, Face[] faces, Node[] faceNodes) {
int x = EMPTY;
Face nface = faces[y];
int i;
for (int j = 0; j < 3; j++) {
if (((i = nface.getNeighbor(j)) != EMPTY) &&
(faceNodes[i].notAccessed())) {
if ((x == EMPTY) || (x > numNhbrs[i])) x = numNhbrs[i];
}
}
return x;
}
/**
* generates hamiltonian strips from the derived binary spanning tree
* using the path peeling algorithm to peel off any node wiht double
* children in a bottom up fashion. Returns a Vector of strips. Also
* return the number of strips and patches in the numStrips and
* numPatches "pointers"
*/
ArrayList hamilton(Node[] sTree, int[] numStrips, int[] numPatches) {
// the number of nodes in the tree
int numNodes = sTree.length;
// number of strips
int ns = 0;
// number of patches
int np = 0;
// some tree node variables
Node node, pnode, cnode;
// the Vector of strips
ArrayList strips = new ArrayList();
// the current strip
ArrayList currStrip;
// the tree nodes are visited in such a bottom-up fashion that
// any node is visited prior to its parent
for (int i = numNodes - 1; i >= 0; i--) {
cnode = sTree[i];
// if cnode is the root of a tree create a strip
if (cnode.isRoot()) {
// each patch is a single tree
np++;
// create a new strip
currStrip = new ArrayList();
// insert the current node into the list
currStrip.add(0, cnode.face);
// add the left "wing" of the parent node to the strip
node = cnode.left;
while (node != null) {
currStrip.add(0, node.face);
node = node.left;
}
// add the right "wing" of the parent node to the strip
node = cnode.right;
while (node != null) {
currStrip.add(currStrip.size(), node.face);
node = node.left;
}
// increase the number of strips
ns++;
// add the strip to the Vector
strips.add(currStrip);
}
// if the number of children of this node is 2, create a strip
else if (cnode.numChildren == 2) {
// if the root has a single child with double children, it
// could be left over as a singleton. However, the following
// rearrangement reduces the chances
pnode = cnode.parent;
if (pnode.isRoot() && (pnode.numChildren == 1)) {
pnode = cnode.right;
if (pnode.left != null) cnode = pnode;
else cnode = cnode.left;
}
// handle non-root case
// remove the node
cnode.remove();
// create a new strip
currStrip = new ArrayList();
// insert the current node into the list
currStrip.add(0, cnode.face);
// add the left "wing" of cnode to the list
node = cnode.left;
while (node != null) {
currStrip.add(0, node.face);
node = node.left;
}
// add the right "wing" of cnode to the list
node = cnode.right;
while (node != null) {
currStrip.add(currStrip.size(), node.face);
node = node.left;
}
// increase the number of strips
ns++;
// add the strip to the Vector
strips.add(currStrip);
}
}
// put the ns and np in the "pointers to return
numStrips[0] = ns;
numPatches[0] = np;
// return the strips
return strips;
}
/**
* creates the triangle strips
*/
ArrayList stripe(ArrayList strips) {
int numStrips = strips.size(); // the number of strips
int count; // where we are in the hamiltonian
Face face; // the face we are adding to the stream
Face prev; // the previous face added to the stream
boolean done; // whether we are done with the current strip
boolean cont; // whether we should continue the current stream
ArrayList currStrip; // the current hamiltonian
Istream currStream; // the stream we are building
ArrayList istreams = new ArrayList(); // the istreams to return
boolean ccw = true;; // counter-clockwise
int share; // the shared edge
Vertex[] buf = new Vertex[4]; // a vertex array to start the stream
// create streams for each hamiltonian
for (int i = 0; i < numStrips; i++) {
currStrip = (ArrayList)strips.get(i);
count = 0;
done = false;
face = getNextFace(currStrip, count++);
// while we are not done with the current hamiltonian
while (!done) {
cont = true;
// if the current face is the only one left in the current
// hamiltonian
if (stripDone(currStrip, count)) {
// create a new istream with the current face
currStream = new Istream(face.verts, 3, false);
// set the head of the strip to this face
currStream.head = face.key;
done = true;
// since we are done with the strip, set the tail to this
// face
currStream.tail = face.key;
}
else {
prev = face;
face = getNextFace(currStrip, count++);
// put the prev vertices in the correct order
// to add the next tri on
share = prev.findSharedEdge(face.key);
buf[0] = prev.verts[share];
buf[1] = prev.verts[(share+1)%3];
buf[2] = prev.verts[(share+2)%3];
// find the fourth vertex
if (CHECK_ORIENT) {
// check for clockwise orientation
if (checkOrientCWSeq(buf[2], buf[1], face)) {
share = face.findSharedEdge(prev.key);
buf[3] = face.verts[share];
currStream = new Istream(buf, 4, false);
// set the head of this strip to the prev face
currStream.head = prev.key;
// if this was the last tri in the strip, then
// we are done
if (stripDone(currStrip, count)) {
done = true;
// set the tail for the strip to current face
currStream.tail = face.key;
}
}
else {
cont = false;
currStream = new Istream(buf, 3, false);
// set the head to the prev face
currStream.head = prev.key;
// since we are not continuing, set
// the tail to prev also
currStream.tail = prev.key;
}
// orientation starts counter-clockwise for 3rd face
ccw = true;
}
else {
share = face.findSharedEdge(prev.key);
buf[3] = face.verts[share];
currStream = new Istream(buf, 4, false);
// set the head of this strip to the prev face
currStream.head = prev.key;
// if this was the last tri in the strip, then
// we are done
if (stripDone(currStrip, count)) {
done = true;
// set the tail for the strip to current face
currStream.tail = face.key;
}
}
// while continue and the strip isn't finished
// add more faces to the stream
while (cont && !stripDone(currStrip, count)) {
prev = face;
face = getNextFace(currStrip, count++);
share = face.findSharedEdge(prev.key);
// if we can add the face without adding any
// zero area triangles
if (seq(currStream, face, share)) {
if (CHECK_ORIENT) {
// if we can add the next face with the correct
// orientation
if (orientSeq(ccw, currStream, face)) {
// append the vertex opposite the
//shared edge
currStream.append(face.verts[share]);
// next face must have opposite orientation
ccw = (!ccw);
// if this was the last tri in the
//strip, then we are done
if (stripDone(currStrip, count)) {
done = true;
// since we are done with this strip,
// set the tail to the current face
currStream.tail = face.key;
}
}
// if we cannot add the face with the correct
// orientation, do not continue with this
// stream
else {
cont = false;
// since we cannot continue with this strip
// set the tail to prev
currStream.tail = prev.key;
}
}
else {
// append the vertex opposite the
//shared edge
currStream.append(face.verts[share]);
// if this was the last tri in the
//strip, then we are done
if (stripDone(currStrip, count)) {
done = true;
// since we are done with this strip,
// set the tail to the current face
currStream.tail = face.key;
}
}
}
// need zero area tris to add continue the strip
else {
if (CHECK_ORIENT) {
// check the orientation for adding a zero
// area tri and this face
if (orientZAT(ccw, currStream, face)) {
// swap the end of the current stream to
// add a zero area triangle
currStream.swapEnd();
// append the vertex opposite the
// shared edge
currStream.append(face.verts[share]);
// if this was the last tri in the
// strip then we are done
if (stripDone(currStrip, count)) {
done = true;
// set the tail because we are done
currStream.tail = face.key;
}
}
// if we cannot add the face with the correct
// orientation, do not continue with this
// stream
else {
cont = false;
// since we cannot continue with this face,
// set the tail to the prev face
currStream.tail = prev.key;
}
}
else {
// swap the end of the current stream to
// add a zero area triangle
currStream.swapEnd();
// append the vertex opposite the
// shared edge
currStream.append(face.verts[share]);
// if this was the last tri in the
// strip then we are done
if (stripDone(currStrip, count)) {
done = true;
// set the tail because we are done
currStream.tail = face.key;
}
}
}
} // while (cont && !stripDone)
} // else
// add the current strip to the strips to be returned
istreams.add(currStream);
} // while !done
} // for each hamiltonian
return istreams;
} // stripe
boolean stripDone(ArrayList strip, int count) {
if (count < strip.size()) {
return false;
}
else return true;
}
boolean seq(Istream stream, Face face, int share) {
int length = stream.length;
Vertex v1 = face.edges[share].v1;
Vertex v2 = face.edges[share].v2;
Vertex last = stream.istream[length-1];
Vertex prev = stream.istream[length-2];
if (((v1.equals(prev)) && (v2.equals(last))) ||
((v1.equals(last)) && (v2.equals(prev)))) {
return true;
}
else return false;
}
boolean orientSeq(boolean ccw, Istream stream, Face face) {
int length = stream.length;
Vertex last = stream.istream[length-1];
Vertex prev = stream.istream[length-2];
if ((ccw && checkOrientCCWSeq(last, prev, face)) ||
((!ccw) && checkOrientCWSeq(last, prev, face))) {
return true;
}
else return false;
}
boolean orientZAT(boolean ccw, Istream stream, Face face) {
int length = stream.length;
Vertex last = stream.istream[length-1];
Vertex swap = stream.istream[length-3];
if ((ccw && checkOrientCWSeq(last, swap, face)) ||
((!ccw) && checkOrientCCWSeq(last, swap, face))) {
return true;
}
else return false;
}
boolean checkOrientCWSeq(Vertex last, Vertex prev, Face face) {
System.out.println("checkOrientCWSeq");
System.out.println("last = " + last.index);
System.out.println("prev = " + prev.index);
System.out.print("face = ");
face.printVertices();
if (last.equals(face.verts[0])) {
if (!prev.equals(face.verts[1])) {
if (DEBUG) System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
else if (last.equals(face.verts[1])) {
if (!prev.equals(face.verts[2])) {
if (DEBUG) System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
else if (last.equals(face.verts[2])) {
if (!prev.equals(face.verts[0])) {
if (DEBUG) System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
return true;
}
boolean checkOrientCCWSeq(Vertex last, Vertex prev, Face face) {
System.out.println("checkOrientCCWSeq");
System.out.println("last = " + last.index);
System.out.println("prev = " + prev.index);
System.out.print("face = ");
face.printVertices();
if (prev.equals(face.verts[0])) {
if (!last.equals(face.verts[1])) {
System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
else if (prev.equals(face.verts[1])) {
if (!last.equals(face.verts[2])) {
System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
else if (prev.equals(face.verts[2])) {
if (!last.equals(face.verts[0])) {
System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
return true;
}
Face getNextFace(ArrayList currStrip, int index) {
if (currStrip.size() > index) return (Face)currStrip.get(index);
else return null;
}
/**
* joins tristrips if their end triangles neighbor each other. The
* priority is performed in three stages: strips are concatenated to
* save 2, 1, or no vertices
*/
void concatenate(ArrayList strips, Face[] faces) {
int numFaces = faces.length;
int[] faceTable = new int[numFaces];
Istream strm;
// initialize the face table to empty
for (int i = 0; i < numFaces; i++) {
faceTable[i] = EMPTY;
}
// set up the faceTable so that a face index relates to a strip
// that owns the face as one of its end faces
for (int i = 0; i < strips.size(); i++) {
strm = (Istream)strips.get(i);
faceTable[strm.head] = i;
faceTable[strm.tail] = i;
}
if (DEBUG) {
System.out.println("");
System.out.println("faceTable:");
for (int i = 0; i < faceTable.length; i++) {
System.out.println(faceTable[i]);
}
System.out.println("");
}
reduceCostByTwo(strips, faces, faceTable);
reduceCostByOne(strips, faces, faceTable);
reduceCostByZero(strips, faces, faceTable);
}
/**
* find all the links that reduce the cost by 2
*/
void reduceCostByTwo(ArrayList strips, Face[] faces, int[] faceTable) {
// System.out.println("reduceCostByTwo");
// number of faces in the face array
int numFaces = faces.length;
// possible adjacent strips
int id, id1, id2;
// Istreams
Istream strm, strm1;
// the length of the Istrem
int len, len1;
// vertex sequences for tristrips
Vertex[] seq, seq1;
// a face
Face face;
// the list of vertices for the face
Vertex[] verts;
// used to syncronize the orientation
boolean sync, sync1;
// a swap variable
Vertex swap;
for (int i = 0; i < numFaces; i++) {
id = faceTable[i];
if (id != EMPTY) {
sync = false; sync1 = false;
strm = (Istream)strips.get(id);
len = strm.length;
seq = strm.istream;
face = faces[i];
verts = face.verts;
// sequential strips
if (!strm.fan) {
// a singleton strip
if (len == 3) {
// check all three neighbors
for (int j = 0; j < 3; j++) {
int k = face.getNeighbor(j);
if ((k != EMPTY) &&
((id1 = faceTable[k]) != EMPTY) &&
(id1 != id)) {
// reassign the sequence
seq[0] = verts[j];
seq[1] = verts[(j+1)%3];
seq[2] = verts[(j+2)%3];
// the neighboring stream
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
if (k != strm1.head) {
strm1.invert();
// if the length is odd set sync1 to true
if ((len1 % 2) != 0) sync1 = true;
}
seq1 = strm1.istream;
// append a singleton strip
if (len1 == 3) {
// System.out.println("reduce2");
int m = faces[k].findSharedEdge(i);
strm.append(faces[k].verts[m]);
strm1.length = 0;
strm1.istream = null;
strm.tail = k;
faceTable[k] = id;
i--;
break;
}
// append a strip of length over 2
else {
if ((len1 == 4) &&
(seq[1].index == seq1[0].index) &&
(seq[2].index == seq1[2].index)) {
// swap seq1[1] and seq1[2] so that
// seq[1] == seq1[0] and
// seq[1] == seq1[1]
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
// see if we can join the strips
if ((seq[1].index == seq1[0].index) &&
(seq[2].index == seq1[1].index)) {
// System.out.println("reduce2");
// add the stream in
strm.addStream(strm1);
faceTable[k] = EMPTY;
faceTable[strm.tail] = id;
i--;
break;
}
else if (sync1) {
strm1.invert();
sync1 = false;
}
}
}
}
}
// not a singleton strip
// can append a stream where the current face is the tail
// or is an even length so we can invert it
else if ((i == strm.tail) || ((len % 2) == 0)) {
// if the current face isn't the tail, then
// have to invert the strip
if (i != strm.tail) {
strm.invert();
seq = strm.istream;
}
// System.out.println("seq.length = " + seq.length);
// System.out.println("len = " + len);
// System.out.print("seq = ");
// for (int l = 0; l < seq.length; l++) {
// if (seq[l] == null) System.out.print(" null");
// else System.out.print(" " + seq[l].index);
// }
// System.out.println("");
swap = seq[len - 3];
// find the neighboring strip
int m = EMPTY;
if (verts[0].index == swap.index) m = 0;
else if (verts[1].index == swap.index) m = 1;
else if (verts[2].index == swap.index) m = 2;
if (m == EMPTY) {
if (DEBUG) System.out.println("problem finding neighbor strip");
}
int j = face.getNeighbor(m);
if (j == EMPTY) id1 = j;
else id1 = faceTable[j];
if ((id1 != EMPTY) &&
(((Istream)strips.get(id1)).fan !=
strm.fan)) {
id1 = EMPTY;
}
if ((id1 != EMPTY) && (id1 != id)) {
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
// if the shared face isn't the head, invert
// the stream
if (j != strm1.head) {
strm1.invert();
// set the sync var if the length is odd
if ((len1 % 2) != 0) sync1 = true;
}
seq1 = strm1.istream;
// append a singleton strip
if (len1 == 3) {
// System.out.println("reduce2");
m = faces[j].findSharedEdge(i);
strm.append(faces[j].verts[m]);
strm1.length = 0;
strm1.istream = null;
strm.tail = j;
faceTable[i] = EMPTY;
faceTable[j] = id;
}
// append a non-singleton strip
else {
if ((len1 == 4) &&
(seq[len-2].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index)) {
// swap seq1[1] and seq1[2] so that
// seq[len-2] == seq1[0] and
// seq[len-1] == seq1[1]
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
// see if we can append the strip
if ((seq[len-2].index == seq1[0].index) &&
(seq[len-1].index == seq1[1].index)) {
// System.out.println("reduce2");
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[j] = EMPTY;
}
else if (sync1) strm1.invert();
}
}
}
}
}
}
}
/**
* find all links that reduce cost by 1
*/
void reduceCostByOne(ArrayList strips, Face[] faces, int[] faceTable) {
// System.out.println("reduceCostByOne");
// number of faces in the face array
int numFaces = faces.length;
// possible adjacent strips
int id, id1, id2;
// Istreams
Istream strm, strm1;
// the length of the Istream
int len, len1;
// vertex sequences for tristrips
Vertex[] seq, seq1;
// a face
Face face;
// the list of vertices for the face
Vertex[] verts;
// used to synchronize the orientation
boolean sync, sync1;
// a swap variable
Vertex swap;
for (int i = 0; i < numFaces; i++) {
id = faceTable[i];
if ((id != EMPTY) && !((Istream)strips.get(id)).fan) {
sync = false;
strm = (Istream)strips.get(id);
seq = strm.istream;
face = faces[i];
verts = face.verts;
len = strm.length;
// a singleton strip
if (len == 3) {
// consider the three neighboring triangles
for (int j = 0; j < 3; j++) {
int k = face.getNeighbor(j);
if ((k != EMPTY) &&
((id1 = faceTable[k]) != EMPTY) &&
(id1 != id) &&
(!((Istream)strips.get(id1)).fan)) {
// reassign the sequence
seq[0] = verts[j];
seq[1] = verts[(j+1)%3];
seq[2] = verts[(j+2)%3];
// the neighboring stream
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
if (k != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
// see if we can join the strips
if ((len1 == 4) &&
(((seq[1].index == seq1[2].index) &&
(seq[2].index == seq1[0].index)) ||
((seq[1].index == seq1[0].index) &&
(seq[2].index == seq1[2].index)))) {
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
if ((seq[1].index == seq1[0].index) &&
(seq[2].index == seq1[1].index)) {
// System.out.println("reduce1");
strm.addStream(strm1);
faceTable[k] = EMPTY;
faceTable[strm.tail] = id;
i--;
break;
}
if ((seq[1].index == seq1[1].index) &&
(seq[2].index == seq1[0].index)) {
// System.out.println("reduce1");
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[k] = EMPTY;
faceTable[strm.tail] = id;
i--;
break;
}
if ((seq[1].index == seq1[0].index) &&
(seq[2].index == seq1[2].index)) {
// System.out.println("reduce1");
seq1[0] = seq1[2];
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[k] = EMPTY;
faceTable[strm.tail] = id;
i--;
break;
}
if (sync) {
strm1.invert();
sync = false;
}
}
}
}
// non-singleton strip
else if ((i == strm.tail) || ((len % 2) == 0)) {
// make sure the face i ends the id-th strip
if (i != strm.tail) {
strm.invert();
seq = strm.istream;
}
swap = seq[len-3];
// find the neighboring strip
int m = EMPTY;
if (verts[0].index == swap.index) m = 0;
else if (verts[1].index == swap.index) m = 1;
else if (verts[2].index == swap.index) m = 2;
if (m == EMPTY) {
if (DEBUG) System.out.println("problem finding neighbor strip");
}
int j = face.getNeighbor(m);
if (j == EMPTY) id1 = j;
else id1 = faceTable[j];
if ((id1 != EMPTY) &&
(((Istream)strips.get(id1)).fan != strm.fan)) {
id1 = EMPTY;
}
// find another neighboring strip
swap = seq[len-2];
m = EMPTY;
if (verts[0].index == swap.index) m = 0;
else if (verts[1].index == swap.index) m = 1;
else if (verts[2].index == swap.index) m = 2;
if (m == EMPTY) {
if (DEBUG) System.out.println("problem finding neighbor strip.");
}
int k = face.getNeighbor(m);
if (k == EMPTY) id2 = k;
else id2 = faceTable[k];
if ((id2 != EMPTY) &&
(((Istream)strips.get(id2)).fan != strm.fan)) {
id2 = EMPTY;
}
// consider strip id1
boolean success = false;
if ((id1 != EMPTY) && (id1 != id)) {
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
if (j != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
if ((len1 == 4) &&
(((seq[len-2].index == seq1[2].index) &&
(seq[len-1].index == seq1[0].index)) ||
(seq[len-2].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index))) {
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
// find matches
if ((seq[len-2].index == seq1[0].index) &&
(seq[len-1].index == seq1[1].index)) {
// System.out.println("reduce1");
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[j] = EMPTY;
success = true;
}
else if ((seq[len-2].index == seq1[1].index) &&
(seq[len-1].index == seq1[0].index)) {
// System.out.println("reduce1");
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[j] = EMPTY;
success = true;
}
else if ((seq[len-2].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index)) {
// System.out.println("reduce1");
seq1[0] = seq1[2];
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[j] = EMPTY;
success = true;
}
else if (sync) {
strm1.invert();
sync = false;
}
}
// now consider strip id2
if (!success &&
(id2 != EMPTY) && (id2 != id)) {
strm1 = (Istream)strips.get(id2);
len1 = strm1.length;
if (k != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
if ((len1 == 4) &&
(seq[len-3].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index)) {
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
// find matches
if ((seq[len-3].index == seq1[0].index) &&
(seq[len-1].index == seq1[1].index)) {
// System.out.println("reduce1");
strm.swapEnd();
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[k] = EMPTY;
success = true;
}
if (!success && sync) strm1.invert();
}
}
}
}
}
/**
* find all the links that reduce the cost by 0
*/
void reduceCostByZero(ArrayList strips, Face[] faces, int[] faceTable) {
// System.out.println("reduceCostByZero");
// number of faces in the face array
int numFaces = faces.length;
// possible adjacent strips
int id, id1, id2;
// Istreams
Istream strm, strm1;
// the length of the Istream
int len, len1;
// vertex sequences for tristrips
Vertex[] seq, seq1;
// a face
Face face;
// the list of vertices for the face
Vertex[] verts;
// used to synchronize the orientation
boolean sync, sync1;
// a swap variable
Vertex swap;
for (int i = 0; i < numFaces; i++) {
id = faceTable[i];
if ((id != EMPTY) && !((Istream)strips.get(id)).fan) {
sync = false;
strm = (Istream)strips.get(id);
seq = strm.istream;
len = strm.length;
face = faces[i];
verts = face.verts;
if (len == 3) {
for (int j = 0; j < 3; j++) {
int k = face.getNeighbor(j);
if ((k != EMPTY) && ((id1 = faceTable[k]) != EMPTY) &&
(id1 != id) &&
!((Istream)strips.get(id1)).fan) {
// reassign the sequence
seq[0] = verts[j];
seq[1] = verts[(j+1)%3];
seq[2] = verts[(j+2)%3];
// the neighboring stream
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
if (k != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
// see if we can join the strips
if ((seq[1].index == seq1[2].index) &&
(seq[2].index == seq1[0].index)) {
// System.out.println("reduce0");
seq1[0] = seq1[2];
strm.append(seq1[0]);
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[k] = EMPTY;
faceTable[strm.tail] = id;
i--;
break;
}
else if (sync) {
strm1.invert();
sync = false;
}
}
}
}
else if ((i == strm.tail) || ((len % 2) == 0)) {
if (i != strm.tail) {
strm.invert();
seq = strm.istream;
}
swap = seq[len-3];
// find neighboring strip
int m = EMPTY;
if (verts[0].index == swap.index) m = 0;
else if (verts[1].index == swap.index) m = 1;
else if (verts[2].index == swap.index) m = 2;
if (m == EMPTY) {
if (DEBUG) System.out.println("problem finding neighbor strip");
}
int j = face.getNeighbor(m);
if (j == EMPTY) id1 = j;
else id1 = faceTable[j];
if ((id1 != EMPTY) &&
(((Istream)strips.get(id1)).fan != strm.fan)) {
id1 = EMPTY;
}
// find another neighboring strip
swap = seq[len-2];
m = EMPTY;
if (verts[0].index == swap.index) m = 0;
else if (verts[1].index == swap.index) m = 1;
else if (verts[2].index == swap.index) m = 2;
if (m == EMPTY) {
if (DEBUG) System.out.println("problem finding neighbor strip.");
}
int k = face.getNeighbor(m);
if (k == EMPTY) id2 = k;
else id2 = faceTable[k];
if ((id2 != EMPTY) &&
(((Istream)strips.get(id2)).fan != strm.fan)) {
id2 = EMPTY;
}
// consider strip id1
boolean success = false;
if ((id1 != EMPTY) && (id1 != id)) {
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
if (j != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
// find matches
if ((seq[len-2].index == seq1[2].index) &&
(seq[len-1].index == seq1[0].index)) {
// System.out.println("reduce0");
seq1[0] = seq1[2];
strm.append(seq1[0]);
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[j] = EMPTY;
success = true;
}
else if (sync) {
strm1.invert();
sync = false;
}
}
// consider strip id2
if (!success && (id2 != EMPTY) && (id2 != id)) {
strm1 = (Istream)strips.get(id2);
len1 = strm1.length;
if (k != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
if ((len1 == 4) &&
(((seq[len-3].index == seq1[2].index) &&
(seq[len-1].index == seq1[0].index)) ||
((seq[len-3].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index)))) {
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
// find matches
if ((seq[len-3].index == seq1[1].index) &&
(seq[len-1].index == seq1[0].index)) {
// System.out.println("reduce0");
strm.swapEnd();
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[k] = EMPTY;
}
else if ((seq[len-3].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index)) {
// System.out.println("reduce0");
seq1[0] = seq1[2];
strm.swapEnd();
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[k] = EMPTY;
}
else if ((seq[len-3].index == seq1[0].index) &&
(seq[len-1].index == seq1[1].index)) {
// System.out.println("reduce0");
strm.swapEnd();
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[k] = EMPTY;
}
else if (sync) strm1.invert();
}
}
}
}
}
/**
* puts the stripified data back into the GeometryInfo object
*/
void putBackData(GeometryInfo gi, ArrayList strips) {
int[] tempStripCounts = new int[strips.size()];
int ciSize = 0;
int stripLength;
for (int i = 0; i < strips.size();) {
stripLength = ((Istream)strips.get(i)).length;
if (stripLength != 0) {
tempStripCounts[i] = stripLength;
ciSize += stripLength;
i++;
}
else {
strips.remove(i);
}
}
if (ciSize > 3) {
gi.setPrimitive(gi.TRIANGLE_STRIP_ARRAY);
int[] stripCounts = new int[strips.size()];
System.arraycopy(tempStripCounts, 0, stripCounts, 0, strips.size());
gi.setStripCounts(stripCounts);
// create one array with all the strips
int[] coords = new int[ciSize];
// create arrays for normals, textures and colors if necessary
int[] normals = null;
int[][] textures = null;
int[] colors = null;
javax.vecmath.Color3b[] stripColors = null;
if (hasNormals) normals = new int[ciSize];
if (hasTextures) {
textures = new int[texSetCount][ciSize];
}
if (hasColors) colors = new int[ciSize];
if (colorStrips) {
stripColors = new javax.vecmath.Color3b[ciSize];
colors = new int[ciSize];
}
int count = 0;
Istream currStrip;
for (int i = 0; i < strips.size(); i++) {
currStrip = (Istream)strips.get(i);
if (currStrip.length < 3) {
throw new RuntimeException("currStrip.length = " +
currStrip.length);
}
java.awt.Color stripColor = null;
if (colorStrips) {
int r = ((int)(Math.random()*1000))%255;
int g = ((int)(Math.random()*1000))%255;
int b = ((int)(Math.random()*1000))%255;
stripColor = new java.awt.Color(r, g, b);
}
for (int j = 0; j < currStrip.length; j++) {
coords[count] = currStrip.istream[j].index;
if (hasNormals) normals[count] = currStrip.istream[j].normal;
if (hasTextures) {
for (int k = 0; k < texSetCount; k++) {
textures[k][count] =
currStrip.istream[j].texture[k];
}
}
if (hasColors) colors[count] = currStrip.istream[j].color;
if (colorStrips) stripColors[count] =
new javax.vecmath.Color3b(stripColor);
count++;
}
}
gi.setCoordinateIndices(coords);
if (hasNormals) gi.setNormalIndices(normals);
if (hasTextures) {
for (int i = 0; i < texSetCount; i++) {
gi.setTextureCoordinateIndices(i, textures[i]);
}
}
if (hasColors) gi.setColorIndices(colors);
if (colorStrips) {
gi.setColors(stripColors);
colors = gi.getListIndices(stripColors);
gi.setColorIndices(colors);
}
}
}
/**
* Stores the infomration about a vertex
*/
class Vertex {
int index;
int normal = EMPTY;
int numTexSets = 0;
int[] texture = null;
int color = EMPTY;
Vertex(int vertIndex) {
this(vertIndex, EMPTY, 0, null, EMPTY);
}
Vertex(int vertIndex, int vertNormal,
int vertNumTexSets, int[] vertTexture, int vertColor) {
index = vertIndex;
normal = vertNormal;
numTexSets = vertNumTexSets;
if (numTexSets > 0) {
texture = new int[numTexSets];
System.arraycopy(vertTexture, 0, texture, 0, numTexSets);
}
color = vertColor;
}
boolean equals(Vertex v) {
for (int i = 0; i < numTexSets; i++) {
if (texture[i] != v.texture[i]) {
return false;
}
}
return ((v.index == index) &&
(v.normal == normal) &&
(v.color == color));
}
// will this yield same results as c code ???
boolean lessThan(Vertex v) {
if (index < v.index) return true;
if (index > v.index) return false;
if (normal < v.normal) return true;
if (normal > v.normal) return false;
for (int i = 0; i < numTexSets; i++) {
if (texture[i] < v.texture[i]) return true;
if (texture[i] > v.texture[i]) return false;
}
if (color < v.color) return true;
if (color > v.color) return false;
return false;
}
}
/**
* Stores the information about an edge of a triangle
*/
class Edge {
Vertex v1, v2;
int face;
Edge(Vertex vertex1, Vertex vertex2, int faceIndex) {
face = faceIndex;
// this could be causing wrapping problem
if (vertex1.lessThan(vertex2)) {
v1 = vertex1;
v2 = vertex2;
} else {
v1 = vertex2;
v2 = vertex1;
}
}
/**
* Determine whether the edges have the same vertices
*/
boolean equals(Edge edge) {
return ((v1.equals(edge.v1)) && (v2.equals(edge.v2)));
}
/**
* Used to sort the edges. If this is less than the edge parameter,
* return true. First check if vertex1 is less than vertex1 of the
* edge provided. If so, return true. If the first vertices are equal
* then check vertex2.
*/
boolean lessThan(Edge edge) {
if (v1.lessThan(edge.v1)) return true;
else if (v1.equals(edge.v1)) return (v2.lessThan(edge.v2));
else return false;
}
}
/**
* Stores the information about the face of a triangle
*/
class Face {
int key;
int numNhbrs = 0;
Vertex[] verts = null;
// edges are kept in order s.t. the ith edge is the opposite
// edge of the ith vertex
Edge[] edges = null;
/**
* Creates a new Face with the three given vertices
*/
Face(int index, Vertex v1, Vertex v2, Vertex v3) {
key = index;
verts = new Vertex[3];
verts[0] = v1;
verts[1] = v2;
verts[2] = v3;
edges = new Edge[3];
edges[0] = null;
edges[1] = null;
edges[2] = null;
numNhbrs = 3;
}
/**
* returns the index of the face that neighbors the edge supplied
* by the parameter
*/
int getNeighbor(int edge) {
return edges[edge].face;
}
/**
* returns the index of the edge that is shared by the triangle
* specified by the key parameter
*/
int findSharedEdge(int key) {
if (edges[0].face == key) return 0;
else if (edges[1].face == key) return 1;
else if (edges[2].face == key) return 2;
else return -1; /* error */
}
int getEdgeIndex(Edge edge) {
if (edges[0].equals(edge)) return 0;
else if (edges[1].equals(edge)) return 1;
else return 2;
}
void counterEdgeDel(Edge edge) {
if (DEBUG) {
System.out.println("counterEdgeDel");
}
if ((edges[0]).equals(edge)) {
edges[0].face = EMPTY;
numNhbrs--;
}
else if ((edges[1]).equals(edge)) {
edges[1].face = EMPTY;
numNhbrs--;
}
else if ((edges[2]).equals(edge)) {
edges[2].face = EMPTY;
numNhbrs--;
}
else {
if (DEBUG) {
System.out.println("error in counterEdgeDel");
}
}
}
void printAdjacency() {
System.out.println("Face " + key + ": ");
System.out.println("\t numNhbrs = " + numNhbrs);
System.out.println("\t edge 0: Face " + edges[0].face);
System.out.println("\t edge 1: Face " + edges[1].face);
System.out.println("\t edge 2: Face " + edges[2].face);
}
void printVertices() {
System.out.println("Face " + key + ": (" + verts[0].index + ", " +
verts[1].index + ", " + verts[2].index + ")");
}
}
/**
* stores the information for a face node
*/
class Node {
Face face; // the data: the face
Node parent; // the parent node
Node left; // the left child
Node right; // the right child
int depth; // the topological distance of the node from the root
int numChildren; // the number of children
int attrib; // characteristic of the node eg. color
// the attributes - 3 states for the Node
static final int WHITE = 0; // not being accessed yet
static final int GREY = 1; // being accessed but not done yet
static final int BLACK = 2; // done
Node(Face f) {
face = f;
}
/**
* inserts this node below the parent supplied.
*/
void insert(Node p) {
parent = p;
depth = p.depth + 1;
attrib = GREY;
if (parent.left == null) parent.left = this;
else parent.right = this;
(parent.numChildren)++;
}
/**
* remove this node from its parent
*/
void remove() {
if (parent != null) {
if (parent.left == this) {
parent.left = parent.right;
parent.right = null;
}
else {
parent.right = null;
}
(parent.numChildren)--;
}
}
/**
* sets the depth to 0 and the attrib to GREY
*/
void setRoot() {
depth = 0;
attrib = GREY;
}
/**
* returns true if the attrib is WHITE
*/
boolean notAccessed() {
return (attrib == WHITE);
}
/**
* sets the color to BLACK
*/
void processed() {
attrib = BLACK;
}
/**
* a node is the root if it doesn't have a parent
*/
boolean isRoot() {
return (parent == null);
}
/**
* prints the information in this Node
*/
void print() {
System.out.println(this);
System.out.println("Node depth: " + depth);
face.printVertices();
System.out.print("parent: ");
if (parent != null) parent.face.printVertices();
else System.out.println("null");
System.out.print("left: ");
if (left != null) left.face.printVertices();
else System.out.println("null");
System.out.print("right: ");
if (right != null) right.face.printVertices();
else System.out.println("null");
System.out.println("attrib: " + attrib);
System.out.println("");
}
}
/**
* sorts the Nodes by depth
*/
class SortedList {
ArrayList list;
/**
* create a new SortedList
*/
SortedList() {
list = new ArrayList();
}
/**
* insert into the list sorted by depth. start looking at start
* to save some time. Returns the index of the next item of the
* inserted element
*/
int sortedInsert(Node data, int start) {
// adjust start to where insert sorted
while ((start < list.size()) &&
(((Node)list.get(start)).depth <= data.depth)) {
start++;
}
// insert at start index
list.add(start, data);
// return start+1 -- the index of the next element
return (start+1);
}
/**
* remove and return 1st element
*/
Node pop() {
if (!list.isEmpty()) return (Node)list.remove(0);
else return null;
}
}
class Istream {
// fan encoding
boolean fan = false;
// length of the strip
int length = 0;
// array that specifies triangle strip
Vertex[] istream;
// indices of the head and tail vertices
int head, tail;
/**
* creates a new Istream to store the triangle strip
*/
Istream(Vertex[] list, int size, boolean isFan) {
if (size == 0) throw new RuntimeException("size is 0");
fan = isFan;
length = size;
istream = new Vertex[length];
int i;
System.arraycopy(list, 0, istream, 0, length);
}
/**
* adds a new vertex to the end of the stream
* makes the int array bigger, if necessary
*/
void append(Vertex vertex) {
growArray();
// add in new vertex
istream[length] = vertex;
length++;
}
/**
* turns the encoding (..., -3, -2, -1) into (.... -3, -2, -3, -1)
* so that zero-area triangle (-3, -2. -3) is added
*/
void swapEnd() {
growArray();
istream[length] = istream[length-1];
istream[length-1] = istream[length-3];
length++;
}
/**
* makes the array bigger, if necessary
*/
void growArray() {
if (length >= istream.length) {
Vertex[] old = istream;
// for now add enough space to add three more vertices
// may change this later
istream = new Vertex[length + 3];
System.arraycopy(old, 0, istream, 0, length);
}
}
/**
* inverts the istream
*/
void invert() {
Vertex[] tmp = new Vertex[istream.length];
// reverse the stream
for (int i = 0; i < length; i++) {
tmp[i] = istream[length - i - 1];
}
// copy it back
System.arraycopy(tmp, 0, istream, 0, istream.length);
tmp = null;
// swap the head and the tail
int swap = head;
head = tail;
tail = swap;
}
/**
* concats two streams into one big stream
*/
void addStream(Istream strm) {
// System.out.println("addStream");
int strmLen = strm.length;
int size = strmLen + length - 2;
// make the istream bigger
if (size >= istream.length) {
Vertex[] old = istream;
istream = new Vertex[size];
System.arraycopy(old, 0, istream, 0, length);
}
// add the strm to istream
System.arraycopy(strm.istream, 2, istream, length, strmLen-2);
tail = strm.tail;
length = size;
strm.length = 0;
strm.istream = null;
}
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Left.java 0000644 0000000 0000000 00000005473 10563126523 027205 0 ustar root root /*
* $RCSfile: Left.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:19 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
class Left extends Object {
int ind;
int index;
Left() {
}
void copy(Left l) {
ind = l.ind;
index = l.index;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/EdgeTable.java 0000644 0000000 0000000 00000007377 10563126522 030133 0 ustar root root /*
* $RCSfile: EdgeTable.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:18 $
* $State: Exp $
*/
package com.sun.j3d.utils.geometry;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import com.sun.j3d.utils.geometry.Edge;
class EdgeTable {
private HashMap edgeTable;
private static final int DEBUG = 0;
Integer get(int a, int b)
{
return (Integer)edgeTable.get(new Edge(a, b));
} // End of get()
Integer get(Edge e)
{
return (Integer)edgeTable.get(e);
} // End of get()
// This function creates a table used to connect the triangles.
// Here's how it works. If a triangle is made of indices 12,
// 40, and 51, then edge(12, 40) gets 51, edge(40, 51) gets 12,
// and edge(51, 12) gets 40. This lets us quickly move from
// triangle to triangle without saving a lot of extra data.
EdgeTable(int triangleIndices[])
{
// We'll have one edge for each vertex
edgeTable = new HashMap(triangleIndices.length * 2);
// Fill in table
Edge e;
for (int t = 0 ; t < triangleIndices.length ; t += 3) {
// Put all 3 edges of triangle into table
for (int v = 0 ; v < 3 ; v++) {
e = new Edge(triangleIndices[t + v],
triangleIndices[t + ((v + 1) % 3)]);
if (edgeTable.get(e) != null) {
if ((DEBUG & 1) != 0) {
System.out.println("EdgeTable Error: duplicate edge (" +
triangleIndices[t + v] + ", " +
triangleIndices[t + ((v + 1) % 3)] + ").");
}
} else {
// Store index of 3rd vertex (across from edge)
edgeTable.put(e, new Integer(t + ((v + 2) % 3)));
}
}
}
if ((DEBUG & 1) != 0) {
System.out.println("Edge Table:");
Iterator list = edgeTable.keySet().iterator();
while (list.hasNext()) {
Edge edge = (Edge)list.next();
System.out.println(" (" + edge.v1 + ", " + edge.v2 + ") = " +
get(edge.v1, edge.v2));
}
}
} // End of constructor EdgeTable
} // End of class EdgeTable
// End of file EdgeTable.java
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Edge.java 0000644 0000000 0000000 00000005214 10563126522 027147 0 ustar root root /*
* $RCSfile: Edge.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:18 $
* $State: Exp $
*/
package com.sun.j3d.utils.geometry;
// Class created so that the two vertex indices that make up an
// edge can be hashed.
class Edge {
public int v1;
public int v2;
private static final int HASHCONST = 0xEDCBA987;
public int hashCode()
{
return ((v1 * HASHCONST) << 2) ^ (v2 * HASHCONST);
} // end of Edge.hashCode
public boolean equals(Object x)
{
if (!(x instanceof Edge)) return false;
Edge e = (Edge)x;
return (v1 == e.v1) && (v2 == e.v2);
} // End of Edge.equals
public String toString()
{
return "(" + v1 + ", " + v2 + ")";
} // End of toString
public Edge(int a, int b)
{
v1 = a;
v2 = b;
}
public Edge(Edge e)
{
v1 = e.v1;
v2 = e.v2;
}
public Edge()
{
}
} // end of class Edge
// End of file Edge.java
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/PntNode.java 0000644 0000000 0000000 00000005430 10563126524 027654 0 ustar root root /*
* $RCSfile: PntNode.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:20 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
// Placeholder list
class PntNode extends Object {
int pnt;
int next;
PntNode() {
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/StripifierStats.java 0000644 0000000 0000000 00000024651 10563126524 031452 0 ustar root root /*
* $RCSfile: StripifierStats.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:20 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import java.util.ArrayList;
/**
* This class collects statistics on the Stripifier. The statistics
* are cumulative over all calls to stripify() until clearData() is called.
*
* @since Java 3D 1.2.1
*/
public class StripifierStats {
int numStrips = 0;
int numVerts = 0;
int minStripLen = 10000;
int maxStripLen = 0;
int totalTris = 0;
int numFaces = 0;
long time = 0;
int[] counts = new int[14];
boolean noData = true;
/**
* Returns the number of triangles in the original, un-stripified data.
* @since Java 3D 1.2.1
*/
public int getNumOrigTris() {
return numFaces;
}
/**
* Returns the number of vertices in the original, un-stripified data
* @since Java 3D 1.2.1
*/
public int getNumOrigVerts() {
return (numFaces * 3);
}
/**
* Returns the number of strips created by the stripifier.
* @since Java 3D 1.2.1
*/
public int getNumStrips() {
return numStrips;
}
/**
* Returns the number of vertices in the stripified data.
* @since Java 3D 1.2.1
*/
public int getNumVerts() {
return numVerts;
}
/**
* Returns the number of triangles in the stripified data.
* @since Java 3D 1.2.1
*/
public int getTotalTris() {
return totalTris;
}
/**
* Returns the length in triangles of the shortest strip
* created by the stripifier.
* @since Java 3D 1.2.1
*/
public int getMinStripLength() {
return minStripLen;
}
/**
* Returns the length in triangles of the longest strip
* created by the stripifier.
* @since Java 3D 1.2.1
*/
public int getMaxStripLength() {
return maxStripLen;
}
/**
* Return the average length of the strips created by the stripifier
* @since Java 3D 1.2.1
*/
public double getAvgStripLength() {
return ((double)totalTris/(double)numStrips);
}
/**
* Returns the average number of vertices per triangle in the stripified
* data
* @since Java 3D 1.2.1
*/
public double getAvgNumVertsPerTri() {
return ((double)numVerts/(double)totalTris);
}
/**
* Returns the total time spent in the stripify() method
* @since Java 3D 1.2.1
*/
public long getTotalTime() {
return time;
}
/**
* Returns an array of length 14 that contains the number of strips of
* a given length created by the stripifier. Spots 0-8 of the array
* represent lengths 1-9, 9 is lengths 10-19, 10 is lengths 20-49,
* 11 is lengths 50-99, 12 is lengths 100-999 and 13 is lengths 1000
* or more.
* @since Java 3D 1.2.1
*/
public int[] getStripLengthCounts() {
return counts;
}
/**
* Returns a formated String that can be used to print out
* the Stripifier stats.
* @since Java 3D 1.2.1
*/
public String toString() {
StringBuffer str = new StringBuffer(
"num orig tris: " + numFaces + "\n" +
"num orig vertices: " + (numFaces*3) + "\n" +
"number of strips: " + numStrips + "\n" +
"number of vertices: " + numVerts + "\n" +
"total tris: " + totalTris + "\n" +
"min strip length: " + minStripLen + "\n" +
"max strip length: " + maxStripLen + "\n" +
"avg strip length: " + ((double)totalTris/
(double)numStrips) + "\n" +
"avg num verts/tri: " + ((double)numVerts/
(double)totalTris) + "\n" +
"total time: " + time + "\n" +
"strip length distribution:\n");
for (int i = 0; i < 9; i++){
str.append(" " + (i+1) + "=" + counts[i]);
}
str.append(" 10-19=" + counts[9]);
str.append(" 20-49=" + counts[10]);
str.append(" 50-99=" + counts[11]);
str.append(" 100-999=" + counts[12]);
str.append(" 1000 or more=" + counts[13] + "\n");
return str.toString();
}
/**
* Clears the statistical data
*/
public void clearData() {
noData = true;
numStrips = 0;
numVerts = 0;
minStripLen = 10000;
maxStripLen = 0;
totalTris = 0;
numFaces = 0;
time = 0;
counts = new int[14];
}
void updateInfo(long ntime, ArrayList strips,
int nNumFaces) {
noData = false;
time += ntime;
numStrips += strips.size();
int nv = 0;
int mnsl = 10000;
int mxsl = 0;
int tt = 0;
for (int i = 0; i < strips.size(); i++) {
Stripifier.Istream strm = (Stripifier.Istream)strips.get(i);
int len = strm.length;
int trilen = (len-2);
nv += len;
if (trilen < mnsl) mnsl = trilen;
if (trilen > mxsl) mxsl = trilen;
tt += trilen;
// add to counts
// how many strips are length 1-9
if (trilen <= 9) counts[trilen-1] += 1;
// how many strips are length 10-19
else if (trilen < 20) counts[9] += 1;
// how many strips are length 20-49
else if (trilen < 50) counts[10] += 1;
// how many strips are length 50-99
else if (trilen < 100) counts[11] += 1;
// how many strips are length 100-1000
else if (trilen < 1000) counts[12] += 1;
// how many strips are length > 1000
else counts[13] += 1;
}
numVerts += nv;
if (mnsl < minStripLen) minStripLen = mnsl;
if (mxsl > maxStripLen) maxStripLen = mxsl;
totalTris += tt;
numFaces += nNumFaces;
}
void updateInfo(long ntime, int scLen, int sc[],
int nNumFaces) {
noData = false;
time += ntime;
numStrips += scLen;
int nv = 0;
int mnsl = 10000;
int mxsl = 0;
int tt = 0;
for (int i = 0; i < scLen; i++) {
int len = sc[i];
int trilen = (len-2);
numVerts += len;
if (trilen < mnsl) mnsl = trilen;
if (trilen > mxsl) mxsl = trilen;
totalTris += trilen;
// add to counts
// how many strips are length 1-9
if (trilen <= 9) counts[trilen-1] += 1;
// how many strips are length 10-19
else if (trilen < 20) counts[9] += 1;
// how many strips are length 20-49
else if (trilen < 50) counts[10] += 1;
// how many strips are length 50-99
else if (trilen < 100) counts[11] += 1;
// how many strips are length 100-1000
else if (trilen < 1000) counts[12] += 1;
// how many strips are length > 1000
else counts[13] += 1;
}
numVerts += nv;
if (mnsl < minStripLen) minStripLen = mnsl;
if (mxsl > maxStripLen) maxStripLen = mxsl;
totalTris += tt;
numFaces += nNumFaces;
}
// void printInfo() {
// System.out.println("num orig tris: " + numFaces);
// System.out.println("num orig vertices: " + (numFaces*3));
// System.out.println("number of strips: " + numStrips);
// System.out.println("number of vertices: " + numVerts);
// System.out.println("total tris: " + totalTris);
// System.out.println("min strip length: " + minStripLen);
// System.out.println("max strip length: " + maxStripLen);
// System.out.println("avg strip length: " + ((double)totalTris/
// (double)numStrips));
// System.out.println("avg num verts/tri: " + ((double)numVerts/
// (double)totalTris));
// System.out.println("total time: " + time);
// System.out.println("strip length distribution:");
// for (int i = 0; i < 9; i++){
// System.out.print(" " + (i+1) + "=" + counts[i]);
// }
// System.out.print(" 10-19=" + counts[9]);
// System.out.print(" 20-49=" + counts[10]);
// System.out.print(" 50-99=" + counts[11]);
// System.out.print(" 100-999=" + counts[12]);
// System.out.println(" 1000 or more=" + counts[13]);
// // reset info after printing data
// numStrips = 0;
// numVerts = 0;
// minStripLen = 10000;
// maxStripLen = 0;
// totalTris = 0;
// numFaces = 0;
// time = 0;
// counts = new int[14];
// }
StripifierStats() {
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/ColorCube.java 0000644 0000000 0000000 00000011133 10563126522 030155 0 ustar root root /*
* $RCSfile: ColorCube.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:18 $
* $State: Exp $
*/
package com.sun.j3d.utils.geometry;
import javax.media.j3d.*;
/**
* Simple color-per-vertex cube with a different color for each face
*/
public class ColorCube extends Shape3D {
private static final float[] verts = {
// front face
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
// back face
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
// right face
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
// left face
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
// top face
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
// bottom face
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
};
private static final float[] colors = {
// front face (red)
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
// back face (green)
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
// right face (blue)
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
// left face (yellow)
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
// top face (magenta)
1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
// bottom face (cyan)
0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f,
};
double scale;
/**
* Constructs a color cube with unit scale. The corners of the
* color cube are [-1,-1,-1] and [1,1,1].
*/
public ColorCube() {
QuadArray cube = new QuadArray(24, QuadArray.COORDINATES |
QuadArray.COLOR_3);
cube.setCoordinates(0, verts);
cube.setColors(0, colors);
this.setGeometry(cube);
scale = 1.0;
}
/**
* Constructs a color cube with the specified scale. The corners of the
* color cube are [-scale,-scale,-scale] and [scale,scale,scale].
* @param scale the scale of the cube
*/
public ColorCube(double scale) {
QuadArray cube = new QuadArray(24, QuadArray.COORDINATES |
QuadArray.COLOR_3);
float scaledVerts[] = new float[verts.length];
for (int i = 0; i < verts.length; i++)
scaledVerts[i] = verts[i] * (float)scale;
cube.setCoordinates(0, scaledVerts);
cube.setColors(0, colors);
this.setGeometry(cube);
this.scale = scale;
}
/**
* @deprecated ColorCube now extends shape so it is no longer necessary
* to call this method.
*/
public Shape3D getShape() {
return this;
}
/**
* Returns the scale of the Cube
*
* @since Java 3D 1.2.1
*/
public double getScale() {
return scale;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/geometry/Distance.java 0000644 0000000 0000000 00000005512 10563126522 030036 0 ustar root root /*
* $RCSfile: Distance.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:18 $
* $State: Exp $
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
class Distance extends Object {
int ind;
double dist;
Distance() {
}
void copy(Distance d) {
ind = d.ind;
dist = d.dist;
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/ 0000755 0000000 0000000 00000000000 11240226632 026140 5 ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/compression/HuffmanNode.java 0000644 0000000 0000000 00000015154 10563126521 031206 0 ustar root root /*
* $RCSfile: HuffmanNode.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/02/09 17:20:17 $
* $State: Exp $
*/
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
* 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.com.sun.j3d.utils.geometry.compression
instead.ConfiguredUniverse.getConfigURL().getBranchGraphPosition.indexout.
*
* This is called after data for the parent SceneGraphObject has been written to
* the out.
*
* @param out the output stream
*/
public void writeSceneGraphObject( java.io.DataOutput out ) throws java.io.IOException;
/**
* This is called after the object has been constructed and the superclass SceneGraphObject
* data has been read from in.
*
* The user should restore all state infomation written in writeSGObject
*
* @param in the input stream
*/
public void readSceneGraphObject( java.io.DataInput in ) throws java.io.IOException;
/**
* Flag indicating for children of this object should be saved
*
* This method only has an effect if this is a subclass of Group.
*
* If this returns true then all children of this Group will be saved.
*
* If it returns false then the children are not saved.
*/
public boolean saveChildren();
}
././@LongLink 0000000 0000000 0000000 00000000157 00000000000 011570 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamWriter.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamW0000644 0000000 0000000 00000012154 10600267042 031574 0 ustar root root /*
* $RCSfile: SceneGraphStreamWriter.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.5 $
* $Date: 2007/03/21 17:40:50 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io;
import java.io.File;
import java.io.IOException;
import java.io.DataOutputStream;
import java.util.HashMap;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.SceneGraphObject;
import javax.media.j3d.CapabilityNotSetException;
import javax.media.j3d.DanglingReferenceException;
import com.sun.j3d.utils.scenegraph.io.retained.StreamControl;
import com.sun.j3d.utils.universe.SimpleUniverse;
/**
* Writes a Java3D SceneGraph to a Java OutputStream.universe to the Stream.writeContent is true then all BranchGraphs attached to the
* universe will be saved. If it is false then only the universe
* data structures will be output (PlatformGeometry, ViewerAvatar, Locales,
* and the MultiTransformGroup between the ViewingPlatform and the View).writeContent is true then all the BranchGraphs
* attached to the Locales of the universe must have the
* ALLOW_DETACH capability set. If they do not, a CapabilityNotSetException
* will be thrownnamedObjects can contain a mapping between a key and a SceneGraphObject
* in the graph. During the read process this can be used to locate nodes
* in the graph.
*/
public void writeBranchGraph( BranchGroup graph, HashMap namedObjects ) throws IOException, DanglingReferenceException, NamedObjectException {
// TODO Add namedObjects to SymbolTable
control.addNamedObjects( namedObjects );
control.writeBranchGraph( graph, null );
}
/**
* Close the SceneGraphStreamWriter and the associated stream
*/
public void close() throws IOException {
control.close();
out.close();
}
}
././@LongLink 0000000 0000000 0000000 00000000153 00000000000 011564 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/UnresolvedBehavior.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/UnresolvedBehavio0000644 0000000 0000000 00000004701 10563126533 031704 0 ustar root root /*
* $RCSfile: UnresolvedBehavior.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:27 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io;
/**
* This Behavior is used in place of any behaviors which can not
* be instantiated when a scene graph is read. This behavior is
* always disabled when it initalizes. It just provides an indicator
* in the scene graph that a Behavior is missing.
*
* This normally means the Behavior is not present in the classpath.
*/
public class UnresolvedBehavior extends javax.media.j3d.Behavior {
public void initialize() {
setEnable(false);
}
public void processStimulus(java.util.Enumeration enumeration) {
}
}
././@LongLink 0000000 0000000 0000000 00000000157 00000000000 011570 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamReader.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamR0000644 0000000 0000000 00000010544 10600271461 031570 0 ustar root root /*
* $RCSfile: SceneGraphStreamReader.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.6 $
* $Date: 2007/03/21 18:02:25 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.HashMap;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.SceneGraphObject;
import javax.media.j3d.Canvas3D;
import com.sun.j3d.utils.scenegraph.io.retained.StreamControl;
import com.sun.j3d.utils.universe.ConfiguredUniverse;
/**
* Read and create a (set) of Java3D BranchGraphs or Universe from a Java Stream.
*/
public class SceneGraphStreamReader extends java.lang.Object {
private StreamControl control;
private DataInputStream in;
/** Creates new SceneGraphStreamReader and reads the file header information */
public SceneGraphStreamReader( InputStream stream ) throws IOException {
in = new DataInputStream( stream );
control = new StreamControl( in );
control.readStreamHeader();
}
/**
* Read and create the universe. If the BranchGraphs were written then
* they will be added to the universe before it is returned.
*/
public ConfiguredUniverse readUniverse() throws IOException {
return control.readUniverse(in, true, null);
}
/**
* Read and create the universe. If the BranchGraphs were written then
* they will be added to the universe before it is returned.
* @param canvas The Canvas3D to associate with the universe.
*/
public ConfiguredUniverse readUniverse(Canvas3D canvas) throws IOException {
return control.readUniverse(in, true, canvas);
}
/**
* Read and return the graph from the stream.
* namedObjects map will be updated with any objects that
* were named during the write process
*/
public BranchGroup readBranchGraph( HashMap namedObjects ) throws IOException {
return control.readBranchGraph( namedObjects );
}
/**
* Set the ClassLoader used to load the scene graph objects and
* deserialize user data
*/
public void setClassLoader( ClassLoader classLoader ) {
control.setClassLoader( classLoader );
}
/**
* Get the ClassLoader used to load the scene graph objects and
* deserialize user data
*/
public ClassLoader getClassLoader() {
return control.getClassLoader();
}
/**
* Close the SceneGraphStreamReader stream
*
* @since Java 3D 1.5.1
*/
public void close() throws IOException {
in.close();
control.close();
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/package.html 0000644 0000000 0000000 00000011166 10563126534 030622 0 ustar root root
j3d.io.UseSuperClassIfNoChildClass when this property is present the load
operation will attempt to avoid failure if Scene Graph nodes are not present
in the classpath. For example if a developer has subclassed BranchGroup with a
class called MyBG but has not
implemented the SceneGraphIO interface when the API saves the graph the
superclass (ie BranchGroup) data will be stored. When the scene is loaded
normally MyBG must be in the classpath otherwise the load will fail. If this
property is set then the superclass node (ie BranchGroup) will be instantiated
when MyBG is missing. Obviously, if MyBG contained any state information
then this will be lost.
UnsupportedUniverseException without detail message.
*/
public UnsupportedUniverseException() {
}
/**
* Constructs an UnsupportedUniverseException with the specified detail message.
* @param msg the detail message.
*/
public UnsupportedUniverseException(String msg) {
super(msg);
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/doc-files/ 0000755 0000000 0000000 00000000000 11240226645 030176 5 ustar root root ././@LongLink 0000000 0000000 0000000 00000000160 00000000000 011562 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/doc-files/extensibility.html java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/doc-files/extensi0000644 0000000 0000000 00000016642 10563126534 031614 0 ustar root root
The scenegraph.io APIs will handle the IO for all the core Java3D SceneGraphObjects. However, if you create a subclass of one of these objects and add it to your Scene Graph, the IO system, by default, will not store any state information specific to your class.
The default behavior when an unrecognized SceneGraphObject class is encountered is to traverse up the superclasses of the object until a recognized Java3D class is located. The data structures for this class are then used for IO. The system does store the class name of the original object.
For example:
public class MyBranchGroup extends javax.media.j3d.BranchGroup {
private int myData;
....
}
When the Scene Graph is written to a file and this node is encountered, the superclass javax.media.j3d.BranchGroup will be used to store all the state for the object so any children of MyBranchGroup, the capabilities, etc. will be stored, but myData will be lost. When the scene graph is loaded, MyBranchGroup will be instantiated and will be populated with all the state from BranchGroup but myData will have been lost.
To overcome this, the scenegraph.io API provides an interface for you to implement in your own classes that provides the opportunity for you to save the state of your classes during the IO processes. This is the SceneGraphIO interface.
When the scenegraph is saved, the methods of SceneGraphIO are called in this order
createSceneGraphObjectReferences
saveChildren
writeSceneGraphObject
During the load cycle the method call order is
Instantiate Object using default constructor
Populate object with state from superclasses
readSceneGraphObject
restoreSceneGraphObjectReferences
Within each method you need to perform the following actions:
createSceneGraphObjectReferences If your object has references to other SceneGraphObjects then you need to obtain an object reference (int) for each reference using the SceneGraphReferenceControl object passed as a parameter to this method. If you don't have references to other SceneGraphObjects then no action is required.
saveChildren If your object is a subclass of Group and you want the scenegraph.io package to save the children then this must return true. If it returns false, the object will be saved but not its children.
writeSceneGraphObject In this method you must write all the state information for your class, including the object references obtained in createSceneGraphObjectReferences, to the DataOutput stream passed into this method.
readSceneGraphObject By the time this method is called your class has been instantiated and the state information in the Java3D superclass will have been loaded. You should load all the state information you saved for your class.
restoreSceneGraphObjectReferences is called once all the SceneGraph objects have been loaded and allows you to restore the references to the other SceneGraph objects.
Here are some examples. Only the parts of the source pertaining to IO are show....
public class BehaviorIO extends javax.media.j3d.Behavior implements SceneGraphIO private TransformGroup target; // The TG on which this behavior acts private int targetRef; // Object Reference for target public void createSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref ) { targetRef = ref.addReference( target ); } public void restoreSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref ) { target = (TransformGroup)ref.resolveReference( targetRef ); } public void writeSceneGraphObject( java.io.DataOutput out ) throws IOException { out.writeInt( targetRef ); } public void readSceneGraphObject( java.io.DataInput in ) throws IOException { targetRef = in.readInt(); } // This has no effect as this is not a subclass of Group public boolean saveChildren() { return true; }
public class House extends Group implements SceneGraphIO {
public House() {
super();
this.addChild( OpenFlightLoader.load( "/dir/house.flt" );
}
public void createSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref ) {
// No references
}
public void restoreSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref ) {
// No references
}
public void writeSceneGraphObject( java.io.DataOutput out ) throws IOException {
// No local state
}
public void readSceneGraphObject( java.io.DataInput in ) throws IOException {
// No local state
}
public boolean saveChildren() {
// Don't save the children as they will be restored by the openflightloader
return false;
}
././@LongLink 0000000 0000000 0000000 00000000161 00000000000 011563 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/ObjectNotLoadedException.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/ObjectNotLoadedEx0000644 0000000 0000000 00000004664 10563126533 031565 0 ustar root root /*
* $RCSfile: ObjectNotLoadedException.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:27 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io;
/**
* The named object has not been loaded so it's instance can not be returned
*/
public class ObjectNotLoadedException extends java.lang.Exception {
/**
* Creates new ObjectNotLoadedException without detail message.
*/
public ObjectNotLoadedException() {
}
/**
* Constructs an ObjectNotLoadedException with the specified detail message.
* @param msg the detail message.
*/
public ObjectNotLoadedException(String msg) {
super(msg);
}
}
././@LongLink 0000000 0000000 0000000 00000000155 00000000000 011566 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileWriter.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileWri0000644 0000000 0000000 00000014370 10563126533 031564 0 ustar root root /*
* $RCSfile: SceneGraphFileWriter.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:27 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.SceneGraphObject;
import javax.media.j3d.CapabilityNotSetException;
import com.sun.j3d.utils.scenegraph.io.retained.RandomAccessFileControl;
import com.sun.j3d.utils.universe.SimpleUniverse;
/**
* Write a (set) of Java3D BranchGraphs and/or Universe to a file. The BranchGraphs
* are stored in the order in which they are written, they can be read in any order
* using SceneGraphFileReader.
*
* The API handles Nodes and NodeComponents that are shared between seperate
* graphs. It will handle all Java3D 1.3 core classes and any user
* subclass of a Node or NodeComponent that implements the SceneGraphIO
* interface.
*/
public class SceneGraphFileWriter extends java.lang.Object {
private RandomAccessFileControl fileControl;
private File file;
/** Creates new SceneGraphFileWriter and opens the file for writing.
*
* Writes the
* Java3D Universe structure to the file. This includes the number and position of
* the Locales, PlatformGeometry, ViewerAvatar, and the MultitransformGroup between
* the ViewingPlatform and the View. However this
* call does not write the content of the branch graphs unless writeUniverseContent is true.
* universe may be null.
* This call will overwrite any existing universe, fileDescription and
* userData in the file.
close() MUST be called when IO is complete. If close() is not called * the file contents will be undefined.
* * @param file The file to write the data to * @param universe The SimpleUniverse to write * @param writeUniverseContent If true, the content of the Locales will be written. * Otherwise just the universe configuration data will be written. * @param fileDescription A description of the file's content * @param fileUserData User defined object * * @exception IOException Thrown if there are any IO errors * @exception UnsupportedUniverseException Thrown ifuniverse is not
* a supported universe class. Currently SimpleUniverse and ConfiguredUniverse
* are supported.
*/
public SceneGraphFileWriter( java.io.File file,
SimpleUniverse universe,
boolean writeUniverseContent,
String fileDescription,
java.io.Serializable fileUserData) throws IOException, UnsupportedUniverseException {
fileControl = new RandomAccessFileControl();
this.file = file;
file.createNewFile();
if (!file.canWrite())
throw new IOException( "Can not Write to File" );
fileControl.createFile( file, universe, writeUniverseContent, fileDescription, fileUserData );
}
/**
* Write the graph to the end of the file.
*
* close() MUST be called when IO is complete. If close() is not called
* the file contents will be undefined.
*/
public void writeBranchGraph( BranchGroup graph ) throws IOException {
writeBranchGraph( graph, null );
}
/**
* Write a branch graph and some user associated data to the
* end of the file.
*
* close() MUST be called when IO is complete. If close() is not called
* the file contents will be undefined.
*/
public void writeBranchGraph( BranchGroup graph,
java.io.Serializable data ) throws IOException {
fileControl.writeBranchGraph( graph, data );
}
/**
* Add a named reference to a SceneGraphObject in the file.
*
* object must have been written to the file before this method is
* called. If the object is not in the file a NamedObjectException will be thrown.
*
* Adding duplicate names will result in the old name being overwritten.
* Different names can reference the same object
*/
public void addObjectName( String name, SceneGraphObject object ) throws NamedObjectException {
fileControl.addNamedObject( name, object );
}
/**
* Close the file and cleanup internal data structures.
*/
public void close() throws IOException {
fileControl.close();
}
}
java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/ 0000755 0000000 0000000 00000000000 11240226647 027453 5 ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/ 0000755 0000000 0000000 00000000000 11240226646 030230 5 ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/ 0000755 0000000 0000000 00000000000 11240226646 031035 5 ustar root root ././@LongLink 0000000 0000000 0000000 00000000146 00000000000 011566 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/ java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000755 0000000 0000000 00000000000 11240226646 031436 5 ustar root root ././@LongLink 0000000 0000000 0000000 00000000154 00000000000 011565 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/ java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000755 0000000 0000000 00000000000 11240226647 031437 5 ustar root root ././@LongLink 0000000 0000000 0000000 00000000165 00000000000 011567 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000755 0000000 0000000 00000000000 11240226647 031437 5 ustar root root ././@LongLink 0000000 0000000 0000000 00000000203 00000000000 011560 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ConeState.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000644 0000000 0000000 00000013324 10563126536 031447 0 ustar root root /*
* $RCSfile: ConeState.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:30 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
import java.io.*;
import com.sun.j3d.utils.geometry.Cone;
import javax.media.j3d.SceneGraphObject;
import javax.media.j3d.Appearance;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Material;
import com.sun.j3d.utils.scenegraph.io.retained.Controller;
import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
public class ConeState extends PrimitiveState {
private float radius=1f;
private float height=2f;
private int xdivision=15;
private int ydivision=1;
private int bodyAppearance=0;
private int capAppearance=0;
public ConeState(SymbolTableData symbol,Controller control) {
super( symbol, control );
if (node!=null) {
bodyAppearance = control.getSymbolTable().addReference( ((Cone)node).getShape( Cone.BODY ).getAppearance() );
capAppearance = control.getSymbolTable().addReference( ((Cone)node).getShape( Cone.CAP ).getAppearance() );
}
}
public void writeObject( DataOutput out ) throws IOException {
super.writeObject( out );
out.writeInt( bodyAppearance );
out.writeInt( capAppearance );
}
public void readObject( DataInput in ) throws IOException {
super.readObject(in);
bodyAppearance = in.readInt();
capAppearance = in.readInt();
}
public void writeConstructorParams( DataOutput out ) throws IOException {
super.writeConstructorParams( out );
out.writeFloat( ((Cone)node).getRadius() );
out.writeFloat( ((Cone)node).getHeight() );
out.writeInt( ((Cone)node).getXdivisions() );
out.writeInt( ((Cone)node).getYdivisions() );
}
public void readConstructorParams( DataInput in ) throws IOException {
super.readConstructorParams(in);
radius = in.readFloat();
height = in.readFloat();
xdivision = in.readInt();
ydivision = in.readInt();
}
public void buildGraph() {
if (bodyAppearance == capAppearance ) {
((Cone)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( bodyAppearance ));
} else {
((Cone)node).setAppearance( Cone.BODY, (Appearance)control.getSymbolTable().getJ3dNode( bodyAppearance ));
((Cone)node).setAppearance( Cone.CAP, (Appearance)control.getSymbolTable().getJ3dNode( capAppearance ));
}
super.buildGraph(); // This must be the last call in the method
}
public SceneGraphObject createNode( Class j3dClass ) {
// Create the node with a null appearance, we will add the appearance
// during build graph
Cone cone = (Cone)createNode( j3dClass, new Class[] {
Float.TYPE,
Float.TYPE,
Integer.TYPE,
Integer.TYPE,
Integer.TYPE,
Appearance.class },
new Object[] {
new Float( radius ),
new Float( height ),
new Integer( primflags ),
new Integer( xdivision ),
new Integer( ydivision ),
null } );
return cone;
}
protected javax.media.j3d.SceneGraphObject createNode() {
return new Cone( radius, height, primflags, xdivision, ydivision, null );
}
}
././@LongLink 0000000 0000000 0000000 00000000210 00000000000 011556 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/PrimitiveState.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000644 0000000 0000000 00000006115 10563126536 031447 0 ustar root root /*
* $RCSfile: PrimitiveState.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:30 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
import java.io.*;
import com.sun.j3d.utils.geometry.Primitive;
import com.sun.j3d.utils.scenegraph.io.retained.Controller;
import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.GroupState;
public class PrimitiveState extends GroupState {
protected int primflags;
public PrimitiveState( SymbolTableData symbol, Controller control ) {
super( symbol, control );
}
public void writeConstructorParams( DataOutput out ) throws IOException {
super.writeConstructorParams( out );
out.writeInt( ((Primitive)node).getPrimitiveFlags() );
}
public void readConstructorParams( DataInput in ) throws IOException {
super.readConstructorParams(in);
primflags = in.readInt();
}
public void buildGraph() {
super.buildGraph(); // This must be the last call in the method
}
/**
* Returns true if the groups children should be saved.
*
* This is overridden by 'black box' groups such a geometry primitives
*/
protected boolean processChildren() {
return false;
}
}
././@LongLink 0000000 0000000 0000000 00000000207 00000000000 011564 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/CylinderState.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000644 0000000 0000000 00000014055 10563126536 031451 0 ustar root root /*
* $RCSfile: CylinderState.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:30 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
import java.io.*;
import com.sun.j3d.utils.geometry.Cylinder;
import javax.media.j3d.SceneGraphObject;
import javax.media.j3d.Appearance;
import com.sun.j3d.utils.scenegraph.io.retained.Controller;
import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
public class CylinderState extends PrimitiveState {
private float radius=1f;
private float height=2f;
private int xdivision=15;
private int ydivision=1;
private int topAppearance;
private int bottomAppearance;
private int bodyAppearance;
public CylinderState( SymbolTableData symbol, Controller control ) {
super( symbol, control );
if (node!=null) {
bodyAppearance = control.getSymbolTable().addReference( ((Cylinder)node).getShape( Cylinder.BODY ).getAppearance() );
topAppearance = control.getSymbolTable().addReference( ((Cylinder)node).getShape( Cylinder.TOP ).getAppearance() );
bottomAppearance = control.getSymbolTable().addReference( ((Cylinder)node).getShape( Cylinder.BOTTOM ).getAppearance() );
}
}
public void writeObject( DataOutput out ) throws IOException {
super.writeObject( out );
out.writeInt( topAppearance );
out.writeInt( bodyAppearance );
out.writeInt( bottomAppearance );
}
public void readObject( DataInput in ) throws IOException {
super.readObject(in);
topAppearance = in.readInt();
bodyAppearance = in.readInt();
bottomAppearance = in.readInt();
}
public void writeConstructorParams( DataOutput out ) throws IOException {
super.writeConstructorParams( out );
out.writeFloat( ((Cylinder)node).getRadius() );
out.writeFloat( ((Cylinder)node).getHeight() );
out.writeInt( ((Cylinder)node).getXdivisions() );
out.writeInt( ((Cylinder)node).getYdivisions() );
}
public void readConstructorParams( DataInput in ) throws IOException {
super.readConstructorParams(in);
radius = in.readFloat();
height = in.readFloat();
xdivision = in.readInt();
ydivision = in.readInt();
}
public void buildGraph() {
if (bodyAppearance == topAppearance && bodyAppearance == bottomAppearance ) {
((Cylinder)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( bodyAppearance ));
} else {
((Cylinder)node).setAppearance( Cylinder.BODY, (Appearance)control.getSymbolTable().getJ3dNode( bodyAppearance ));
((Cylinder)node).setAppearance( Cylinder.TOP, (Appearance)control.getSymbolTable().getJ3dNode( topAppearance ));
((Cylinder)node).setAppearance( Cylinder.BOTTOM, (Appearance)control.getSymbolTable().getJ3dNode( bottomAppearance ));
}
super.buildGraph(); // This must be the last call in the method
}
public SceneGraphObject createNode( Class j3dClass ) {
// Create the node with a null appearance, we will add the appearance
// during build graph
Cylinder cylinder = (Cylinder)createNode( j3dClass, new Class[] {
Float.TYPE,
Float.TYPE,
Integer.TYPE,
Integer.TYPE,
Integer.TYPE,
Appearance.class },
new Object[] {
new Float( radius ),
new Float( height ),
new Integer( primflags ),
new Integer( xdivision ),
new Integer( ydivision ),
null } );
return cylinder;
}
protected javax.media.j3d.SceneGraphObject createNode() {
return new Cylinder( radius, height, primflags, xdivision, ydivision, null );
}
}
././@LongLink 0000000 0000000 0000000 00000000210 00000000000 011556 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ColorCubeState.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000644 0000000 0000000 00000006625 10563126536 031455 0 ustar root root /*
* $RCSfile: ColorCubeState.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:30 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
import java.io.*;
import com.sun.j3d.utils.geometry.ColorCube;
import javax.media.j3d.Shape3D;
import javax.media.j3d.SceneGraphObject;
import javax.vecmath.Color3f;
import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.Shape3DState;
import com.sun.j3d.utils.scenegraph.io.retained.Controller;
import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
public class ColorCubeState extends Shape3DState {
private double scale=1.0;
public ColorCubeState(SymbolTableData symbol,Controller control) {
super( symbol, control );
}
public void writeConstructorParams( DataOutput out ) throws IOException {
super.writeConstructorParams( out );
out.writeDouble( ((ColorCube)node).getScale() );
}
public void readConstructorParams( DataInput in ) throws IOException {
super.readConstructorParams(in);
scale = in.readDouble();
}
/**
* Returns true if the groups children should be saved.
*
* This is overridden by 'black box' groups such a geometry primitives
*/
protected boolean processChildren() {
return false;
}
public SceneGraphObject createNode( Class j3dClass ) {
Shape3D shape = (Shape3D) createNode( j3dClass, new Class[]{ Double.TYPE }, new Object[] { new Double(scale) } );
return shape;
}
protected javax.media.j3d.SceneGraphObject createNode() {
return new ColorCube( scale );
}
}
././@LongLink 0000000 0000000 0000000 00000000202 00000000000 011557 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/BoxState.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000644 0000000 0000000 00000015235 10563126536 031452 0 ustar root root /*
* $RCSfile: BoxState.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:30 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
import java.io.*;
import com.sun.j3d.utils.geometry.Box;
import javax.media.j3d.Appearance;
import javax.media.j3d.SceneGraphObject;
import javax.vecmath.Color3f;
import com.sun.j3d.utils.scenegraph.io.retained.Controller;
import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
public class BoxState extends PrimitiveState {
private float xdim;
private float ydim;
private float zdim;
private int frontAppearance;
private int backAppearance;
private int topAppearance;
private int bottomAppearance;
private int leftAppearance;
private int rightAppearance;
public BoxState( SymbolTableData symbol, Controller control ) {
super( symbol, control );
if (node!=null) {
frontAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.FRONT ).getAppearance() );
backAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.BACK ).getAppearance() );
topAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.TOP ).getAppearance() );
bottomAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.BOTTOM ).getAppearance() );
leftAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.LEFT ).getAppearance() );
rightAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.RIGHT ).getAppearance() );
}
}
public void writeObject( DataOutput out ) throws IOException {
super.writeObject( out );
out.writeInt( frontAppearance );
out.writeInt( backAppearance );
out.writeInt( topAppearance );
out.writeInt( bottomAppearance );
out.writeInt( leftAppearance );
out.writeInt( rightAppearance );
}
public void readObject( DataInput in ) throws IOException {
super.readObject(in);
frontAppearance = in.readInt();
backAppearance = in.readInt();
topAppearance = in.readInt();
bottomAppearance = in.readInt();
leftAppearance = in.readInt();
rightAppearance = in.readInt();
}
public void writeConstructorParams( DataOutput out ) throws IOException {
super.writeConstructorParams( out );
out.writeFloat( ((Box)node).getXdimension() );
out.writeFloat( ((Box)node).getYdimension() );
out.writeFloat( ((Box)node).getZdimension() );
}
public void readConstructorParams( DataInput in ) throws IOException {
super.readConstructorParams(in);
xdim = in.readFloat();
ydim = in.readFloat();
zdim = in.readFloat();
}
public void buildGraph() {
if (frontAppearance == backAppearance &&
frontAppearance == topAppearance &&
frontAppearance == bottomAppearance &&
frontAppearance == leftAppearance &&
frontAppearance == rightAppearance ) {
((Box)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( frontAppearance ));
} else {
((Box)node).setAppearance( Box.FRONT, (Appearance)control.getSymbolTable().getJ3dNode( frontAppearance ));
((Box)node).setAppearance( Box.BACK, (Appearance)control.getSymbolTable().getJ3dNode( backAppearance ));
((Box)node).setAppearance( Box.TOP, (Appearance)control.getSymbolTable().getJ3dNode( topAppearance ));
((Box)node).setAppearance( Box.BOTTOM, (Appearance)control.getSymbolTable().getJ3dNode( bottomAppearance ));
((Box)node).setAppearance( Box.LEFT, (Appearance)control.getSymbolTable().getJ3dNode( leftAppearance ));
((Box)node).setAppearance( Box.RIGHT, (Appearance)control.getSymbolTable().getJ3dNode( rightAppearance ));
}
super.buildGraph(); // This must be the last call in the method
}
public SceneGraphObject createNode( Class j3dClass ) {
Box box = (Box)super.createNode( j3dClass, new Class[] { Float.TYPE,
Float.TYPE,
Float.TYPE,
Integer.TYPE,
Appearance.class },
new Object[] { new Float( xdim ),
new Float( ydim ),
new Float( zdim ),
new Integer( primflags ),
null } );
return box;
}
protected javax.media.j3d.SceneGraphObject createNode() {
return new Box( xdim, ydim, zdim, primflags, null );
}
}
././@LongLink 0000000 0000000 0000000 00000000205 00000000000 011562 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/SphereState.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000644 0000000 0000000 00000011110 10563126536 031436 0 ustar root root /*
* $RCSfile: SphereState.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:30 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
import java.io.*;
import com.sun.j3d.utils.geometry.Sphere;
import javax.media.j3d.SceneGraphObject;
import javax.media.j3d.Appearance;
import com.sun.j3d.utils.scenegraph.io.retained.Controller;
import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
public class SphereState extends PrimitiveState {
private float radius;
private int divisions;
private int bodyAppearance;
public SphereState( SymbolTableData symbol, Controller control ) {
super( symbol, control );
if (node!=null) {
bodyAppearance = control.getSymbolTable().addReference( ((Sphere)node).getShape( Sphere.BODY ).getAppearance() );
}
}
public void writeObject( DataOutput out ) throws IOException {
super.writeObject( out );
out.writeInt( bodyAppearance );
}
public void readObject( DataInput in ) throws IOException {
super.readObject(in);
bodyAppearance = in.readInt();
}
public void writeConstructorParams( DataOutput out ) throws IOException {
super.writeConstructorParams( out );
out.writeFloat( ((Sphere)node).getRadius() );
out.writeInt( ((Sphere)node).getDivisions() );
}
public void readConstructorParams( DataInput in ) throws IOException {
super.readConstructorParams(in);
radius = in.readFloat();
divisions = in.readInt();
}
public void buildGraph() {
((Sphere)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( bodyAppearance ));
super.buildGraph(); // This must be the last call in the method
}
public SceneGraphObject createNode( Class j3dClass ) {
// Create the node with a null appearance, we will add the appearance
// during build graph
Sphere sphere = (Sphere)createNode( j3dClass, new Class[] {
Float.TYPE,
Integer.TYPE,
Integer.TYPE,
javax.media.j3d.Appearance.class },
new Object[] {
new Float( radius ),
new Integer( primflags ),
new Integer( divisions ),
null } );
return sphere;
}
protected javax.media.j3d.SceneGraphObject createNode() {
return new Sphere( radius, primflags, divisions, null );
}
}
././@LongLink 0000000 0000000 0000000 00000000205 00000000000 011562 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/Text2DState.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000644 0000000 0000000 00000012366 10563126536 031454 0 ustar root root /*
* $RCSfile: Text2DState.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:30 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
import java.io.*;
import com.sun.j3d.utils.geometry.Sphere;
import javax.media.j3d.SceneGraphObject;
import javax.media.j3d.Shape3D;
import javax.vecmath.Color3f;
import com.sun.j3d.utils.geometry.Text2D;
import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.LeafState;
import com.sun.j3d.utils.scenegraph.io.retained.Controller;
import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
public class Text2DState extends LeafState {
// Text2D is really a subclass of Shape3D, however we don't want
// the Shape3DState class to save the geometry or appearance data
// so this class is a subclass of LeafState and we handle
// the CollisionBounds in the read/write Object methods.
private String text;
private Color3f color;
private String fontName;
private int fontSize;
private int fontStyle;
public Text2DState(SymbolTableData symbol,Controller control) {
super( symbol, control );
if (node!=null) {
Text2D t = (Text2D)node;
text = t.getString();
color = t.getColor();
fontName = t.getFontName();
fontSize = t.getFontSize();
fontStyle = t.getFontStyle();
}
}
public void writeObject( DataOutput out ) throws IOException {
super.writeObject( out );
control.writeBounds( out, ((Shape3D)node).getCollisionBounds() );
}
public void readObject( DataInput in ) throws IOException {
super.readObject(in);
((Shape3D)node).setCollisionBounds( control.readBounds( in ));
}
public void writeConstructorParams( DataOutput out ) throws IOException {
super.writeConstructorParams( out );
out.writeUTF( text );
control.writeColor3f( out, color );
out.writeUTF( fontName );
out.writeInt( fontSize );
out.writeInt( fontStyle );
}
public void readConstructorParams( DataInput in ) throws IOException {
super.readConstructorParams(in);
text = in.readUTF();
color = control.readColor3f( in );
fontName = in.readUTF();
fontSize = in.readInt();
fontStyle = in.readInt();
}
public SceneGraphObject createNode( Class j3dClass ) {
// Create the node with a null appearance, we will add the appearance
// during build graph
Text2D text2D = (Text2D)createNode( j3dClass, new Class[] {
String.class,
Color3f.class,
String.class,
Integer.TYPE,
Integer.TYPE },
new Object[] {
text,
color,
fontName,
new Integer( fontSize ),
new Integer( fontStyle )
} );
return text2D;
}
protected javax.media.j3d.SceneGraphObject createNode() {
return new Text2D( text, color, fontName, fontSize, fontStyle );
}
}
././@LongLink 0000000 0000000 0000000 00000000165 00000000000 011567 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/ java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000755 0000000 0000000 00000000000 11240226647 031437 5 ustar root root ././@LongLink 0000000 0000000 0000000 00000000215 00000000000 011563 L ustar root root java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/SimpleUniverseState.java java3d-1.5.2+dfsg/j3d-core-utils/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d0000644 0000000 0000000 00000027176 10563126537 031462 0 ustar root root /*
* $RCSfile: SimpleUniverseState.java,v $
*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2007/02/09 17:20:31 $
* $State: Exp $
*/
package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.universe;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Enumeration;
import java.util.ArrayList;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.Transform3D;
import javax.media.j3d.HiResCoord;
import javax.media.j3d.Locale;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.vecmath.Matrix4d;
import com.sun.j3d.utils.universe.MultiTransformGroup;
import com.sun.j3d.utils.universe.ViewingPlatform;
import com.sun.j3d.utils.universe.ViewerAvatar;
import com.sun.j3d.utils.universe.PlatformGeometry;
import com.sun.j3d.utils.scenegraph.io.retained.Controller;
import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.SceneGraphObjectState;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ConfiguredUniverse;
public class SimpleUniverseState extends java.lang.Object {
private SimpleUniverse universe=null;
private Controller control;
private ArrayList localeBGs;
private int totalBGs=0;
private PlatformGeometryState platformGeom;
private ViewerAvatarState viewerAvatar;
/**
* Creates new SimpleUniverseState for writing.
*/
public SimpleUniverseState( ConfiguredUniverse universe, Controller control ) {
this.universe = universe;
this.control = control;
}
/**
* Creates new SimpleUniverseState for writing.
*/
public SimpleUniverseState( SimpleUniverse universe, Controller control ) {
this.universe = universe;
this.control = control;
}
/**
* Creates new SimpleUniverseState for reading.
*/
public SimpleUniverseState( Controller control ) {
this.control = control;
}
public void writeObject( DataOutput out ) throws IOException {
MultiTransformGroup mtg = universe.getViewingPlatform().getMultiTransformGroup();
int mtgSize = mtg.getNumTransforms();
out.writeInt( mtgSize );
// Store the matrix from each MTG transform
Transform3D trans = new Transform3D();
Matrix4d matrix = new Matrix4d();
for(int i=0; i