nvtristrip-ruby 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README ADDED
@@ -0,0 +1,50 @@
1
+
2
+
3
+ = NvTriStrip: Triangle stripification library
4
+
5
+ NvTriStrip provides a Ruby interface for Nvidia's Triangle stripification
6
+ library.
7
+
8
+ Original NvTriStrip is available from http://developer.nvidia.com ,
9
+ I've slightly modified the lib to fix compiling on various platforms.
10
+
11
+ == Features
12
+
13
+ All features and shortcomings of Nvidia's library are (hopefully) preserved.
14
+
15
+ Namely, features
16
+ * Makes trianglestrips out of triangle lists
17
+ * Optimizes trianglestrips for good cache use
18
+ * Optionally stitches separate strips with degenerates
19
+ * Can throw small strips into a list instead
20
+ * Remaps indices to improve spatial locality in your vertex buffer
21
+
22
+ Some shortcomings
23
+ * Limited geometry to 65k vertices (But that also has some practical value)
24
+ * Uses global state to control some options (but should be otherwise reentrant)
25
+ * Ruby version has to do some back and forth copying of the indices
26
+
27
+ == Installation
28
+
29
+ With rubygems:
30
+ gem install nvtristrip-ruby
31
+
32
+ Otherwise:
33
+ ruby setup.rb
34
+
35
+ == Usage
36
+
37
+ require 'nvtristrip'
38
+
39
+ my_triangle_indices=[0,1,2,1,2,3] # Two triangles
40
+ validate=true
41
+
42
+ stripified_indices=NvTriStrip::generate_strips my_triangle_indices, validate
43
+
44
+ # And now stripifiedIndices is an Array of NvTriStrip::PrimitiveGroup objects, which are Enumerable.
45
+
46
+ remapped_indices=NvTriStrip::remap_indices stripified_indices, 3
47
+
48
+ # And now remappedIndices is an array of PrimitiveGroups objects with indices
49
+ # remapped for better spatial locality. With this information you should remap your vertex buffer.
50
+
data/README.nvtristrip ADDED
@@ -0,0 +1,32 @@
1
+ README for NvTriStrip, library version
2
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+
4
+ To use:
5
+ -#include "NvTriStrip.h"
6
+ -put nvtristrip.lib in your library path (the pragma in nvtristrip.h will automatically look for the library).
7
+
8
+ Check out NvTriStrip.h for the interface.
9
+
10
+ See the StripTest source code (in function LoadXFileStripped) for an example of using the library.
11
+
12
+ Features:
13
+ -generates strips from arbitrary geometry.
14
+ -flexibly optimizes for post TnL vertex caches (16 on GeForce1/2, 24 on GeForce3).
15
+ -can stitch together strips using degenerate triangles, or not.
16
+ -can output lists instead of strips.
17
+ -can optionally throw excessively small strips into a list instead.
18
+ -can remap indices to improve spatial locality in your vertex buffers.
19
+
20
+ On cache sizes:
21
+ Note that it's better to UNDERESTIMATE the cache size instead of OVERESTIMATING.
22
+ So, if you're targetting GeForce1, 2, and 3, be conservative and use the GeForce1_2 cache
23
+ size, NOT the GeForce3 cache size.
24
+ This will make sure you don't "blow" the cache of the GeForce1 and 2.
25
+ Also note that the cache size you specify is the "actual" cache size, not the "effective"
26
+ cache size you may have heard about. This is 16 for GeForce1 and 2, and 24 for GeForce3.
27
+
28
+ Credit goes to Curtis Beeson and Joe Demers for the basis for this stripifier and to Jason Regier and
29
+ Jon Stone at Blizzard for providing a much cleaner version of CreateStrips().
30
+
31
+ Questions/comments email cem@nvidia.com
32
+
data/Rakefile ADDED
@@ -0,0 +1,76 @@
1
+
2
+ require 'rubygems'
3
+ require 'rake/gempackagetask'
4
+
5
+ require 'rake/clean'
6
+ require 'rake/testtask'
7
+ require 'rake/rdoctask'
8
+
9
+ PKG_VERSION="0.5.0"
10
+
11
+ file "ext/Makefile" => [ "ext/extconf.rb"] do
12
+ Dir.chdir "ext" do
13
+ ruby "extconf.rb"
14
+ end
15
+ end
16
+
17
+ EXT_SO="ext/nvtristrip.#{Config::CONFIG['DLEXT']}"
18
+
19
+ file EXT_SO => FileList[
20
+ "ext/*.cpp",
21
+ "ext/*.h",
22
+ "ext/Makefile",
23
+ ] do
24
+
25
+ Dir.chdir "ext" do
26
+ sh "make"
27
+ end
28
+ cp EXT_SO, "lib"
29
+
30
+ end
31
+
32
+ task :default => :build
33
+
34
+ desc "Build the extension"
35
+ task :build => [EXT_SO]
36
+
37
+
38
+ spec=Gem::Specification.new do |s|
39
+ s.name="nvtristrip-ruby"
40
+ s.author="Mikko Lehtonen"
41
+ s.version=PKG_VERSION
42
+ s.summary="Interface for NvTriStrip triangle stripification library."
43
+ s.platform=Gem::Platform::RUBY
44
+ s.has_rdoc=true
45
+ s.extra_rdoc_files = ["README","TODO","ext/RbTriStrip.cpp"]
46
+ s.files=Dir.glob("{test,lib}/**/*")+
47
+ Dir.glob("ext/**/*.{cpp,h,rb}")+
48
+ ["Rakefile","setup.rb","README.nvtristrip", "README"]
49
+
50
+ s.require_path="lib"
51
+ s.test_files=Dir.glob("test/test_*.rb")
52
+ s.extensions=FileList["ext/extconf.rb"].to_a
53
+ s.homepage="http://nvtristrip.rubyforge.org"
54
+ s.rubyforge_project="nvtristrip"
55
+ end
56
+
57
+ Rake::GemPackageTask.new(spec) do |pkg|
58
+
59
+ end
60
+
61
+
62
+ Rake::TestTask.new do |t|
63
+ t.libs << "test"
64
+ t.test_files=FileList['test/test*.rb']
65
+ t.verbose=true
66
+ end
67
+
68
+ Rake::RDocTask.new do |r|
69
+ r.rdoc_dir='doc/rdoc'
70
+ # r.options
71
+ r.rdoc_files.add ['README','TODO', 'ext/RbTriStrip.cpp']
72
+ end
73
+
74
+
75
+ CLEAN.include ['**/*.o', '**/*.log', "lib/"+File.basename(EXT_SO), EXT_SO]
76
+
data/TODO ADDED
@@ -0,0 +1,7 @@
1
+
2
+ * Improve test-suite
3
+ * Convenience function for remapping vertexbuffers?
4
+ * Maybe try to modify nvtristrip lib to use the ruby arrays directly, so I wouldn't have to
5
+ copy the indices back and forth.
6
+ * Clean up some of the code and improve documentation
7
+ * Win32 precompiled gem
@@ -0,0 +1,501 @@
1
+
2
+ #include "NvTriStripObjects.h"
3
+ #include "NvTriStrip.h"
4
+
5
+ ////////////////////////////////////////////////////////////////////////////////////////
6
+ //private data
7
+ static unsigned int cacheSize = CACHESIZE_GEFORCE1_2;
8
+ static bool bStitchStrips = true;
9
+ static unsigned int minStripSize = 0;
10
+ static bool bListsOnly = false;
11
+ static unsigned int restartVal = 0;
12
+ static bool bRestart = false;
13
+
14
+ void EnableRestart(const unsigned int _restartVal)
15
+ {
16
+ bRestart = true;
17
+ restartVal = _restartVal;
18
+ }
19
+
20
+ void DisableRestart()
21
+ {
22
+ bRestart = false;
23
+ }
24
+
25
+ ////////////////////////////////////////////////////////////////////////////////////////
26
+ // SetListsOnly()
27
+ //
28
+ // If set to true, will return an optimized list, with no strips at all.
29
+ //
30
+ // Default value: false
31
+ //
32
+ void SetListsOnly(const bool _bListsOnly)
33
+ {
34
+ bListsOnly = _bListsOnly;
35
+ }
36
+
37
+ ////////////////////////////////////////////////////////////////////////////////////////
38
+ // SetCacheSize()
39
+ //
40
+ // Sets the cache size which the stripfier uses to optimize the data.
41
+ // Controls the length of the generated individual strips.
42
+ // This is the "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2
43
+ // You may want to play around with this number to tweak performance.
44
+ //
45
+ // Default value: 16
46
+ //
47
+ void SetCacheSize(const unsigned int _cacheSize)
48
+ {
49
+ cacheSize = _cacheSize;
50
+ }
51
+
52
+
53
+ ////////////////////////////////////////////////////////////////////////////////////////
54
+ // SetStitchStrips()
55
+ //
56
+ // bool to indicate whether to stitch together strips into one huge strip or not.
57
+ // If set to true, you'll get back one huge strip stitched together using degenerate
58
+ // triangles.
59
+ // If set to false, you'll get back a large number of separate strips.
60
+ //
61
+ // Default value: true
62
+ //
63
+ void SetStitchStrips(const bool _bStitchStrips)
64
+ {
65
+ bStitchStrips = _bStitchStrips;
66
+ }
67
+
68
+
69
+ ////////////////////////////////////////////////////////////////////////////////////////
70
+ // SetMinStripSize()
71
+ //
72
+ // Sets the minimum acceptable size for a strip, in triangles.
73
+ // All strips generated which are shorter than this will be thrown into one big, separate list.
74
+ //
75
+ // Default value: 0
76
+ //
77
+ void SetMinStripSize(const unsigned int _minStripSize)
78
+ {
79
+ minStripSize = _minStripSize;
80
+ }
81
+
82
+
83
+ ////////////////////////////////////////////////////////////////////////////////////////
84
+ //Cleanup strips / faces, used by generatestrips
85
+ void Cleanup(NvStripInfoVec& tempStrips, NvFaceInfoVec& tempFaces)
86
+ {
87
+ //delete strips
88
+ int i;
89
+ for(i = 0; i < tempStrips.size(); i++)
90
+ {
91
+ for(int j = 0; j < tempStrips[i]->m_faces.size(); j++)
92
+ {
93
+ delete tempStrips[i]->m_faces[j];
94
+ tempStrips[i]->m_faces[j] = NULL;
95
+ }
96
+ tempStrips[i]->m_faces.resize(0);
97
+ delete tempStrips[i];
98
+ tempStrips[i] = NULL;
99
+ }
100
+
101
+ //delete faces
102
+ for(i = 0; i < tempFaces.size(); i++)
103
+ {
104
+ delete tempFaces[i];
105
+ tempFaces[i] = NULL;
106
+ }
107
+ }
108
+
109
+
110
+ ////////////////////////////////////////////////////////////////////////////////////////
111
+ //SameTriangle()
112
+ //
113
+ //Returns true if the two triangles defined by firstTri and secondTri are the same
114
+ // The "same" is defined in this case as having the same indices with the same winding order
115
+ //
116
+ bool SameTriangle(unsigned short firstTri0, unsigned short firstTri1, unsigned short firstTri2,
117
+ unsigned short secondTri0, unsigned short secondTri1, unsigned short secondTri2)
118
+ {
119
+ bool isSame = false;
120
+
121
+ if (firstTri0 == secondTri0)
122
+ {
123
+ if (firstTri1 == secondTri1)
124
+ {
125
+ if (firstTri2 == secondTri2)
126
+ isSame = true;
127
+ }
128
+ }
129
+ else if (firstTri0 == secondTri1)
130
+ {
131
+ if (firstTri1 == secondTri2)
132
+ {
133
+ if (firstTri2 == secondTri0)
134
+ isSame = true;
135
+ }
136
+ }
137
+ else if (firstTri0 == secondTri2)
138
+ {
139
+ if (firstTri1 == secondTri0)
140
+ {
141
+ if (firstTri2 == secondTri1)
142
+ isSame = true;
143
+ }
144
+ }
145
+
146
+ return isSame;
147
+ }
148
+
149
+
150
+ bool TestTriangle(const unsigned short v0, const unsigned short v1, const unsigned short v2, const std::vector<NvFaceInfo>* in_bins, const int NUMBINS)
151
+ {
152
+ //hash this triangle
153
+ bool isLegit = false;
154
+ int ctr = v0 % NUMBINS;
155
+ for (int k = 0; k < in_bins[ctr].size(); ++k)
156
+ {
157
+ //check triangles in this bin
158
+ if (SameTriangle(in_bins[ctr][k].m_v0, in_bins[ctr][k].m_v1, in_bins[ctr][k].m_v2,
159
+ v0, v1, v2))
160
+ {
161
+ isLegit = true;
162
+ break;
163
+ }
164
+ }
165
+ if (!isLegit)
166
+ {
167
+ ctr = v1 % NUMBINS;
168
+ for (int k = 0; k < in_bins[ctr].size(); ++k)
169
+ {
170
+ //check triangles in this bin
171
+ if (SameTriangle(in_bins[ctr][k].m_v0, in_bins[ctr][k].m_v1, in_bins[ctr][k].m_v2,
172
+ v0, v1, v2))
173
+ {
174
+ isLegit = true;
175
+ break;
176
+ }
177
+ }
178
+
179
+ if (!isLegit)
180
+ {
181
+ ctr = v2 % NUMBINS;
182
+ for (int k = 0; k < in_bins[ctr].size(); ++k)
183
+ {
184
+ //check triangles in this bin
185
+ if (SameTriangle(in_bins[ctr][k].m_v0, in_bins[ctr][k].m_v1, in_bins[ctr][k].m_v2,
186
+ v0, v1, v2))
187
+ {
188
+ isLegit = true;
189
+ break;
190
+ }
191
+ }
192
+
193
+ }
194
+ }
195
+
196
+ return isLegit;
197
+ }
198
+
199
+
200
+ ////////////////////////////////////////////////////////////////////////////////////////
201
+ // GenerateStrips()
202
+ //
203
+ // in_indices: input index list, the indices you would use to render
204
+ // in_numIndices: number of entries in in_indices
205
+ // primGroups: array of optimized/stripified PrimitiveGroups
206
+ // numGroups: number of groups returned
207
+ //
208
+ // Be sure to call delete[] on the returned primGroups to avoid leaking mem
209
+ //
210
+ bool GenerateStrips(const unsigned short* in_indices, const unsigned int in_numIndices,
211
+ PrimitiveGroup** primGroups, unsigned short* numGroups, bool validateEnabled)
212
+ {
213
+ //put data in format that the stripifier likes
214
+ WordVec tempIndices;
215
+ tempIndices.resize(in_numIndices);
216
+ unsigned short maxIndex = 0;
217
+ unsigned short minIndex = 0xFFFF;
218
+ for(int i = 0; i < in_numIndices; i++)
219
+ {
220
+ tempIndices[i] = in_indices[i];
221
+ if (in_indices[i] > maxIndex)
222
+ maxIndex = in_indices[i];
223
+ if (in_indices[i] < minIndex)
224
+ minIndex = in_indices[i];
225
+ }
226
+ NvStripInfoVec tempStrips;
227
+ NvFaceInfoVec tempFaces;
228
+
229
+ NvStripifier stripifier;
230
+
231
+ //do actual stripification
232
+ stripifier.Stripify(tempIndices, cacheSize, minStripSize, maxIndex, tempStrips, tempFaces);
233
+
234
+ //stitch strips together
235
+ IntVec stripIndices;
236
+ unsigned int numSeparateStrips = 0;
237
+
238
+ if(bListsOnly)
239
+ {
240
+ //if we're outputting only lists, we're done
241
+ *numGroups = 1;
242
+ (*primGroups) = new PrimitiveGroup[*numGroups];
243
+ PrimitiveGroup* primGroupArray = *primGroups;
244
+
245
+ //count the total number of indices
246
+ unsigned int numIndices = 0;
247
+ int i;
248
+ for(i = 0; i < tempStrips.size(); i++)
249
+ {
250
+ numIndices += tempStrips[i]->m_faces.size() * 3;
251
+ }
252
+
253
+ //add in the list
254
+ numIndices += tempFaces.size() * 3;
255
+
256
+ primGroupArray[0].type = PT_LIST;
257
+ primGroupArray[0].numIndices = numIndices;
258
+ primGroupArray[0].indices = new unsigned short[numIndices];
259
+
260
+ //do strips
261
+ unsigned int indexCtr = 0;
262
+ for(i = 0; i < tempStrips.size(); i++)
263
+ {
264
+ for(int j = 0; j < tempStrips[i]->m_faces.size(); j++)
265
+ {
266
+ //degenerates are of no use with lists
267
+ if(!NvStripifier::IsDegenerate(tempStrips[i]->m_faces[j]))
268
+ {
269
+ primGroupArray[0].indices[indexCtr++] = tempStrips[i]->m_faces[j]->m_v0;
270
+ primGroupArray[0].indices[indexCtr++] = tempStrips[i]->m_faces[j]->m_v1;
271
+ primGroupArray[0].indices[indexCtr++] = tempStrips[i]->m_faces[j]->m_v2;
272
+ }
273
+ else
274
+ {
275
+ //we've removed a tri, reduce the number of indices
276
+ primGroupArray[0].numIndices -= 3;
277
+ }
278
+ }
279
+ }
280
+
281
+ //do lists
282
+ for(i = 0; i < tempFaces.size(); i++)
283
+ {
284
+ primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v0;
285
+ primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v1;
286
+ primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v2;
287
+ }
288
+ }
289
+ else
290
+ {
291
+ stripifier.CreateStrips(tempStrips, stripIndices, bStitchStrips, numSeparateStrips, bRestart, restartVal);
292
+
293
+ //if we're stitching strips together, we better get back only one strip from CreateStrips()
294
+ assert( (bStitchStrips && (numSeparateStrips == 1)) || !bStitchStrips);
295
+
296
+ //convert to output format
297
+ *numGroups = numSeparateStrips; //for the strips
298
+ if(tempFaces.size() != 0)
299
+ (*numGroups)++; //we've got a list as well, increment
300
+ (*primGroups) = new PrimitiveGroup[*numGroups];
301
+
302
+ PrimitiveGroup* primGroupArray = *primGroups;
303
+
304
+ //first, the strips
305
+ int startingLoc = 0;
306
+ for(int stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++)
307
+ {
308
+ int stripLength = 0;
309
+
310
+ if(!bStitchStrips)
311
+ {
312
+ //if we've got multiple strips, we need to figure out the correct length
313
+ int i;
314
+ for(i = startingLoc; i < stripIndices.size(); i++)
315
+ {
316
+ if(stripIndices[i] == -1)
317
+ break;
318
+ }
319
+
320
+ stripLength = i - startingLoc;
321
+ }
322
+ else
323
+ stripLength = stripIndices.size();
324
+
325
+ primGroupArray[stripCtr].type = PT_STRIP;
326
+ primGroupArray[stripCtr].indices = new unsigned short[stripLength];
327
+ primGroupArray[stripCtr].numIndices = stripLength;
328
+
329
+ int indexCtr = 0;
330
+ for(int i = startingLoc; i < stripLength + startingLoc; i++)
331
+ primGroupArray[stripCtr].indices[indexCtr++] = stripIndices[i];
332
+
333
+ //we add 1 to account for the -1 separating strips
334
+ //this doesn't break the stitched case since we'll exit the loop
335
+ startingLoc += stripLength + 1;
336
+ }
337
+
338
+ //next, the list
339
+ if(tempFaces.size() != 0)
340
+ {
341
+ int faceGroupLoc = (*numGroups) - 1; //the face group is the last one
342
+ primGroupArray[faceGroupLoc].type = PT_LIST;
343
+ primGroupArray[faceGroupLoc].indices = new unsigned short[tempFaces.size() * 3];
344
+ primGroupArray[faceGroupLoc].numIndices = tempFaces.size() * 3;
345
+ int indexCtr = 0;
346
+ for(int i = 0; i < tempFaces.size(); i++)
347
+ {
348
+ primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v0;
349
+ primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v1;
350
+ primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v2;
351
+ }
352
+ }
353
+ }
354
+
355
+ //validate generated data against input
356
+ if (validateEnabled)
357
+ {
358
+ const int NUMBINS = 100;
359
+
360
+ std::vector<NvFaceInfo> in_bins[NUMBINS];
361
+
362
+ int i;
363
+ //hash input indices on first index
364
+ for (i = 0; i < in_numIndices; i += 3)
365
+ {
366
+ NvFaceInfo faceInfo(in_indices[i], in_indices[i + 1], in_indices[i + 2]);
367
+ in_bins[in_indices[i] % NUMBINS].push_back(faceInfo);
368
+ }
369
+
370
+ for (i = 0; i < *numGroups; ++i)
371
+ {
372
+ switch ((*primGroups)[i].type)
373
+ {
374
+ case PT_LIST:
375
+ {
376
+ for (int j = 0; j < (*primGroups)[i].numIndices; j += 3)
377
+ {
378
+ unsigned short v0 = (*primGroups)[i].indices[j];
379
+ unsigned short v1 = (*primGroups)[i].indices[j + 1];
380
+ unsigned short v2 = (*primGroups)[i].indices[j + 2];
381
+
382
+ //ignore degenerates
383
+ if (NvStripifier::IsDegenerate(v0, v1, v2))
384
+ continue;
385
+
386
+ if (!TestTriangle(v0, v1, v2, in_bins, NUMBINS))
387
+ {
388
+ Cleanup(tempStrips, tempFaces);
389
+ return false;
390
+ }
391
+ }
392
+ break;
393
+ }
394
+
395
+ case PT_STRIP:
396
+ {
397
+ int brokenCtr = 0;
398
+ bool flip = false;
399
+ for (int j = 2; j < (*primGroups)[i].numIndices; ++j)
400
+ {
401
+ unsigned short v0 = (*primGroups)[i].indices[j - 2];
402
+ unsigned short v1 = (*primGroups)[i].indices[j - 1];
403
+ unsigned short v2 = (*primGroups)[i].indices[j];
404
+
405
+ if (flip)
406
+ {
407
+ //swap v1 and v2
408
+ unsigned short swap = v1;
409
+ v1 = v2;
410
+ v2 = swap;
411
+ }
412
+
413
+ //ignore degenerates
414
+ if (NvStripifier::IsDegenerate(v0, v1, v2))
415
+ {
416
+ flip = !flip;
417
+ continue;
418
+ }
419
+
420
+ if (!TestTriangle(v0, v1, v2, in_bins, NUMBINS))
421
+ {
422
+ Cleanup(tempStrips, tempFaces);
423
+ return false;
424
+ }
425
+
426
+ flip = !flip;
427
+ }
428
+ break;
429
+ }
430
+
431
+ case PT_FAN:
432
+ default:
433
+ break;
434
+ }
435
+ }
436
+
437
+ }
438
+
439
+ //clean up everything
440
+ Cleanup(tempStrips, tempFaces);
441
+
442
+ return true;
443
+ }
444
+
445
+
446
+ ////////////////////////////////////////////////////////////////////////////////////////
447
+ // RemapIndices()
448
+ //
449
+ // Function to remap your indices to improve spatial locality in your vertex buffer.
450
+ //
451
+ // in_primGroups: array of PrimitiveGroups you want remapped
452
+ // numGroups: number of entries in in_primGroups
453
+ // numVerts: number of vertices in your vertex buffer, also can be thought of as the range
454
+ // of acceptable values for indices in your primitive groups.
455
+ // remappedGroups: array of remapped PrimitiveGroups
456
+ //
457
+ // Note that, according to the remapping handed back to you, you must reorder your
458
+ // vertex buffer.
459
+ //
460
+ void RemapIndices(const PrimitiveGroup* in_primGroups, const unsigned short numGroups,
461
+ const unsigned short numVerts, PrimitiveGroup** remappedGroups)
462
+ {
463
+ (*remappedGroups) = new PrimitiveGroup[numGroups];
464
+
465
+ //caches oldIndex --> newIndex conversion
466
+ int *indexCache;
467
+ indexCache = new int[numVerts];
468
+ memset(indexCache, -1, sizeof(int)*numVerts);
469
+
470
+ //loop over primitive groups
471
+ unsigned int indexCtr = 0;
472
+ for(int i = 0; i < numGroups; i++)
473
+ {
474
+ unsigned int numIndices = in_primGroups[i].numIndices;
475
+
476
+ //init remapped group
477
+ (*remappedGroups)[i].type = in_primGroups[i].type;
478
+ (*remappedGroups)[i].numIndices = numIndices;
479
+ (*remappedGroups)[i].indices = new unsigned short[numIndices];
480
+
481
+ for(int j = 0; j < numIndices; j++)
482
+ {
483
+ int cachedIndex = indexCache[in_primGroups[i].indices[j]];
484
+ if(cachedIndex == -1) //we haven't seen this index before
485
+ {
486
+ //point to "last" vertex in VB
487
+ (*remappedGroups)[i].indices[j] = indexCtr;
488
+
489
+ //add to index cache, increment
490
+ indexCache[in_primGroups[i].indices[j]] = indexCtr++;
491
+ }
492
+ else
493
+ {
494
+ //we've seen this index before
495
+ (*remappedGroups)[i].indices[j] = cachedIndex;
496
+ }
497
+ }
498
+ }
499
+
500
+ delete[] indexCache;
501
+ }