/**
* Copyright (c) 2003-2009, Xith3D Project Group all rights reserved.
*
* Portions based on the Java3D interface, Copyright by Sun Microsystems.
* Many thanks to the developers of Java3D and Sun Microsystems for their
* innovation and design.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions 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 the 'Xith3D Project Group' nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A
* RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE
*/
package org.xith3d.utility.geometry.nvtristrip;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Vector;
/**
* To use, call generateStrips method, passing your triangle index list and
* then construct geometry/render resulting PrimitiveGroup objects.
* Features:
* <ul>
* <li>generates strips from arbitrary geometry.
* <li>flexibly optimizes for post TnL vertex caches (16 on GeForce1/2, 24 on GeForce3).
* <li>can stitch together strips using degenerate triangles, or not.
* <li>can output lists instead of strips.
* <li>can optionally throw excessively small strips into a list instead.
* <li>can remap indices to improve spatial locality in your vertex buffers.
* </ul>
* On cache sizes: Note that it's better to UNDERESTIMATE the cache size
* instead of OVERESTIMATING. So, if you're targetting GeForce1, 2, and 3, be
* conservative and use the GeForce1_2 cache size, NOT the GeForce3 cache size.
* This will make sure you don't "blow" the cache of the GeForce1 and 2. Also
* note that the cache size you specify is the "actual" cache size, not the
* "effective" cache size you may have heard about. This is 16 for GeForce1 and 2,
* and 24 for GeForce3.
*
* Credit goes to Curtis Beeson and Joe Demers for the basis for this
* stripifier and to Jason Regier and Jon Stone at Blizzard for providing a
* much cleaner version of CreateStrips().
*
* Ported to java by Artur Biesiadowski <abies@pg.gda.pl>
*
* @author YVG
*/
public class TriStrip
{
public static final int CACHESIZE_GEFORCE1_2 = 16;
public static final int CACHESIZE_GEFORCE3 = 24;
private int cacheSize = CACHESIZE_GEFORCE1_2;
private boolean bStitchStrips = true;
private int minStripSize = 0;
private boolean bListsOnly = false;
/**
*
*/
public TriStrip()
{
super();
}
/**
* If set to true, will return an optimized list, with no strips at all.
* Default value: false
*/
public void setListsOnly( boolean _bListsOnly )
{
bListsOnly = _bListsOnly;
}
/**
* Sets the cache size which the stripfier uses to optimize the data.
* Controls the length of the generated individual strips. This is the
* "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2 You may
* want to play around with this number to tweak performance. Default
* value: 16
*/
public void setCacheSize( int _cacheSize )
{
cacheSize = _cacheSize;
}
/**
* bool to indicate whether to stitch together strips into one huge strip
* or not. If set to true, you'll get back one huge strip stitched together
* using degenerate triangles. If set to false, you'll get back a large
* number of separate strips. Default value: true
*/
public void setStitchStrips( boolean _bStitchStrips )
{
bStitchStrips = _bStitchStrips;
}
/**
* Sets the minimum acceptable size for a strip, in triangles. All strips
* generated which are shorter than this will be thrown into one big,
* separate list. Default value: 0
*/
public void setMinStripSize( int _minStripSize )
{
minStripSize = _minStripSize;
}
/**
* @param in_indices
* input index list, the indices you would use to render
* @return array of optimized/stripified PrimitiveGroups
*/
public PrimitiveGroup[] generateStrips( int[] in_indices )
{
int numGroups = 0;
PrimitiveGroup[] primGroups;
//put data in format that the stripifier likes
Vector< Integer > tempIndices = new Vector< Integer >();
int maxIndex = 0;
for ( int i = 0; i < in_indices.length; i++ )
{
tempIndices.add( in_indices[ i ] );
if ( in_indices[ i ] > maxIndex )
maxIndex = in_indices[ i ];
}
ArrayList< StripInfo > tempStrips = new ArrayList< StripInfo >();
ArrayList< FaceInfo > tempFaces = new ArrayList< FaceInfo >();
Stripifier stripifier = new Stripifier();
//do actual stripification
stripifier.stripify( tempIndices, cacheSize, minStripSize, maxIndex, tempStrips, tempFaces );
//stitch strips together
Vector< Integer > stripIndices = new Vector< Integer >();
int numSeparateStrips = 0;
if ( bListsOnly )
{
//if we're outputting only lists, we're done
numGroups = 1;
primGroups = new PrimitiveGroup[ numGroups ];
primGroups[ 0 ] = new PrimitiveGroup();
PrimitiveGroup[] primGroupArray = primGroups;
//count the total number of indices
int numIndices = 0;
for ( int i = 0; i < tempStrips.size(); i++ )
{
numIndices += tempStrips.get( i ).m_faces.size() * 3;
}
//add in the list
numIndices += tempFaces.size() * 3;
primGroupArray[ 0 ].type = PrimitiveGroup.PT_LIST;
primGroupArray[ 0 ].indices = new int[ numIndices ];
primGroupArray[ 0 ].numIndices = numIndices;
//do strips
int indexCtr = 0;
for ( int i = 0; i < tempStrips.size(); i++ )
{
for ( int j = 0; j < tempStrips.get( i ).m_faces.size(); j++ )
{
//degenerates are of no use with lists
if ( !Stripifier.isDegenerate( tempStrips.get( i ).m_faces.get( j ) ) )
{
primGroupArray[ 0 ].indices[ indexCtr++ ] = tempStrips.get( i ).m_faces.get( j ).m_v0;
primGroupArray[ 0 ].indices[ indexCtr++ ] = tempStrips.get( i ).m_faces.get( j ).m_v1;
primGroupArray[ 0 ].indices[ indexCtr++ ] = tempStrips.get( i ).m_faces.get( j ).m_v2;
}
else
{
//we've removed a tri, reduce the number of indices
primGroupArray[ 0 ].numIndices -= 3;
}
}
}
//do lists
for ( int i = 0; i < tempFaces.size(); i++ )
{
primGroupArray[ 0 ].indices[ indexCtr++ ] = tempFaces.get( i ).m_v0;
primGroupArray[ 0 ].indices[ indexCtr++ ] = tempFaces.get( i ).m_v1;
primGroupArray[ 0 ].indices[ indexCtr++ ] = tempFaces.get( i ).m_v2;
}
}
else
{
numSeparateStrips = stripifier.createStrips( tempStrips, stripIndices, bStitchStrips );
//if we're stitching strips together, we better get back only one
// strip from CreateStrips()
assert ( ( bStitchStrips && ( numSeparateStrips == 1 ) ) || !bStitchStrips );
//convert to output format
numGroups = numSeparateStrips; //for the strips
if ( tempFaces.size() != 0 )
numGroups++; //we've got a list as well, increment
primGroups = new PrimitiveGroup[ numGroups ];
for ( int i = 0; i < primGroups.length; i++ )
{
primGroups[ i ] = new PrimitiveGroup();
}
PrimitiveGroup[] primGroupArray = primGroups;
//first, the strips
int startingLoc = 0;
for ( int stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++ )
{
int stripLength = 0;
if ( !bStitchStrips )
{
int i;
//if we've got multiple strips, we need to figure out the
// correct length
for ( i = startingLoc; i < stripIndices.size(); i++ )
{
if ( stripIndices.get( i ) == -1 )
break;
}
stripLength = i - startingLoc;
}
else
stripLength = stripIndices.size();
primGroupArray[ stripCtr ].type = PrimitiveGroup.PT_STRIP;
primGroupArray[ stripCtr ].indices = new int[ stripLength ];
primGroupArray[ stripCtr ].numIndices = stripLength;
int indexCtr = 0;
for ( int i = startingLoc; i < stripLength + startingLoc; i++ )
primGroupArray[ stripCtr ].indices[ indexCtr++ ] = stripIndices.get( i );
//we add 1 to account for the -1 separating strips
//this doesn't break the stitched case since we'll exit the
// loop
startingLoc += stripLength + 1;
}
//next, the list
if ( tempFaces.size() != 0 )
{
int faceGroupLoc = numGroups - 1; //the face group is the last
// one
primGroupArray[ faceGroupLoc ].type = PrimitiveGroup.PT_LIST;
primGroupArray[ faceGroupLoc ].indices = new int[ tempFaces.size() * 3 ];
primGroupArray[ faceGroupLoc ].numIndices = tempFaces.size() * 3;
int indexCtr = 0;
for ( int i = 0; i < tempFaces.size(); i++ )
{
primGroupArray[ faceGroupLoc ].indices[ indexCtr++ ] = tempFaces.get( i ).m_v0;
primGroupArray[ faceGroupLoc ].indices[ indexCtr++ ] = tempFaces.get( i ).m_v1;
primGroupArray[ faceGroupLoc ].indices[ indexCtr++ ] = tempFaces.get( i ).m_v2;
}
}
}
return primGroups;
}
/**
* Function to remap your indices to improve spatial locality in your
* vertex buffer.
*
* in_primGroups: array of PrimitiveGroups you want remapped numGroups:
* number of entries in in_primGroups numVerts: number of vertices in your
* vertex buffer, also can be thought of as the range of acceptable values
* for indices in your primitive groups. remappedGroups: array of remapped
* PrimitiveGroups
*
* Note that, according to the remapping handed back to you, you must
* reorder your vertex buffer.
*/
public static int[] remapIndices( int[] indices, int numVerts )
{
int[] indexCache = new int[ numVerts ];
Arrays.fill( indexCache, -1 );
int numIndices = indices.length;
int[] remappedIndices = new int[ numIndices ];
int indexCtr = 0;
for ( int j = 0; j < numIndices; j++ )
{
int cachedIndex = indexCache[ indices[ j ] ];
if ( cachedIndex == -1 ) //we haven't seen this index before
{
//point to "last" vertex in VB
remappedIndices[ j ] = indexCtr;
//add to index cache, increment
indexCache[ indices[ j ] ] = indexCtr++;
}
else
{
//we've seen this index before
remappedIndices[ j ] = cachedIndex;
}
}
return remappedIndices;
}
public static void remapArrays( float[] vertexBuffer, int vertexSize, int[] indices )
{
int[] remapped = remapIndices( indices, vertexBuffer.length / vertexSize );
float[] bufferCopy = vertexBuffer.clone();
for ( int i = 0; i < remapped.length; i++ )
{
int from = indices[ i ] * vertexSize;
int to = remapped[ i ] * vertexSize;
for ( int j = 0; j < vertexSize; j++ )
{
vertexBuffer[ to + j ] = bufferCopy[ from + j ];
}
}
System.arraycopy( remapped, 0, indices, 0, indices.length );
}
}