blob: aa84d5cb110b0038baa04c582fa768763ab124d7 [file] [log] [blame]
/*
* Copyright (c) 2003-2009 jMonkeyEngine
* All rights reserved.
*
* 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jme3tools.converters.model.strip;
import java.util.Arrays;
/**
* 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>
*/
public class TriStrip {
public static final int CACHESIZE_GEFORCE1_2 = 16;
public static final int CACHESIZE_GEFORCE3 = 24;
int cacheSize = CACHESIZE_GEFORCE1_2;
boolean bStitchStrips = true;
int minStripSize = 0;
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
IntVec tempIndices = new IntVec();
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];
}
StripInfoVec tempStrips = new StripInfoVec();
FaceInfoVec tempFaces = new FaceInfoVec();
Stripifier stripifier = new Stripifier();
//do actual stripification
stripifier.stripify(tempIndices, cacheSize, minStripSize, maxIndex, tempStrips, tempFaces);
//stitch strips together
IntVec stripIndices = new IntVec();
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.at(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.at(i).m_faces.size(); j++) {
//degenerates are of no use with lists
if (!Stripifier.isDegenerate(tempStrips.at(i).m_faces.at(j))) {
primGroupArray[0].indices[indexCtr++] = tempStrips.at(i).m_faces.at(j).m_v0;
primGroupArray[0].indices[indexCtr++] = tempStrips.at(i).m_faces.at(j).m_v1;
primGroupArray[0].indices[indexCtr++] = tempStrips.at(i).m_faces.at(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.at(i).m_v0;
primGroupArray[0].indices[indexCtr++] = tempFaces.at(i).m_v1;
primGroupArray[0].indices[indexCtr++] = tempFaces.at(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()
//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.at(i).m_v0;
primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces.at(i).m_v1;
primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces.at(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);
}
}