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@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 	 */
TriStrip()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 	 */
setListsOnly(boolean _bListsOnly)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 	 */
setCacheSize(int _cacheSize)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 	 */
setStitchStrips(boolean _bStitchStrips)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 	 */
setMinStripSize(int _minStripSize)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 	 */
generateStrips(int[] in_indices)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 
remapIndices(int[] indices, int numVerts)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 
remapArrays(float[] vertexBuffer, int vertexSize, int[] indices)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