1/*
2 * Copyright (c) 2009-2010 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
33package jme3tools.converters.model.strip;
34
35import java.util.HashSet;
36import java.util.logging.Logger;
37
38/**
39 *
40 */
41class Stripifier {
42    private static final Logger logger = Logger.getLogger(Stripifier.class
43            .getName());
44
45	public static int CACHE_INEFFICIENCY = 6;
46
47	IntVec indices = new IntVec();
48
49	int cacheSize;
50
51	int minStripLength;
52
53	float meshJump;
54
55	boolean bFirstTimeResetPoint;
56
57	Stripifier() {
58		super();
59	}
60
61	///////////////////////////////////////////////////////////////////////////////////////////
62	// FindEdgeInfo()
63	//
64	// find the edge info for these two indices
65	//
66	static EdgeInfo findEdgeInfo(EdgeInfoVec edgeInfos, int v0, int v1) {
67
68		// we can get to it through either array
69		// because the edge infos have a v0 and v1
70		// and there is no order except how it was
71		// first created.
72		EdgeInfo infoIter = edgeInfos.at(v0);
73		while (infoIter != null) {
74			if (infoIter.m_v0 == v0) {
75				if (infoIter.m_v1 == v1)
76					return infoIter;
77
78				infoIter = infoIter.m_nextV0;
79			} else {
80				if (infoIter.m_v0 == v1)
81					return infoIter;
82
83				infoIter = infoIter.m_nextV1;
84			}
85		}
86		return null;
87	}
88
89	///////////////////////////////////////////////////////////////////////////////////////////
90	// FindOtherFace
91	//
92	// find the other face sharing these vertices
93	// exactly like the edge info above
94	//
95	static FaceInfo findOtherFace(EdgeInfoVec edgeInfos, int v0, int v1,
96			FaceInfo faceInfo) {
97		EdgeInfo edgeInfo = findEdgeInfo(edgeInfos, v0, v1);
98
99		if ((edgeInfo == null) || (v0 == v1)) {
100			//we've hit a degenerate
101			return null;
102		}
103
104		return (edgeInfo.m_face0 == faceInfo ? edgeInfo.m_face1
105				: edgeInfo.m_face0);
106	}
107
108	static boolean alreadyExists(FaceInfo faceInfo, FaceInfoVec faceInfos) {
109		for (int i = 0; i < faceInfos.size(); ++i) {
110			FaceInfo o = faceInfos.at(i);
111			if ((o.m_v0 == faceInfo.m_v0) && (o.m_v1 == faceInfo.m_v1)
112					&& (o.m_v2 == faceInfo.m_v2))
113				return true;
114		}
115		return false;
116	}
117
118	///////////////////////////////////////////////////////////////////////////////////////////
119	// BuildStripifyInfo()
120	//
121	// Builds the list of all face and edge infos
122	//
123	void buildStripifyInfo(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos,
124			int maxIndex) {
125		// reserve space for the face infos, but do not resize them.
126		int numIndices = indices.size();
127		faceInfos.reserve(numIndices / 3);
128
129		// we actually resize the edge infos, so we must initialize to null
130		for (int i = 0; i < maxIndex + 1; i++)
131			edgeInfos.add(null);
132
133		// iterate through the triangles of the triangle list
134		int numTriangles = numIndices / 3;
135		int index = 0;
136		boolean[] bFaceUpdated = new boolean[3];
137
138		for (int i = 0; i < numTriangles; i++) {
139			boolean bMightAlreadyExist = true;
140			bFaceUpdated[0] = false;
141			bFaceUpdated[1] = false;
142			bFaceUpdated[2] = false;
143
144			// grab the indices
145			int v0 = indices.get(index++);
146			int v1 = indices.get(index++);
147			int v2 = indices.get(index++);
148
149			//we disregard degenerates
150			if (isDegenerate(v0, v1, v2))
151				continue;
152
153			// create the face info and add it to the list of faces, but only
154			// if this exact face doesn't already
155			//  exist in the list
156			FaceInfo faceInfo = new FaceInfo(v0, v1, v2);
157
158			// grab the edge infos, creating them if they do not already exist
159			EdgeInfo edgeInfo01 = findEdgeInfo(edgeInfos, v0, v1);
160			if (edgeInfo01 == null) {
161				//since one of it's edges isn't in the edge data structure, it
162				// can't already exist in the face structure
163				bMightAlreadyExist = false;
164
165				// create the info
166				edgeInfo01 = new EdgeInfo(v0, v1);
167
168				// update the linked list on both
169				edgeInfo01.m_nextV0 = edgeInfos.at(v0);
170				edgeInfo01.m_nextV1 = edgeInfos.at(v1);
171				edgeInfos.set(v0, edgeInfo01);
172				edgeInfos.set(v1, edgeInfo01);
173
174				// set face 0
175				edgeInfo01.m_face0 = faceInfo;
176			} else {
177				if (edgeInfo01.m_face1 != null) {
178					logger.info("BuildStripifyInfo: > 2 triangles on an edge"
179                            + v0 + "," + v1 + "... uncertain consequences\n");
180				} else {
181					edgeInfo01.m_face1 = faceInfo;
182					bFaceUpdated[0] = true;
183				}
184			}
185
186			// grab the edge infos, creating them if they do not already exist
187			EdgeInfo edgeInfo12 = findEdgeInfo(edgeInfos, v1, v2);
188			if (edgeInfo12 == null) {
189				bMightAlreadyExist = false;
190
191				// create the info
192				edgeInfo12 = new EdgeInfo(v1, v2);
193
194				// update the linked list on both
195				edgeInfo12.m_nextV0 = edgeInfos.at(v1);
196				edgeInfo12.m_nextV1 = edgeInfos.at(v2);
197				edgeInfos.set(v1, edgeInfo12);
198				edgeInfos.set(v2, edgeInfo12);
199
200				// set face 0
201				edgeInfo12.m_face0 = faceInfo;
202			} else {
203				if (edgeInfo12.m_face1 != null) {
204					logger.info("BuildStripifyInfo: > 2 triangles on an edge"
205									+ v1
206									+ ","
207									+ v2
208									+ "... uncertain consequences\n");
209				} else {
210					edgeInfo12.m_face1 = faceInfo;
211					bFaceUpdated[1] = true;
212				}
213			}
214
215			// grab the edge infos, creating them if they do not already exist
216			EdgeInfo edgeInfo20 = findEdgeInfo(edgeInfos, v2, v0);
217			if (edgeInfo20 == null) {
218				bMightAlreadyExist = false;
219
220				// create the info
221				edgeInfo20 = new EdgeInfo(v2, v0);
222
223				// update the linked list on both
224				edgeInfo20.m_nextV0 = edgeInfos.at(v2);
225				edgeInfo20.m_nextV1 = edgeInfos.at(v0);
226				edgeInfos.set(v2, edgeInfo20);
227				edgeInfos.set(v0, edgeInfo20);
228
229				// set face 0
230				edgeInfo20.m_face0 = faceInfo;
231			} else {
232				if (edgeInfo20.m_face1 != null) {
233					logger.info("BuildStripifyInfo: > 2 triangles on an edge"
234									+ v2
235									+ ","
236									+ v0
237									+ "... uncertain consequences\n");
238				} else {
239					edgeInfo20.m_face1 = faceInfo;
240					bFaceUpdated[2] = true;
241				}
242			}
243
244			if (bMightAlreadyExist) {
245				if (!alreadyExists(faceInfo, faceInfos))
246					faceInfos.add(faceInfo);
247				else {
248
249					//cleanup pointers that point to this deleted face
250					if (bFaceUpdated[0])
251						edgeInfo01.m_face1 = null;
252					if (bFaceUpdated[1])
253						edgeInfo12.m_face1 = null;
254					if (bFaceUpdated[2])
255						edgeInfo20.m_face1 = null;
256				}
257			} else {
258				faceInfos.add(faceInfo);
259			}
260
261		}
262	}
263
264	static boolean isDegenerate(FaceInfo face) {
265		if (face.m_v0 == face.m_v1)
266			return true;
267		else if (face.m_v0 == face.m_v2)
268			return true;
269		else if (face.m_v1 == face.m_v2)
270			return true;
271		else
272			return false;
273	}
274
275	static boolean isDegenerate(int v0, int v1, int v2) {
276		if (v0 == v1)
277			return true;
278		else if (v0 == v2)
279			return true;
280		else if (v1 == v2)
281			return true;
282		else
283			return false;
284	}
285
286	///////////////////////////////////////////////////////////////////////////////////////////
287	// GetNextIndex()
288	//
289	// Returns vertex of the input face which is "next" in the input index list
290	//
291	static int getNextIndex(IntVec indices, FaceInfo face) {
292
293		int numIndices = indices.size();
294
295		int v0 = indices.get(numIndices - 2);
296		int v1 = indices.get(numIndices - 1);
297
298		int fv0 = face.m_v0;
299		int fv1 = face.m_v1;
300		int fv2 = face.m_v2;
301
302		if (fv0 != v0 && fv0 != v1) {
303			if ((fv1 != v0 && fv1 != v1) || (fv2 != v0 && fv2 != v1)) {
304                logger.info("GetNextIndex: Triangle doesn't have all of its vertices\n");
305                logger.info("GetNextIndex: Duplicate triangle probably got us derailed\n");
306			}
307			return fv0;
308		}
309		if (fv1 != v0 && fv1 != v1) {
310			if ((fv0 != v0 && fv0 != v1) || (fv2 != v0 && fv2 != v1)) {
311                logger.info("GetNextIndex: Triangle doesn't have all of its vertices\n");
312                logger.info("GetNextIndex: Duplicate triangle probably got us derailed\n");
313			}
314			return fv1;
315		}
316		if (fv2 != v0 && fv2 != v1) {
317			if ((fv0 != v0 && fv0 != v1) || (fv1 != v0 && fv1 != v1)) {
318                logger.info("GetNextIndex: Triangle doesn't have all of its vertices\n");
319                logger.info("GetNextIndex: Duplicate triangle probably got us derailed\n");
320			}
321			return fv2;
322		}
323
324		// shouldn't get here, but let's try and fail gracefully
325		if ((fv0 == fv1) || (fv0 == fv2))
326			return fv0;
327		else if ((fv1 == fv0) || (fv1 == fv2))
328			return fv1;
329		else if ((fv2 == fv0) || (fv2 == fv1))
330			return fv2;
331		else
332			return -1;
333	}
334
335	///////////////////////////////////////////////////////////////////////////////////////////
336	// FindStartPoint()
337	//
338	// Finds a good starting point, namely one which has only one neighbor
339	//
340	static int findStartPoint(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos) {
341		int bestCtr = -1;
342		int bestIndex = -1;
343
344		for (int i = 0; i < faceInfos.size(); i++) {
345			int ctr = 0;
346
347			if (findOtherFace(edgeInfos, faceInfos.at(i).m_v0,
348					faceInfos.at(i).m_v1, faceInfos.at(i)) == null)
349				ctr++;
350			if (findOtherFace(edgeInfos, faceInfos.at(i).m_v1,
351					faceInfos.at(i).m_v2, faceInfos.at(i)) == null)
352				ctr++;
353			if (findOtherFace(edgeInfos, faceInfos.at(i).m_v2,
354					faceInfos.at(i).m_v0, faceInfos.at(i)) == null)
355				ctr++;
356			if (ctr > bestCtr) {
357				bestCtr = ctr;
358				bestIndex = i;
359				//return i;
360			}
361		}
362		//return -1;
363
364		if (bestCtr == 0)
365			return -1;
366
367		return bestIndex;
368	}
369
370	///////////////////////////////////////////////////////////////////////////////////////////
371	// FindGoodResetPoint()
372	//
373	// A good reset point is one near other commited areas so that
374	// we know that when we've made the longest strips its because
375	// we're stripifying in the same general orientation.
376	//
377	FaceInfo findGoodResetPoint(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos) {
378		// we hop into different areas of the mesh to try to get
379		// other large open spans done. Areas of small strips can
380		// just be left to triangle lists added at the end.
381		FaceInfo result = null;
382
383		if (result == null) {
384			int numFaces = faceInfos.size();
385			int startPoint;
386			if (bFirstTimeResetPoint) {
387				//first time, find a face with few neighbors (look for an edge
388				// of the mesh)
389				startPoint = findStartPoint(faceInfos, edgeInfos);
390				bFirstTimeResetPoint = false;
391			} else
392				startPoint = (int) (((float) numFaces - 1) * meshJump);
393
394			if (startPoint == -1) {
395				startPoint = (int) (((float) numFaces - 1) * meshJump);
396
397				//meshJump += 0.1f;
398				//if (meshJump > 1.0f)
399				//  meshJump = .05f;
400			}
401
402			int i = startPoint;
403			do {
404
405				// if this guy isn't visited, try him
406				if (faceInfos.at(i).m_stripId < 0) {
407					result = faceInfos.at(i);
408					break;
409				}
410
411				// update the index and clamp to 0-(numFaces-1)
412				if (++i >= numFaces)
413					i = 0;
414
415			} while (i != startPoint);
416
417			// update the meshJump
418			meshJump += 0.1f;
419			if (meshJump > 1.0f)
420				meshJump = .05f;
421		}
422
423		// return the best face we found
424		return result;
425	}
426
427	///////////////////////////////////////////////////////////////////////////////////////////
428	// GetUniqueVertexInB()
429	//
430	// Returns the vertex unique to faceB
431	//
432	static int getUniqueVertexInB(FaceInfo faceA, FaceInfo faceB) {
433
434		int facev0 = faceB.m_v0;
435		if (facev0 != faceA.m_v0 && facev0 != faceA.m_v1
436				&& facev0 != faceA.m_v2)
437			return facev0;
438
439		int facev1 = faceB.m_v1;
440		if (facev1 != faceA.m_v0 && facev1 != faceA.m_v1
441				&& facev1 != faceA.m_v2)
442			return facev1;
443
444		int facev2 = faceB.m_v2;
445		if (facev2 != faceA.m_v0 && facev2 != faceA.m_v1
446				&& facev2 != faceA.m_v2)
447			return facev2;
448
449		// nothing is different
450		return -1;
451	}
452
453	///////////////////////////////////////////////////////////////////////////////////////////
454	// GetSharedVertices()
455	//
456	// Returns the (at most) two vertices shared between the two faces
457	//
458	static void getSharedVertices(FaceInfo faceA, FaceInfo faceB, int[] vertex) {
459		vertex[0] = -1;
460		vertex[1] = -1;
461
462		int facev0 = faceB.m_v0;
463		if (facev0 == faceA.m_v0 || facev0 == faceA.m_v1
464				|| facev0 == faceA.m_v2) {
465			if (vertex[0] == -1)
466				vertex[0] = facev0;
467			else {
468				vertex[1] = facev0;
469				return;
470			}
471		}
472
473		int facev1 = faceB.m_v1;
474		if (facev1 == faceA.m_v0 || facev1 == faceA.m_v1
475				|| facev1 == faceA.m_v2) {
476			if (vertex[0] == -1)
477				vertex[0] = facev1;
478			else {
479				vertex[1] = facev1;
480				return;
481			}
482		}
483
484		int facev2 = faceB.m_v2;
485		if (facev2 == faceA.m_v0 || facev2 == faceA.m_v1
486				|| facev2 == faceA.m_v2) {
487			if (vertex[0] == -1)
488				vertex[0] = facev2;
489			else {
490				vertex[1] = facev2;
491				return;
492			}
493		}
494
495	}
496
497	///////////////////////////////////////////////////////////////////////////////////////////
498	// CommitStrips()
499	//
500	// "Commits" the input strips by setting their m_experimentId to -1 and
501	// adding to the allStrips
502	//  vector
503	//
504	static void commitStrips(StripInfoVec allStrips, StripInfoVec strips) {
505		// Iterate through strips
506		int numStrips = strips.size();
507		for (int i = 0; i < numStrips; i++) {
508
509			// Tell the strip that it is now real
510			StripInfo strip = strips.at(i);
511			strip.m_experimentId = -1;
512
513			// add to the list of real strips
514			allStrips.add(strip);
515
516			// Iterate through the faces of the strip
517			// Tell the faces of the strip that they belong to a real strip now
518			FaceInfoVec faces = strips.at(i).m_faces;
519			int numFaces = faces.size();
520
521			for (int j = 0; j < numFaces; j++) {
522				strip.markTriangle(faces.at(j));
523			}
524		}
525	}
526
527	///////////////////////////////////////////////////////////////////////////////////////////
528	// NextIsCW()
529	//
530	// Returns true if the next face should be ordered in CW fashion
531	//
532	static boolean nextIsCW(int numIndices) {
533		return ((numIndices % 2) == 0);
534	}
535
536	///////////////////////////////////////////////////////////////////////////////////////////
537	// UpdateCacheFace()
538	//
539	// Updates the input vertex cache with this face's vertices
540	//
541	static void updateCacheFace(VertexCache vcache, FaceInfo face) {
542		if (!vcache.inCache(face.m_v0))
543			vcache.addEntry(face.m_v0);
544
545		if (!vcache.inCache(face.m_v1))
546			vcache.addEntry(face.m_v1);
547
548		if (!vcache.inCache(face.m_v2))
549			vcache.addEntry(face.m_v2);
550	}
551
552	///////////////////////////////////////////////////////////////////////////////////////////
553	// UpdateCacheStrip()
554	//
555	// Updates the input vertex cache with this strip's vertices
556	//
557	static void updateCacheStrip(VertexCache vcache, StripInfo strip) {
558		for (int i = 0; i < strip.m_faces.size(); ++i) {
559			if (!vcache.inCache(strip.m_faces.at(i).m_v0))
560				vcache.addEntry(strip.m_faces.at(i).m_v0);
561
562			if (!vcache.inCache(strip.m_faces.at(i).m_v1))
563				vcache.addEntry(strip.m_faces.at(i).m_v1);
564
565			if (!vcache.inCache(strip.m_faces.at(i).m_v2))
566				vcache.addEntry(strip.m_faces.at(i).m_v2);
567		}
568	}
569
570	///////////////////////////////////////////////////////////////////////////////////////////
571	// CalcNumHitsStrip()
572	//
573	// returns the number of cache hits per face in the strip
574	//
575	static float calcNumHitsStrip(VertexCache vcache, StripInfo strip) {
576		int numHits = 0;
577		int numFaces = 0;
578
579		for (int i = 0; i < strip.m_faces.size(); i++) {
580			if (vcache.inCache(strip.m_faces.at(i).m_v0))
581				++numHits;
582
583			if (vcache.inCache(strip.m_faces.at(i).m_v1))
584				++numHits;
585
586			if (vcache.inCache(strip.m_faces.at(i).m_v2))
587				++numHits;
588
589			numFaces++;
590		}
591
592		return ((float) numHits / (float) numFaces);
593	}
594
595	///////////////////////////////////////////////////////////////////////////////////////////
596	// AvgStripSize()
597	//
598	// Finds the average strip size of the input vector of strips
599	//
600	static float avgStripSize(StripInfoVec strips) {
601		int sizeAccum = 0;
602		int numStrips = strips.size();
603		for (int i = 0; i < numStrips; i++) {
604			StripInfo strip = strips.at(i);
605			sizeAccum += strip.m_faces.size();
606			sizeAccum -= strip.m_numDegenerates;
607		}
608		return ((float) sizeAccum) / ((float) numStrips);
609	}
610
611	///////////////////////////////////////////////////////////////////////////////////////////
612	// CalcNumHitsFace()
613	//
614	// returns the number of cache hits in the face
615	//
616	static int calcNumHitsFace(VertexCache vcache, FaceInfo face) {
617		int numHits = 0;
618
619		if (vcache.inCache(face.m_v0))
620			numHits++;
621
622		if (vcache.inCache(face.m_v1))
623			numHits++;
624
625		if (vcache.inCache(face.m_v2))
626			numHits++;
627
628		return numHits;
629	}
630
631	///////////////////////////////////////////////////////////////////////////////////////////
632	// NumNeighbors()
633	//
634	// Returns the number of neighbors that this face has
635	//
636	static int numNeighbors(FaceInfo face, EdgeInfoVec edgeInfoVec) {
637		int numNeighbors = 0;
638
639		if (findOtherFace(edgeInfoVec, face.m_v0, face.m_v1, face) != null) {
640			numNeighbors++;
641		}
642
643		if (findOtherFace(edgeInfoVec, face.m_v1, face.m_v2, face) != null) {
644			numNeighbors++;
645		}
646
647		if (findOtherFace(edgeInfoVec, face.m_v2, face.m_v0, face) != null) {
648			numNeighbors++;
649		}
650
651		return numNeighbors;
652	}
653
654	///////////////////////////////////////////////////////////////////////////////////////////
655	// IsCW()
656	//
657	// Returns true if the face is ordered in CW fashion
658	//
659	static boolean isCW(FaceInfo faceInfo, int v0, int v1) {
660		if (faceInfo.m_v0 == v0)
661			return (faceInfo.m_v1 == v1);
662		else if (faceInfo.m_v1 == v0)
663			return (faceInfo.m_v2 == v1);
664		else
665			return (faceInfo.m_v0 == v1);
666
667	}
668
669	static boolean faceContainsIndex(FaceInfo face, int index) {
670		return ((face.m_v0 == index) || (face.m_v1 == index) || (face.m_v2 == index));
671	}
672
673	///////////////////////////////////////////////////////////////////////////////////////////
674	// FindTraversal()
675	//
676	// Finds the next face to start the next strip on.
677	//
678	static boolean findTraversal(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos,
679			StripInfo strip, StripStartInfo startInfo) {
680
681		// if the strip was v0.v1 on the edge, then v1 will be a vertex in the
682		// next edge.
683		int v = (strip.m_startInfo.m_toV1 ? strip.m_startInfo.m_startEdge.m_v1
684				: strip.m_startInfo.m_startEdge.m_v0);
685
686		FaceInfo untouchedFace = null;
687		EdgeInfo edgeIter = edgeInfos.at(v);
688		while (edgeIter != null) {
689			FaceInfo face0 = edgeIter.m_face0;
690			FaceInfo face1 = edgeIter.m_face1;
691			if ((face0 != null && !strip.isInStrip(face0)) && face1 != null
692					&& !strip.isMarked(face1)) {
693				untouchedFace = face1;
694				break;
695			}
696			if ((face1 != null && !strip.isInStrip(face1)) && face0 != null
697					&& !strip.isMarked(face0)) {
698				untouchedFace = face0;
699				break;
700			}
701
702			// find the next edgeIter
703			edgeIter = (edgeIter.m_v0 == v ? edgeIter.m_nextV0
704					: edgeIter.m_nextV1);
705		}
706
707		startInfo.m_startFace = untouchedFace;
708		startInfo.m_startEdge = edgeIter;
709		if (edgeIter != null) {
710			if (strip.sharesEdge(startInfo.m_startFace, edgeInfos))
711				startInfo.m_toV1 = (edgeIter.m_v0 == v); //note! used to be
712			// m_v1
713			else
714				startInfo.m_toV1 = (edgeIter.m_v1 == v);
715		}
716		return (startInfo.m_startFace != null);
717	}
718
719	////////////////////////////////////////////////////////////////////////////////////////
720	// RemoveSmallStrips()
721	//
722	// allStrips is the whole strip vector...all small strips will be deleted
723	// from this list, to avoid leaking mem
724	// allBigStrips is an out parameter which will contain all strips above
725	// minStripLength
726	// faceList is an out parameter which will contain all faces which were
727	// removed from the striplist
728	//
729	void removeSmallStrips(StripInfoVec allStrips, StripInfoVec allBigStrips,
730			FaceInfoVec faceList) {
731		faceList.clear();
732		allBigStrips.clear(); //make sure these are empty
733		FaceInfoVec tempFaceList = new FaceInfoVec();
734
735		for (int i = 0; i < allStrips.size(); i++) {
736			if (allStrips.at(i).m_faces.size() < minStripLength) {
737				//strip is too small, add faces to faceList
738				for (int j = 0; j < allStrips.at(i).m_faces.size(); j++)
739					tempFaceList.add(allStrips.at(i).m_faces.at(j));
740
741			} else {
742				allBigStrips.add(allStrips.at(i));
743			}
744		}
745
746		boolean[] bVisitedList = new boolean[tempFaceList.size()];
747
748		VertexCache vcache = new VertexCache(cacheSize);
749
750		int bestNumHits = -1;
751		int numHits;
752		int bestIndex = -9999;
753
754		while (true) {
755			bestNumHits = -1;
756
757			//find best face to add next, given the current cache
758			for (int i = 0; i < tempFaceList.size(); i++) {
759				if (bVisitedList[i])
760					continue;
761
762				numHits = calcNumHitsFace(vcache, tempFaceList.at(i));
763				if (numHits > bestNumHits) {
764					bestNumHits = numHits;
765					bestIndex = i;
766				}
767			}
768
769			if (bestNumHits == -1.0f)
770				break;
771			bVisitedList[bestIndex] = true;
772			updateCacheFace(vcache, tempFaceList.at(bestIndex));
773			faceList.add(tempFaceList.at(bestIndex));
774		}
775	}
776
777	////////////////////////////////////////////////////////////////////////////////////////
778	// CreateStrips()
779	//
780	// Generates actual strips from the list-in-strip-order.
781	//
782	int createStrips(StripInfoVec allStrips, IntVec stripIndices,
783			boolean bStitchStrips) {
784		int numSeparateStrips = 0;
785
786		FaceInfo tLastFace = new FaceInfo(0, 0, 0);
787		int nStripCount = allStrips.size();
788
789		//we infer the cw/ccw ordering depending on the number of indices
790		//this is screwed up by the fact that we insert -1s to denote changing
791		// strips
792		//this is to account for that
793		int accountForNegatives = 0;
794
795		for (int i = 0; i < nStripCount; i++) {
796			StripInfo strip = allStrips.at(i);
797			int nStripFaceCount = strip.m_faces.size();
798
799			// Handle the first face in the strip
800			{
801				FaceInfo tFirstFace = new FaceInfo(strip.m_faces.at(0).m_v0,
802						strip.m_faces.at(0).m_v1, strip.m_faces.at(0).m_v2);
803
804				// If there is a second face, reorder vertices such that the
805				// unique vertex is first
806				if (nStripFaceCount > 1) {
807					int nUnique = getUniqueVertexInB(strip.m_faces.at(1),
808							tFirstFace);
809					if (nUnique == tFirstFace.m_v1) {
810						int tmp = tFirstFace.m_v0;
811						tFirstFace.m_v0 = tFirstFace.m_v1;
812						tFirstFace.m_v1 = tmp;
813					} else if (nUnique == tFirstFace.m_v2) {
814						int tmp = tFirstFace.m_v0;
815						tFirstFace.m_v0 = tFirstFace.m_v2;
816						tFirstFace.m_v2 = tmp;
817					}
818
819					// If there is a third face, reorder vertices such that the
820					// shared vertex is last
821					if (nStripFaceCount > 2) {
822						if (isDegenerate(strip.m_faces.at(1))) {
823							int pivot = strip.m_faces.at(1).m_v1;
824							if (tFirstFace.m_v1 == pivot) {
825								int tmp = tFirstFace.m_v1;
826								tFirstFace.m_v1 = tFirstFace.m_v2;
827								tFirstFace.m_v2 = tmp;
828							}
829						} else {
830							int[] nShared = new int[2];
831							getSharedVertices(strip.m_faces.at(2), tFirstFace,
832									nShared);
833							if ((nShared[0] == tFirstFace.m_v1)
834									&& (nShared[1] == -1)) {
835								int tmp = tFirstFace.m_v1;
836								tFirstFace.m_v1 = tFirstFace.m_v2;
837								tFirstFace.m_v2 = tmp;
838							}
839						}
840					}
841				}
842
843				if ((i == 0) || !bStitchStrips) {
844					if (!isCW(strip.m_faces.at(0), tFirstFace.m_v0,
845							tFirstFace.m_v1))
846						stripIndices.add(tFirstFace.m_v0);
847				} else {
848					// Double tap the first in the new strip
849					stripIndices.add(tFirstFace.m_v0);
850
851					// Check CW/CCW ordering
852					if (nextIsCW(stripIndices.size() - accountForNegatives) != isCW(
853							strip.m_faces.at(0), tFirstFace.m_v0,
854							tFirstFace.m_v1)) {
855						stripIndices.add(tFirstFace.m_v0);
856					}
857				}
858
859				stripIndices.add(tFirstFace.m_v0);
860				stripIndices.add(tFirstFace.m_v1);
861				stripIndices.add(tFirstFace.m_v2);
862
863				// Update last face info
864				tLastFace.set(tFirstFace);
865			}
866
867			for (int j = 1; j < nStripFaceCount; j++) {
868				int nUnique = getUniqueVertexInB(tLastFace, strip.m_faces.at(j));
869				if (nUnique != -1) {
870					stripIndices.add(nUnique);
871
872					// Update last face info
873					tLastFace.m_v0 = tLastFace.m_v1;
874					tLastFace.m_v1 = tLastFace.m_v2;
875					tLastFace.m_v2 = nUnique;
876				} else {
877					//we've hit a degenerate
878					stripIndices.add(strip.m_faces.at(j).m_v2);
879					tLastFace.m_v0 = strip.m_faces.at(j).m_v0; //tLastFace.m_v1;
880					tLastFace.m_v1 = strip.m_faces.at(j).m_v1; //tLastFace.m_v2;
881					tLastFace.m_v2 = strip.m_faces.at(j).m_v2; //tLastFace.m_v1;
882
883				}
884			}
885
886			// Double tap between strips.
887			if (bStitchStrips) {
888				if (i != nStripCount - 1)
889					stripIndices.add(tLastFace.m_v2);
890			} else {
891				//-1 index indicates next strip
892				stripIndices.add(-1);
893				accountForNegatives++;
894				numSeparateStrips++;
895			}
896
897			// Update last face info
898			tLastFace.m_v0 = tLastFace.m_v1;
899			tLastFace.m_v1 = tLastFace.m_v2;
900			tLastFace.m_v2 = tLastFace.m_v2;
901		}
902
903		if (bStitchStrips)
904			numSeparateStrips = 1;
905		return numSeparateStrips;
906	}
907
908	///////////////////////////////////////////////////////////////////////////////////////////
909	// FindAllStrips()
910	//
911	// Does the stripification, puts output strips into vector allStrips
912	//
913	// Works by setting runnning a number of experiments in different areas of
914	// the mesh, and
915	//  accepting the one which results in the longest strips. It then accepts
916	// this, and moves
917	//  on to a different area of the mesh. We try to jump around the mesh some,
918	// to ensure that
919	//  large open spans of strips get generated.
920	//
921	void findAllStrips(StripInfoVec allStrips, FaceInfoVec allFaceInfos,
922			EdgeInfoVec allEdgeInfos, int numSamples) {
923		// the experiments
924		int experimentId = 0;
925		int stripId = 0;
926		boolean done = false;
927
928		int loopCtr = 0;
929
930		while (!done) {
931			loopCtr++;
932
933			//
934			// PHASE 1: Set up numSamples * numEdges experiments
935			//
936			StripInfoVec[] experiments = new StripInfoVec[numSamples * 6];
937			for (int i = 0; i < experiments.length; i++)
938				experiments[i] = new StripInfoVec();
939
940			int experimentIndex = 0;
941			HashSet<FaceInfo> resetPoints = new HashSet<FaceInfo>(); /* NvFaceInfo */
942			for (int i = 0; i < numSamples; i++) {
943				// Try to find another good reset point.
944				// If there are none to be found, we are done
945				FaceInfo nextFace = findGoodResetPoint(allFaceInfos,
946						allEdgeInfos);
947				if (nextFace == null) {
948					done = true;
949					break;
950				}
951				// If we have already evaluated starting at this face in this
952				// slew of experiments, then skip going any further
953				else if (resetPoints.contains(nextFace)) {
954					continue;
955				}
956
957				// trying it now...
958				resetPoints.add(nextFace);
959
960				// otherwise, we shall now try experiments for starting on the
961				// 01,12, and 20 edges
962
963				// build the strip off of this face's 0-1 edge
964				EdgeInfo edge01 = findEdgeInfo(allEdgeInfos, nextFace.m_v0,
965						nextFace.m_v1);
966				StripInfo strip01 = new StripInfo(new StripStartInfo(nextFace,
967						edge01, true), stripId++, experimentId++);
968				experiments[experimentIndex++].add(strip01);
969
970				// build the strip off of this face's 1-0 edge
971				EdgeInfo edge10 = findEdgeInfo(allEdgeInfos, nextFace.m_v0,
972						nextFace.m_v1);
973				StripInfo strip10 = new StripInfo(new StripStartInfo(nextFace,
974						edge10, false), stripId++, experimentId++);
975				experiments[experimentIndex++].add(strip10);
976
977				// build the strip off of this face's 1-2 edge
978				EdgeInfo edge12 = findEdgeInfo(allEdgeInfos, nextFace.m_v1,
979						nextFace.m_v2);
980				StripInfo strip12 = new StripInfo(new StripStartInfo(nextFace,
981						edge12, true), stripId++, experimentId++);
982				experiments[experimentIndex++].add(strip12);
983
984				// build the strip off of this face's 2-1 edge
985				EdgeInfo edge21 = findEdgeInfo(allEdgeInfos, nextFace.m_v1,
986						nextFace.m_v2);
987				StripInfo strip21 = new StripInfo(new StripStartInfo(nextFace,
988						edge21, false), stripId++, experimentId++);
989				experiments[experimentIndex++].add(strip21);
990
991				// build the strip off of this face's 2-0 edge
992				EdgeInfo edge20 = findEdgeInfo(allEdgeInfos, nextFace.m_v2,
993						nextFace.m_v0);
994				StripInfo strip20 = new StripInfo(new StripStartInfo(nextFace,
995						edge20, true), stripId++, experimentId++);
996				experiments[experimentIndex++].add(strip20);
997
998				// build the strip off of this face's 0-2 edge
999				EdgeInfo edge02 = findEdgeInfo(allEdgeInfos, nextFace.m_v2,
1000						nextFace.m_v0);
1001				StripInfo strip02 = new StripInfo(new StripStartInfo(nextFace,
1002						edge02, false), stripId++, experimentId++);
1003				experiments[experimentIndex++].add(strip02);
1004			}
1005
1006			//
1007			// PHASE 2: Iterate through that we setup in the last phase
1008			// and really build each of the strips and strips that follow to
1009			// see how
1010			// far we get
1011			//
1012			int numExperiments = experimentIndex;
1013			for (int i = 0; i < numExperiments; i++) {
1014
1015				// get the strip set
1016
1017				// build the first strip of the list
1018				experiments[i].at(0).build(allEdgeInfos, allFaceInfos);
1019				int experimentId2 = experiments[i].at(0).m_experimentId;
1020
1021				StripInfo stripIter = experiments[i].at(0);
1022				StripStartInfo startInfo = new StripStartInfo(null, null, false);
1023				while (findTraversal(allFaceInfos, allEdgeInfos, stripIter,
1024						startInfo)) {
1025
1026					// create the new strip info
1027					//TODO startInfo clone ?
1028					stripIter = new StripInfo(startInfo, stripId++,
1029							experimentId2);
1030
1031					// build the next strip
1032					stripIter.build(allEdgeInfos, allFaceInfos);
1033
1034					// add it to the list
1035					experiments[i].add(stripIter);
1036				}
1037			}
1038
1039			//
1040			// Phase 3: Find the experiment that has the most promise
1041			//
1042			int bestIndex = 0;
1043			double bestValue = 0;
1044			for (int i = 0; i < numExperiments; i++) {
1045				float avgStripSizeWeight = 1.0f;
1046				//float numTrisWeight = 0.0f;
1047				float numStripsWeight = 0.0f;
1048				float avgStripSize = avgStripSize(experiments[i]);
1049				float numStrips = experiments[i].size();
1050				float value = avgStripSize * avgStripSizeWeight
1051						+ (numStrips * numStripsWeight);
1052				//float value = 1.f / numStrips;
1053				//float value = numStrips * avgStripSize;
1054
1055				if (value > bestValue) {
1056					bestValue = value;
1057					bestIndex = i;
1058				}
1059			}
1060
1061			//
1062			// Phase 4: commit the best experiment of the bunch
1063			//
1064			commitStrips(allStrips, experiments[bestIndex]);
1065		}
1066	}
1067
1068	///////////////////////////////////////////////////////////////////////////////////////////
1069	// SplitUpStripsAndOptimize()
1070	//
1071	// Splits the input vector of strips (allBigStrips) into smaller, cache
1072	// friendly pieces, then
1073	//  reorders these pieces to maximize cache hits
1074	// The final strips are output through outStrips
1075	//
1076	void splitUpStripsAndOptimize(StripInfoVec allStrips,
1077			StripInfoVec outStrips, EdgeInfoVec edgeInfos,
1078			FaceInfoVec outFaceList) {
1079		int threshold = cacheSize;
1080		StripInfoVec tempStrips = new StripInfoVec();
1081		int j;
1082
1083		//split up strips into threshold-sized pieces
1084		for (int i = 0; i < allStrips.size(); i++) {
1085			StripInfo currentStrip;
1086			StripStartInfo startInfo = new StripStartInfo(null, null, false);
1087
1088			int actualStripSize = 0;
1089			for (j = 0; j < allStrips.at(i).m_faces.size(); ++j) {
1090				if (!isDegenerate(allStrips.at(i).m_faces.at(j)))
1091					actualStripSize++;
1092			}
1093
1094			if (actualStripSize /* allStrips.at(i).m_faces.size() */
1095			> threshold) {
1096
1097				int numTimes = actualStripSize /* allStrips.at(i).m_faces.size() */
1098						/ threshold;
1099				int numLeftover = actualStripSize /* allStrips.at(i).m_faces.size() */
1100						% threshold;
1101
1102				int degenerateCount = 0;
1103				for (j = 0; j < numTimes; j++) {
1104					currentStrip = new StripInfo(startInfo, 0, -1);
1105
1106					int faceCtr = j * threshold + degenerateCount;
1107					boolean bFirstTime = true;
1108					while (faceCtr < threshold + (j * threshold)
1109							+ degenerateCount) {
1110						if (isDegenerate(allStrips.at(i).m_faces.at(faceCtr))) {
1111							degenerateCount++;
1112
1113							//last time or first time through, no need for a
1114							// degenerate
1115							if ((((faceCtr + 1) != threshold + (j * threshold)
1116									+ degenerateCount) || ((j == numTimes - 1)
1117									&& (numLeftover < 4) && (numLeftover > 0)))
1118									&& !bFirstTime) {
1119								currentStrip.m_faces
1120										.add(allStrips.at(i).m_faces
1121												.at(faceCtr++));
1122							} else
1123								++faceCtr;
1124						} else {
1125							currentStrip.m_faces.add(allStrips.at(i).m_faces
1126									.at(faceCtr++));
1127							bFirstTime = false;
1128						}
1129					}
1130					/*
1131					 * threshold; faceCtr < threshold+(j*threshold); faceCtr++) {
1132					 * currentStrip.m_faces.add(allStrips.at(i).m_faces.at(faceCtr]); }
1133					 */
1134					///*
1135					if (j == numTimes - 1) //last time through
1136					{
1137						if ((numLeftover < 4) && (numLeftover > 0)) //way too
1138						// small
1139						{
1140							//just add to last strip
1141							int ctr = 0;
1142							while (ctr < numLeftover) {
1143								if (!isDegenerate(allStrips.at(i).m_faces
1144										.at(faceCtr))) {
1145									currentStrip.m_faces
1146											.add(allStrips.at(i).m_faces
1147													.at(faceCtr++));
1148									++ctr;
1149								} else {
1150									currentStrip.m_faces
1151											.add(allStrips.at(i).m_faces
1152													.at(faceCtr++));
1153									++degenerateCount;
1154								}
1155							}
1156							numLeftover = 0;
1157						}
1158					}
1159					//*/
1160					tempStrips.add(currentStrip);
1161				}
1162
1163				int leftOff = j * threshold + degenerateCount;
1164
1165				if (numLeftover != 0) {
1166					currentStrip = new StripInfo(startInfo, 0, -1);
1167
1168					int ctr = 0;
1169					boolean bFirstTime = true;
1170					while (ctr < numLeftover) {
1171						if (!isDegenerate(allStrips.at(i).m_faces.at(leftOff))) {
1172							ctr++;
1173							bFirstTime = false;
1174							currentStrip.m_faces.add(allStrips.at(i).m_faces
1175									.at(leftOff++));
1176						} else if (!bFirstTime)
1177							currentStrip.m_faces.add(allStrips.at(i).m_faces
1178									.at(leftOff++));
1179						else
1180							leftOff++;
1181					}
1182					/*
1183					 * for(int k = 0; k < numLeftover; k++) {
1184					 * currentStrip.m_faces.add(allStrips.at(i).m_faces[leftOff++]); }
1185					 */
1186
1187					tempStrips.add(currentStrip);
1188				}
1189			} else {
1190				//we're not just doing a tempStrips.add(allBigStrips[i])
1191				// because
1192				// this way we can delete allBigStrips later to free the memory
1193				currentStrip = new StripInfo(startInfo, 0, -1);
1194
1195				for (j = 0; j < allStrips.at(i).m_faces.size(); j++)
1196					currentStrip.m_faces.add(allStrips.at(i).m_faces.at(j));
1197
1198				tempStrips.add(currentStrip);
1199			}
1200		}
1201
1202		//add small strips to face list
1203		StripInfoVec tempStrips2 = new StripInfoVec();
1204		removeSmallStrips(tempStrips, tempStrips2, outFaceList);
1205
1206		outStrips.clear();
1207		//screw optimization for now
1208		//  for(i = 0; i < tempStrips.size(); ++i)
1209		//    outStrips.add(tempStrips[i]);
1210
1211		if (tempStrips2.size() != 0) {
1212			//Optimize for the vertex cache
1213			VertexCache vcache = new VertexCache(cacheSize);
1214
1215			float bestNumHits = -1.0f;
1216			float numHits;
1217			int bestIndex = -99999;
1218
1219			int firstIndex = 0;
1220			float minCost = 10000.0f;
1221
1222			for (int i = 0; i < tempStrips2.size(); i++) {
1223				int numNeighbors = 0;
1224
1225				//find strip with least number of neighbors per face
1226				for (j = 0; j < tempStrips2.at(i).m_faces.size(); j++) {
1227					numNeighbors += numNeighbors(tempStrips2.at(i).m_faces
1228							.at(j), edgeInfos);
1229				}
1230
1231				float currCost = (float) numNeighbors
1232						/ (float) tempStrips2.at(i).m_faces.size();
1233				if (currCost < minCost) {
1234					minCost = currCost;
1235					firstIndex = i;
1236				}
1237			}
1238
1239			updateCacheStrip(vcache, tempStrips2.at(firstIndex));
1240			outStrips.add(tempStrips2.at(firstIndex));
1241
1242			tempStrips2.at(firstIndex).visited = true;
1243
1244			boolean bWantsCW = (tempStrips2.at(firstIndex).m_faces.size() % 2) == 0;
1245
1246			//this n^2 algo is what slows down stripification so much....
1247			// needs to be improved
1248			while (true) {
1249				bestNumHits = -1.0f;
1250
1251				//find best strip to add next, given the current cache
1252				for (int i = 0; i < tempStrips2.size(); i++) {
1253					if (tempStrips2.at(i).visited)
1254						continue;
1255
1256					numHits = calcNumHitsStrip(vcache, tempStrips2.at(i));
1257					if (numHits > bestNumHits) {
1258						bestNumHits = numHits;
1259						bestIndex = i;
1260					} else if (numHits >= bestNumHits) {
1261						//check previous strip to see if this one requires it
1262						// to switch polarity
1263						StripInfo strip = tempStrips2.at(i);
1264						int nStripFaceCount = strip.m_faces.size();
1265
1266						FaceInfo tFirstFace = new FaceInfo(
1267								strip.m_faces.at(0).m_v0,
1268								strip.m_faces.at(0).m_v1,
1269								strip.m_faces.at(0).m_v2);
1270
1271						// If there is a second face, reorder vertices such
1272						// that the
1273						// unique vertex is first
1274						if (nStripFaceCount > 1) {
1275							int nUnique = getUniqueVertexInB(strip.m_faces
1276									.at(1), tFirstFace);
1277							if (nUnique == tFirstFace.m_v1) {
1278								int tmp = tFirstFace.m_v0;
1279								tFirstFace.m_v0 = tFirstFace.m_v1;
1280								tFirstFace.m_v1 = tmp;
1281							} else if (nUnique == tFirstFace.m_v2) {
1282								int tmp = tFirstFace.m_v0;
1283								tFirstFace.m_v0 = tFirstFace.m_v2;
1284								tFirstFace.m_v2 = tmp;
1285							}
1286
1287							// If there is a third face, reorder vertices such
1288							// that the
1289							// shared vertex is last
1290							if (nStripFaceCount > 2) {
1291								int[] nShared = new int[2];
1292								getSharedVertices(strip.m_faces.at(2),
1293										tFirstFace, nShared);
1294								if ((nShared[0] == tFirstFace.m_v1)
1295										&& (nShared[1] == -1)) {
1296									int tmp = tFirstFace.m_v2;
1297									tFirstFace.m_v2 = tFirstFace.m_v1;
1298									tFirstFace.m_v1 = tmp;
1299								}
1300							}
1301						}
1302
1303						// Check CW/CCW ordering
1304						if (bWantsCW == isCW(strip.m_faces.at(0),
1305								tFirstFace.m_v0, tFirstFace.m_v1)) {
1306							//I like this one!
1307							bestIndex = i;
1308						}
1309					}
1310				}
1311
1312				if (bestNumHits == -1.0f)
1313					break;
1314				tempStrips2.at(bestIndex).visited = true;
1315				updateCacheStrip(vcache, tempStrips2.at(bestIndex));
1316				outStrips.add(tempStrips2.at(bestIndex));
1317				bWantsCW = (tempStrips2.at(bestIndex).m_faces.size() % 2 == 0) ? bWantsCW
1318						: !bWantsCW;
1319			}
1320		}
1321	}
1322
1323	///////////////////////////////////////////////////////////////////////////////////////////
1324	// Stripify()
1325	//
1326	//
1327	// in_indices are the input indices of the mesh to stripify
1328	// in_cacheSize is the target cache size
1329	//
1330	void stripify(IntVec in_indices, int in_cacheSize, int in_minStripLength,
1331			int maxIndex, StripInfoVec outStrips, FaceInfoVec outFaceList) {
1332		meshJump = 0.0f;
1333		bFirstTimeResetPoint = true; //used in FindGoodResetPoint()
1334
1335		//the number of times to run the experiments
1336		int numSamples = 10;
1337
1338		//the cache size, clamped to one
1339		cacheSize = Math.max(1, in_cacheSize - CACHE_INEFFICIENCY);
1340
1341		minStripLength = in_minStripLength;
1342		//this is the strip size threshold below which we dump the strip into
1343		// a list
1344
1345		indices = in_indices;
1346
1347		// build the stripification info
1348		FaceInfoVec allFaceInfos = new FaceInfoVec();
1349		EdgeInfoVec allEdgeInfos = new EdgeInfoVec();
1350
1351		buildStripifyInfo(allFaceInfos, allEdgeInfos, maxIndex);
1352
1353		StripInfoVec allStrips = new StripInfoVec();
1354
1355		// stripify
1356		findAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples);
1357
1358		//split up the strips into cache friendly pieces, optimize them, then
1359		// dump these into outStrips
1360		splitUpStripsAndOptimize(allStrips, outStrips, allEdgeInfos,
1361				outFaceList);
1362
1363	}
1364
1365}