1 /* 2 * Copyright (c) 2003-2009 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package jme3tools.converters.model.strip; 34 35 import java.util.Arrays; 36 37 /** 38 * To use, call generateStrips method, passing your triangle index list and 39 * then construct geometry/render resulting PrimitiveGroup objects. 40 * Features: 41 * <ul> 42 * <li>generates strips from arbitrary geometry. 43 * <li>flexibly optimizes for post TnL vertex caches (16 on GeForce1/2, 24 on GeForce3). 44 * <li>can stitch together strips using degenerate triangles, or not. 45 * <li>can output lists instead of strips. 46 * <li>can optionally throw excessively small strips into a list instead. 47 * <li>can remap indices to improve spatial locality in your vertex buffers. 48 * </ul> 49 * On cache sizes: Note that it's better to UNDERESTIMATE the cache size 50 * instead of OVERESTIMATING. So, if you're targetting GeForce1, 2, and 3, be 51 * conservative and use the GeForce1_2 cache size, NOT the GeForce3 cache size. 52 * This will make sure you don't "blow" the cache of the GeForce1 and 2. Also 53 * note that the cache size you specify is the "actual" cache size, not the 54 * "effective" cache size you may have heard about. This is 16 for GeForce1 and 2, 55 * and 24 for GeForce3. 56 * 57 * Credit goes to Curtis Beeson and Joe Demers for the basis for this 58 * stripifier and to Jason Regier and Jon Stone at Blizzard for providing a 59 * much cleaner version of CreateStrips(). 60 * 61 * Ported to java by Artur Biesiadowski <abies (at) pg.gda.pl> 62 */ 63 public class TriStrip { 64 65 public static final int CACHESIZE_GEFORCE1_2 = 16; 66 public static final int CACHESIZE_GEFORCE3 = 24; 67 68 int cacheSize = CACHESIZE_GEFORCE1_2; 69 boolean bStitchStrips = true; 70 int minStripSize = 0; 71 boolean bListsOnly = false; 72 73 /** 74 * 75 */ 76 public TriStrip() { 77 super(); 78 } 79 80 /** 81 * If set to true, will return an optimized list, with no strips at all. 82 * Default value: false 83 */ 84 public void setListsOnly(boolean _bListsOnly) { 85 bListsOnly = _bListsOnly; 86 } 87 88 /** 89 * Sets the cache size which the stripfier uses to optimize the data. 90 * Controls the length of the generated individual strips. This is the 91 * "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2 You may 92 * want to play around with this number to tweak performance. Default 93 * value: 16 94 */ 95 public void setCacheSize(int _cacheSize) { 96 cacheSize = _cacheSize; 97 } 98 99 /** 100 * bool to indicate whether to stitch together strips into one huge strip 101 * or not. If set to true, you'll get back one huge strip stitched together 102 * using degenerate triangles. If set to false, you'll get back a large 103 * number of separate strips. Default value: true 104 */ 105 public void setStitchStrips(boolean _bStitchStrips) { 106 bStitchStrips = _bStitchStrips; 107 } 108 109 /** 110 * Sets the minimum acceptable size for a strip, in triangles. All strips 111 * generated which are shorter than this will be thrown into one big, 112 * separate list. Default value: 0 113 */ 114 public void setMinStripSize(int _minStripSize) { 115 minStripSize = _minStripSize; 116 } 117 118 /** 119 * @param in_indices 120 * input index list, the indices you would use to render 121 * @return array of optimized/stripified PrimitiveGroups 122 */ 123 public PrimitiveGroup[] generateStrips(int[] in_indices) { 124 int numGroups = 0; 125 PrimitiveGroup[] primGroups; 126 //put data in format that the stripifier likes 127 IntVec tempIndices = new IntVec(); 128 int maxIndex = 0; 129 130 for (int i = 0; i < in_indices.length; i++) { 131 tempIndices.add(in_indices[i]); 132 if (in_indices[i] > maxIndex) 133 maxIndex = in_indices[i]; 134 } 135 136 StripInfoVec tempStrips = new StripInfoVec(); 137 FaceInfoVec tempFaces = new FaceInfoVec(); 138 139 Stripifier stripifier = new Stripifier(); 140 141 //do actual stripification 142 stripifier.stripify(tempIndices, cacheSize, minStripSize, maxIndex, tempStrips, tempFaces); 143 144 //stitch strips together 145 IntVec stripIndices = new IntVec(); 146 int numSeparateStrips = 0; 147 148 if (bListsOnly) { 149 //if we're outputting only lists, we're done 150 numGroups = 1; 151 primGroups = new PrimitiveGroup[numGroups]; 152 primGroups[0] = new PrimitiveGroup(); 153 PrimitiveGroup[] primGroupArray = primGroups; 154 155 //count the total number of indices 156 int numIndices = 0; 157 for (int i = 0; i < tempStrips.size(); i++) { 158 numIndices += tempStrips.at(i).m_faces.size() * 3; 159 } 160 161 //add in the list 162 numIndices += tempFaces.size() * 3; 163 164 primGroupArray[0].type = PrimitiveGroup.PT_LIST; 165 primGroupArray[0].indices = new int[numIndices]; 166 primGroupArray[0].numIndices = numIndices; 167 168 //do strips 169 int indexCtr = 0; 170 for (int i = 0; i < tempStrips.size(); i++) { 171 for (int j = 0; j < tempStrips.at(i).m_faces.size(); j++) { 172 //degenerates are of no use with lists 173 if (!Stripifier.isDegenerate(tempStrips.at(i).m_faces.at(j))) { 174 primGroupArray[0].indices[indexCtr++] = tempStrips.at(i).m_faces.at(j).m_v0; 175 primGroupArray[0].indices[indexCtr++] = tempStrips.at(i).m_faces.at(j).m_v1; 176 primGroupArray[0].indices[indexCtr++] = tempStrips.at(i).m_faces.at(j).m_v2; 177 } else { 178 //we've removed a tri, reduce the number of indices 179 primGroupArray[0].numIndices -= 3; 180 } 181 } 182 } 183 184 //do lists 185 for (int i = 0; i < tempFaces.size(); i++) { 186 primGroupArray[0].indices[indexCtr++] = tempFaces.at(i).m_v0; 187 primGroupArray[0].indices[indexCtr++] = tempFaces.at(i).m_v1; 188 primGroupArray[0].indices[indexCtr++] = tempFaces.at(i).m_v2; 189 } 190 } else { 191 numSeparateStrips = stripifier.createStrips(tempStrips, stripIndices, bStitchStrips); 192 193 //if we're stitching strips together, we better get back only one 194 // strip from CreateStrips() 195 196 //convert to output format 197 numGroups = numSeparateStrips; //for the strips 198 if (tempFaces.size() != 0) 199 numGroups++; //we've got a list as well, increment 200 primGroups = new PrimitiveGroup[numGroups]; 201 for (int i = 0; i < primGroups.length; i++) { 202 primGroups[i] = new PrimitiveGroup(); 203 } 204 205 PrimitiveGroup[] primGroupArray = primGroups; 206 207 //first, the strips 208 int startingLoc = 0; 209 for (int stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++) { 210 int stripLength = 0; 211 212 if (!bStitchStrips) { 213 int i; 214 //if we've got multiple strips, we need to figure out the 215 // correct length 216 for (i = startingLoc; i < stripIndices.size(); i++) { 217 if (stripIndices.get(i) == -1) 218 break; 219 } 220 221 stripLength = i - startingLoc; 222 } else 223 stripLength = stripIndices.size(); 224 225 primGroupArray[stripCtr].type = PrimitiveGroup.PT_STRIP; 226 primGroupArray[stripCtr].indices = new int[stripLength]; 227 primGroupArray[stripCtr].numIndices = stripLength; 228 229 int indexCtr = 0; 230 for (int i = startingLoc; i < stripLength + startingLoc; i++) 231 primGroupArray[stripCtr].indices[indexCtr++] = stripIndices.get(i); 232 233 //we add 1 to account for the -1 separating strips 234 //this doesn't break the stitched case since we'll exit the 235 // loop 236 startingLoc += stripLength + 1; 237 } 238 239 //next, the list 240 if (tempFaces.size() != 0) { 241 int faceGroupLoc = numGroups - 1; //the face group is the last 242 // one 243 primGroupArray[faceGroupLoc].type = PrimitiveGroup.PT_LIST; 244 primGroupArray[faceGroupLoc].indices = new int[tempFaces.size() * 3]; 245 primGroupArray[faceGroupLoc].numIndices = tempFaces.size() * 3; 246 int indexCtr = 0; 247 for (int i = 0; i < tempFaces.size(); i++) { 248 primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces.at(i).m_v0; 249 primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces.at(i).m_v1; 250 primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces.at(i).m_v2; 251 } 252 } 253 } 254 return primGroups; 255 } 256 257 /** 258 * Function to remap your indices to improve spatial locality in your 259 * vertex buffer. 260 * 261 * in_primGroups: array of PrimitiveGroups you want remapped numGroups: 262 * number of entries in in_primGroups numVerts: number of vertices in your 263 * vertex buffer, also can be thought of as the range of acceptable values 264 * for indices in your primitive groups. remappedGroups: array of remapped 265 * PrimitiveGroups 266 * 267 * Note that, according to the remapping handed back to you, you must 268 * reorder your vertex buffer. 269 * 270 */ 271 272 public static int[] remapIndices(int[] indices, int numVerts) { 273 int[] indexCache = new int[numVerts]; 274 Arrays.fill(indexCache, -1); 275 276 int numIndices = indices.length; 277 int[] remappedIndices = new int[numIndices]; 278 int indexCtr = 0; 279 for (int j = 0; j < numIndices; j++) { 280 int cachedIndex = indexCache[indices[j]]; 281 if (cachedIndex == -1) //we haven't seen this index before 282 { 283 //point to "last" vertex in VB 284 remappedIndices[j] = indexCtr; 285 286 //add to index cache, increment 287 indexCache[indices[j]] = indexCtr++; 288 } else { 289 //we've seen this index before 290 remappedIndices[j] = cachedIndex; 291 } 292 } 293 294 return remappedIndices; 295 } 296 297 public static void remapArrays(float[] vertexBuffer, int vertexSize, int[] indices) { 298 int[] remapped = remapIndices(indices, vertexBuffer.length / vertexSize); 299 float[] bufferCopy = vertexBuffer.clone(); 300 for (int i = 0; i < remapped.length; i++) { 301 int from = indices[i] * vertexSize; 302 int to = remapped[i] * vertexSize; 303 for (int j = 0; j < vertexSize; j++) { 304 vertexBuffer[to + j] = bufferCopy[from + j]; 305 } 306 } 307 308 System.arraycopy(remapped, 0, indices, 0, indices.length); 309 } 310 311 } 312