diff --git a/NvTriStrip/NvTriStrip.cpp b/NvTriStrip/NvTriStrip.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a2b60b98c744722e2925445f8e118f27ecce68b
--- /dev/null
+++ b/NvTriStrip/NvTriStrip.cpp
@@ -0,0 +1,498 @@
+
+#include "NvTriStripObjects.h"
+#include "NvTriStrip.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+//private data
+static unsigned int cacheSize    = CACHESIZE_GEFORCE1_2;
+static bool bStitchStrips         = true;
+static unsigned int minStripSize = 0;
+static bool bListsOnly            = false;
+static unsigned int restartVal   = 0;
+static bool bRestart              = false;
+
+void EnableRestart(const unsigned int _restartVal)
+{
+	bRestart = true;
+	restartVal = _restartVal;
+}
+
+void DisableRestart()
+{
+	bRestart = false;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetListsOnly()
+//
+// If set to true, will return an optimized list, with no strips at all.
+//
+// Default value: false
+//
+void SetListsOnly(const bool _bListsOnly)
+{
+	bListsOnly = _bListsOnly;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetCacheSize()
+//
+// 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
+//
+void SetCacheSize(const unsigned int _cacheSize)
+{
+	cacheSize = _cacheSize;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetStitchStrips()
+//
+// 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
+//
+void SetStitchStrips(const bool _bStitchStrips)
+{
+	bStitchStrips = _bStitchStrips;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetMinStripSize()
+//
+// 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
+//
+void SetMinStripSize(const unsigned int _minStripSize)
+{
+	minStripSize = _minStripSize;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+//Cleanup strips / faces, used by generatestrips
+void Cleanup(NvStripInfoVec& tempStrips, NvFaceInfoVec& tempFaces)
+{
+	//delete strips
+	for(size_t i = 0; i < tempStrips.size(); i++)
+	{
+		for(size_t j = 0; j < tempStrips[i]->m_faces.size(); j++)
+		{
+			delete tempStrips[i]->m_faces[j];
+			tempStrips[i]->m_faces[j] = NULL;
+		}
+		tempStrips[i]->m_faces.resize(0);
+		delete tempStrips[i];
+		tempStrips[i] = NULL;
+	}
+
+	//delete faces
+	for(size_t i = 0; i < tempFaces.size(); i++)
+	{
+		delete tempFaces[i];
+		tempFaces[i] = NULL;
+	}
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+//SameTriangle()
+//
+//Returns true if the two triangles defined by firstTri and secondTri are the same
+// The "same" is defined in this case as having the same indices with the same winding order
+//
+bool SameTriangle(unsigned short firstTri0, unsigned short firstTri1, unsigned short firstTri2, 
+				  unsigned short secondTri0, unsigned short secondTri1, unsigned short secondTri2)
+{
+	bool isSame = false;
+
+	if (firstTri0 == secondTri0)
+	{
+		if (firstTri1 == secondTri1)
+		{
+			if (firstTri2 == secondTri2)
+				isSame = true;
+		}
+	}
+	else if (firstTri0 == secondTri1)
+	{
+		if (firstTri1 == secondTri2)
+		{
+			if (firstTri2 == secondTri0)
+				isSame = true;
+		}
+	}
+	else if (firstTri0 == secondTri2)
+	{
+		if (firstTri1 == secondTri0)
+		{
+			if (firstTri2 == secondTri1)
+				isSame = true;
+		}
+	}
+
+	return isSame;
+}
+
+
+bool TestTriangle(const unsigned short v0, const unsigned short v1, const unsigned short v2, const std::vector<NvFaceInfo>* in_bins, const int NUMBINS)
+{
+	//hash this triangle
+	bool isLegit = false;
+	int ctr = v0 % NUMBINS;
+	for (size_t k = 0; k < in_bins[ctr].size(); ++k)
+	{
+		//check triangles in this bin
+		if (SameTriangle(in_bins[ctr][k].m_v0, in_bins[ctr][k].m_v1, in_bins[ctr][k].m_v2, 
+			v0, v1, v2))
+		{
+			isLegit = true;
+			break;
+		}
+	}
+	if (!isLegit)
+	{
+		ctr = v1 % NUMBINS;
+		for (size_t k = 0; k < in_bins[ctr].size(); ++k)
+		{
+			//check triangles in this bin
+			if (SameTriangle(in_bins[ctr][k].m_v0, in_bins[ctr][k].m_v1, in_bins[ctr][k].m_v2, 
+				v0, v1, v2))
+			{
+				isLegit = true;
+				break;
+			}
+		}
+		
+		if (!isLegit)
+		{
+			ctr = v2 % NUMBINS;
+			for (size_t k = 0; k < in_bins[ctr].size(); ++k)
+			{
+				//check triangles in this bin
+				if (SameTriangle(in_bins[ctr][k].m_v0, in_bins[ctr][k].m_v1, in_bins[ctr][k].m_v2, 
+					v0, v1, v2))
+				{
+					isLegit = true;
+					break;
+				}
+			}
+			
+		}
+	}
+
+	return isLegit;
+}
+	
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GenerateStrips()
+//
+// in_indices: input index list, the indices you would use to render
+// in_numIndices: number of entries in in_indices
+// primGroups: array of optimized/stripified PrimitiveGroups
+// numGroups: number of groups returned
+//
+// Be sure to call delete[] on the returned primGroups to avoid leaking mem
+//
+bool GenerateStrips(const unsigned short* in_indices, const unsigned int in_numIndices,
+					PrimitiveGroup** primGroups, unsigned short* numGroups, bool validateEnabled)
+{
+	//put data in format that the stripifier likes
+	WordVec tempIndices;
+	tempIndices.resize(in_numIndices);
+	unsigned short maxIndex = 0;
+	unsigned short minIndex = 0xFFFF;
+	for(size_t i = 0; i < in_numIndices; i++)
+	{
+		tempIndices[i] = in_indices[i];
+		if (in_indices[i] > maxIndex)
+			maxIndex = in_indices[i];
+		if (in_indices[i] < minIndex)
+			minIndex = in_indices[i];
+	}
+	NvStripInfoVec tempStrips;
+	NvFaceInfoVec tempFaces;
+
+	NvStripifier stripifier;
+	
+	//do actual stripification
+	stripifier.Stripify(tempIndices, cacheSize, minStripSize, maxIndex, tempStrips, tempFaces);
+
+	//stitch strips together
+	IntVec stripIndices;
+	unsigned int numSeparateStrips = 0;
+
+	if(bListsOnly)
+	{
+		//if we're outputting only lists, we're done
+		*numGroups = 1;
+		(*primGroups) = new PrimitiveGroup[*numGroups];
+		PrimitiveGroup* primGroupArray = *primGroups;
+
+		//count the total number of indices
+		unsigned int numIndices = 0;
+		for(size_t i = 0; i < tempStrips.size(); i++)
+		{
+			numIndices += tempStrips[i]->m_faces.size() * 3;
+		}
+
+		//add in the list
+		numIndices += tempFaces.size() * 3;
+
+		primGroupArray[0].type       = PT_LIST;
+		primGroupArray[0].numIndices = numIndices;
+		primGroupArray[0].indices    = new unsigned short[numIndices];
+
+		//do strips
+		unsigned int indexCtr = 0;
+		for(size_t i = 0; i < tempStrips.size(); i++)
+		{
+			for(size_t j = 0; j < tempStrips[i]->m_faces.size(); j++)
+			{
+				//degenerates are of no use with lists
+				if(!NvStripifier::IsDegenerate(tempStrips[i]->m_faces[j]))
+				{
+					primGroupArray[0].indices[indexCtr++] = tempStrips[i]->m_faces[j]->m_v0;
+					primGroupArray[0].indices[indexCtr++] = tempStrips[i]->m_faces[j]->m_v1;
+					primGroupArray[0].indices[indexCtr++] = tempStrips[i]->m_faces[j]->m_v2;
+				}
+				else
+				{
+					//we've removed a tri, reduce the number of indices
+					primGroupArray[0].numIndices -= 3;
+				}
+			}
+		}
+
+		//do lists
+		for(size_t i = 0; i < tempFaces.size(); i++)
+		{			
+			primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v0;
+			primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v1;
+			primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v2;
+		}
+	}
+	else
+	{
+		stripifier.CreateStrips(tempStrips, stripIndices, bStitchStrips, numSeparateStrips, bRestart, restartVal);
+
+		//if we're stitching strips together, we better get back only one strip from CreateStrips()
+		assert( (bStitchStrips && (numSeparateStrips == 1)) || !bStitchStrips);
+		
+		//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];
+		
+		PrimitiveGroup* primGroupArray = *primGroups;
+		
+		//first, the strips
+		int startingLoc = 0;
+   		for(size_t stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++)
+		{
+			int stripLength = 0;
+
+			if(!bStitchStrips)
+			{
+				//if we've got multiple strips, we need to figure out the correct length
+				size_t i;
+				for(i = startingLoc; i < stripIndices.size(); i++)
+				{
+					if(stripIndices[i] == -1)
+						break;
+				}
+				
+				stripLength = i - startingLoc;
+			}
+			else
+				stripLength = stripIndices.size();
+			
+			primGroupArray[stripCtr].type       = PT_STRIP;
+			primGroupArray[stripCtr].indices    = new unsigned short[stripLength];
+			primGroupArray[stripCtr].numIndices = stripLength;
+			
+			int indexCtr = 0;
+			for(int i = startingLoc; i < stripLength + startingLoc; i++)
+				primGroupArray[stripCtr].indices[indexCtr++] = stripIndices[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       = PT_LIST;
+			primGroupArray[faceGroupLoc].indices    = new unsigned short[tempFaces.size() * 3];
+			primGroupArray[faceGroupLoc].numIndices = tempFaces.size() * 3;
+			int indexCtr = 0;
+			for(size_t i = 0; i < tempFaces.size(); i++)
+			{
+				primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v0;
+				primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v1;
+				primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v2;
+			}
+		}
+	}
+
+	//validate generated data against input
+	if (validateEnabled)
+	{
+		const int NUMBINS = 100;
+
+		std::vector<NvFaceInfo> in_bins[NUMBINS];
+		
+		//hash input indices on first index
+		for (size_t i = 0; i < in_numIndices; i += 3)
+		{
+			NvFaceInfo faceInfo(in_indices[i], in_indices[i + 1], in_indices[i + 2]);
+			in_bins[in_indices[i] % NUMBINS].push_back(faceInfo);
+		}
+		
+		for (int i = 0; i < *numGroups; ++i)
+		{
+			switch ((*primGroups)[i].type)
+			{
+				case PT_LIST:
+				{
+					for (size_t j = 0; j < (*primGroups)[i].numIndices; j += 3)
+					{
+						unsigned short v0 = (*primGroups)[i].indices[j];
+						unsigned short v1 = (*primGroups)[i].indices[j + 1];
+						unsigned short v2 = (*primGroups)[i].indices[j + 2];
+						
+						//ignore degenerates
+						if (NvStripifier::IsDegenerate(v0, v1, v2))
+							continue;
+
+						if (!TestTriangle(v0, v1, v2, in_bins, NUMBINS))
+						{
+							Cleanup(tempStrips, tempFaces);
+							return false;
+						}
+					}
+					break;
+				}
+
+				case PT_STRIP:
+				{
+					//int brokenCtr = 0;
+					bool flip = false;
+					for (size_t j = 2; j < (*primGroups)[i].numIndices; ++j)
+					{
+						unsigned short v0 = (*primGroups)[i].indices[j - 2];
+						unsigned short v1 = (*primGroups)[i].indices[j - 1];
+						unsigned short v2 = (*primGroups)[i].indices[j];
+						
+						if (flip)
+						{
+							//swap v1 and v2
+							unsigned short swap = v1;
+							v1 = v2;
+							v2 = swap;
+						}
+
+						//ignore degenerates
+						if (NvStripifier::IsDegenerate(v0, v1, v2))
+						{
+							flip = !flip;
+							continue;
+						}
+
+						if (!TestTriangle(v0, v1, v2, in_bins, NUMBINS))
+						{
+							Cleanup(tempStrips, tempFaces);
+							return false;
+						}
+
+						flip = !flip;
+					}
+					break;
+				}
+
+				case PT_FAN:
+				default:
+					break;
+			}
+		}
+
+	}
+
+	//clean up everything
+	Cleanup(tempStrips, tempFaces);
+
+	return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RemapIndices()
+//
+// 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.
+//
+void RemapIndices(const PrimitiveGroup* in_primGroups, const unsigned short numGroups,
+				  const unsigned short numVerts, PrimitiveGroup** remappedGroups)
+{
+	(*remappedGroups) = new PrimitiveGroup[numGroups];
+
+	//caches oldIndex --> newIndex conversion
+	int *indexCache;
+	indexCache = new int[numVerts];
+	memset(indexCache, -1, sizeof(int)*numVerts);
+	
+	//loop over primitive groups
+	unsigned int indexCtr = 0;
+	for(int i = 0; i < numGroups; i++)
+	{
+		unsigned int numIndices = in_primGroups[i].numIndices;
+
+		//init remapped group
+		(*remappedGroups)[i].type       = in_primGroups[i].type;
+		(*remappedGroups)[i].numIndices = numIndices;
+		(*remappedGroups)[i].indices    = new unsigned short[numIndices];
+
+		for(size_t j = 0; j < numIndices; j++)
+		{
+			int cachedIndex = indexCache[in_primGroups[i].indices[j]];
+			if(cachedIndex == -1) //we haven't seen this index before
+			{
+				//point to "last" vertex in VB
+				(*remappedGroups)[i].indices[j] = indexCtr;
+
+				//add to index cache, increment
+				indexCache[in_primGroups[i].indices[j]] = indexCtr++;
+			}
+			else
+			{
+				//we've seen this index before
+				(*remappedGroups)[i].indices[j] = cachedIndex;
+			}
+		}
+	}
+
+	delete[] indexCache;
+}
\ No newline at end of file
diff --git a/NvTriStrip/NvTriStrip.h b/NvTriStrip/NvTriStrip.h
new file mode 100644
index 0000000000000000000000000000000000000000..398a7364577d6107601866ff9bce79451908d83d
--- /dev/null
+++ b/NvTriStrip/NvTriStrip.h
@@ -0,0 +1,143 @@
+#ifndef NVTRISTRIP_H
+#define NVTRISTRIP_H
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////
+// Public interface for stripifier
+////////////////////////////////////////////////////////////////////////////////////////
+
+//GeForce1 and 2 cache size
+#define CACHESIZE_GEFORCE1_2 16
+
+//GeForce3 cache size
+#define CACHESIZE_GEFORCE3   24
+
+enum PrimType
+{
+	PT_LIST,
+	PT_STRIP,
+	PT_FAN
+};
+
+struct PrimitiveGroup
+{
+	PrimType type;
+	unsigned int numIndices;
+	unsigned short* indices;
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+	PrimitiveGroup() : type(PT_STRIP), numIndices(0), indices(NULL) {}
+	~PrimitiveGroup()
+	{
+		if(indices)
+			delete[] indices;
+		indices = NULL;
+	}
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// EnableRestart()
+//
+// For GPUs that support primitive restart, this sets a value as the restart index
+//
+// Restart is meaningless if strips are not being stitched together, so enabling restart
+//  makes NvTriStrip forcing stitching.  So, you'll get back one strip.
+//
+// Default value: disabled
+//
+void EnableRestart(const unsigned int restartVal);
+
+////////////////////////////////////////////////////////////////////////////////////////
+// DisableRestart()
+//
+// For GPUs that support primitive restart, this disables using primitive restart
+//
+void DisableRestart();
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetCacheSize()
+//
+// 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
+//
+void SetCacheSize(const unsigned int cacheSize);
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetStitchStrips()
+//
+// 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
+//
+void SetStitchStrips(const bool bStitchStrips);
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetMinStripSize()
+//
+// 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
+//
+void SetMinStripSize(const unsigned int minSize);
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetListsOnly()
+//
+// If set to true, will return an optimized list, with no strips at all.
+//
+// Default value: false
+//
+void SetListsOnly(const bool bListsOnly);
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GenerateStrips()
+//
+// in_indices: input index list, the indices you would use to render
+// in_numIndices: number of entries in in_indices
+// primGroups: array of optimized/stripified PrimitiveGroups
+// numGroups: number of groups returned
+//
+// Be sure to call delete[] on the returned primGroups to avoid leaking mem
+//
+bool GenerateStrips(const unsigned short* in_indices, const unsigned int in_numIndices,
+					PrimitiveGroup** primGroups, unsigned short* numGroups, bool validateEnabled = false);
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RemapIndices()
+//
+// 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.
+//
+// Credit goes to the MS Xbox crew for the idea for this interface.
+//
+void RemapIndices(const PrimitiveGroup* in_primGroups, const unsigned short numGroups, 
+				  const unsigned short numVerts, PrimitiveGroup** remappedGroups);
+
+#endif
\ No newline at end of file
diff --git a/NvTriStrip/NvTriStripObjects.cpp b/NvTriStrip/NvTriStripObjects.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..738169d3f63786a540e576f6a6839e6c9abf37ec
--- /dev/null
+++ b/NvTriStrip/NvTriStripObjects.cpp
@@ -0,0 +1,1769 @@
+
+#pragma warning( disable : 4786 )  
+
+#include <assert.h>
+#include <set>
+#include "NvTriStripObjects.h"
+#include "VertexCache.h"
+
+#define CACHE_INEFFICIENCY 6
+
+NvStripifier::NvStripifier()
+{
+	
+}
+
+NvStripifier::~NvStripifier()
+{
+	
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// FindEdgeInfo()
+//
+// find the edge info for these two indices
+//
+NvEdgeInfo * NvStripifier::FindEdgeInfo(NvEdgeInfoVec &edgeInfos, int v0, int v1){
+	
+	// we can get to it through either array
+	// because the edge infos have a v0 and v1
+	// and there is no order except how it was
+	// first created.
+	NvEdgeInfo *infoIter = edgeInfos[v0];
+	while (infoIter != NULL){
+		if (infoIter->m_v0 == v0){
+			if (infoIter->m_v1 == v1)
+				return infoIter;
+			else
+				infoIter = infoIter->m_nextV0;
+		}
+		else {
+			assert(infoIter->m_v1 == v0);
+			if (infoIter->m_v0 == v1)
+				return infoIter;
+			else
+				infoIter = infoIter->m_nextV1;
+		}
+	}
+	return NULL;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// FindOtherFace
+//
+// find the other face sharing these vertices
+// exactly like the edge info above
+//
+NvFaceInfo * NvStripifier::FindOtherFace(NvEdgeInfoVec &edgeInfos, int v0, int v1, NvFaceInfo *faceInfo){
+	NvEdgeInfo *edgeInfo = FindEdgeInfo(edgeInfos, v0, v1);
+
+	if( (edgeInfo == NULL) && (v0 == v1))
+	{
+		//we've hit a degenerate
+		return NULL;
+	}
+
+	assert(edgeInfo != NULL);
+	return (edgeInfo->m_face0 == faceInfo ? edgeInfo->m_face1 : edgeInfo->m_face0);
+}
+
+
+bool NvStripifier::AlreadyExists(NvFaceInfo* faceInfo, NvFaceInfoVec& faceInfos)
+{
+	for(size_t i = 0; i < faceInfos.size(); ++i)
+	{
+		if( (faceInfos[i]->m_v0 == faceInfo->m_v0) &&
+			(faceInfos[i]->m_v1 == faceInfo->m_v1) &&
+			(faceInfos[i]->m_v2 == faceInfo->m_v2) )
+			return true;
+	}
+
+	return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// BuildStripifyInfo()
+//
+// Builds the list of all face and edge infos
+//
+void NvStripifier::BuildStripifyInfo(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos,
+									 const unsigned short maxIndex)
+{
+	// reserve space for the face infos, but do not resize them.
+	int numIndices = indices.size();
+	faceInfos.reserve(numIndices / 3);
+	
+	// we actually resize the edge infos, so we must initialize to NULL
+	edgeInfos.resize(maxIndex + 1);
+	for (int i = 0; i < maxIndex + 1; i++)
+		edgeInfos[i] = NULL;
+	
+	// iterate through the triangles of the triangle list
+	int numTriangles = numIndices / 3;
+	int index        = 0;
+	bool bFaceUpdated[3];
+
+	for (int i = 0; i < numTriangles; i++)
+	{	
+		bool bMightAlreadyExist = true;
+		bFaceUpdated[0] = false;
+		bFaceUpdated[1] = false;
+		bFaceUpdated[2] = false;
+
+		// grab the indices
+		int v0 = indices[index++];
+		int v1 = indices[index++];
+		int v2 = indices[index++];
+
+		//we disregard degenerates
+		if(IsDegenerate(v0, v1, v2))
+			continue;
+		
+		// create the face info and add it to the list of faces, but only if this exact face doesn't already 
+		//  exist in the list
+		NvFaceInfo *faceInfo = new NvFaceInfo(v0, v1, v2);
+		
+		// grab the edge infos, creating them if they do not already exist
+		NvEdgeInfo *edgeInfo01 = FindEdgeInfo(edgeInfos, v0, v1);
+		if (edgeInfo01 == NULL)
+		{
+			//since one of it's edges isn't in the edge data structure, it can't already exist in the face structure
+			bMightAlreadyExist = false;
+
+			// create the info
+			edgeInfo01 = new NvEdgeInfo(v0, v1);
+			
+			// update the linked list on both 
+			edgeInfo01->m_nextV0 = edgeInfos[v0];
+			edgeInfo01->m_nextV1 = edgeInfos[v1];
+			edgeInfos[v0] = edgeInfo01;
+			edgeInfos[v1] = edgeInfo01;
+			
+			// set face 0
+			edgeInfo01->m_face0 = faceInfo;
+		}
+		else 
+		{
+			if (edgeInfo01->m_face1 != NULL)
+			{
+				printf("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n");
+			}
+			else
+			{
+				edgeInfo01->m_face1 = faceInfo;
+				bFaceUpdated[0] = true;
+			}
+		}
+		
+		// grab the edge infos, creating them if they do not already exist
+		NvEdgeInfo *edgeInfo12 = FindEdgeInfo(edgeInfos, v1, v2);
+		if (edgeInfo12 == NULL)
+		{
+			bMightAlreadyExist = false;
+			
+			// create the info
+			edgeInfo12 = new NvEdgeInfo(v1, v2);
+			
+			// update the linked list on both 
+			edgeInfo12->m_nextV0 = edgeInfos[v1];
+			edgeInfo12->m_nextV1 = edgeInfos[v2];
+			edgeInfos[v1] = edgeInfo12;
+			edgeInfos[v2] = edgeInfo12;
+			
+			// set face 0
+			edgeInfo12->m_face0 = faceInfo;
+		}
+		else 
+		{
+			if (edgeInfo12->m_face1 != NULL)
+			{
+				printf("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n");
+			}
+			else
+			{
+				edgeInfo12->m_face1 = faceInfo;
+				bFaceUpdated[1] = true;
+			}
+		}
+		
+		// grab the edge infos, creating them if they do not already exist
+		NvEdgeInfo *edgeInfo20 = FindEdgeInfo(edgeInfos, v2, v0);
+		if (edgeInfo20 == NULL)
+		{
+			bMightAlreadyExist = false;
+
+			// create the info
+			edgeInfo20 = new NvEdgeInfo(v2, v0);
+			
+			// update the linked list on both 
+			edgeInfo20->m_nextV0 = edgeInfos[v2];
+			edgeInfo20->m_nextV1 = edgeInfos[v0];
+			edgeInfos[v2] = edgeInfo20;
+			edgeInfos[v0] = edgeInfo20;
+			
+			// set face 0
+			edgeInfo20->m_face0 = faceInfo;
+		}
+		else 
+		{
+			if (edgeInfo20->m_face1 != NULL)
+			{
+				printf("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n");
+			}
+			else
+			{
+				edgeInfo20->m_face1 = faceInfo;
+				bFaceUpdated[2] = true;
+			}
+		}
+
+		if(bMightAlreadyExist)
+		{
+			if(!AlreadyExists(faceInfo, faceInfos))
+				faceInfos.push_back(faceInfo);
+			else
+			{
+				delete faceInfo;
+
+				//cleanup pointers that point to this deleted face
+				if(bFaceUpdated[0])
+					edgeInfo01->m_face1 = NULL;
+				if(bFaceUpdated[1])
+					edgeInfo12->m_face1 = NULL;
+				if(bFaceUpdated[2])
+					edgeInfo20->m_face1 = NULL;
+			}
+		}
+		else
+		{
+			faceInfos.push_back(faceInfo);
+		}
+
+	}
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// FindStartPoint()
+//
+// Finds a good starting point, namely one which has only one neighbor
+//
+int NvStripifier::FindStartPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos)
+{
+	int bestCtr = -1;
+	int bestIndex = -1;
+
+	for(size_t i = 0; i < faceInfos.size(); i++)
+	{
+		int ctr = 0;
+		
+		if(FindOtherFace(edgeInfos, faceInfos[i]->m_v0, faceInfos[i]->m_v1, faceInfos[i]) == NULL)
+			ctr++;
+		if(FindOtherFace(edgeInfos, faceInfos[i]->m_v1, faceInfos[i]->m_v2, faceInfos[i]) == NULL)
+			ctr++;
+		if(FindOtherFace(edgeInfos, faceInfos[i]->m_v2, faceInfos[i]->m_v0, faceInfos[i]) == NULL)
+			ctr++;
+		if(ctr > bestCtr)
+		{
+			bestCtr = ctr;
+			bestIndex = i;
+			//return i;
+		}
+	}
+	//return -1;
+	
+	if(bestCtr == 0)
+		return -1;
+	else
+		return bestIndex;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// FindGoodResetPoint()
+//  
+// A good reset point is one near other commited areas so that
+// we know that when we've made the longest strips its because
+// we're stripifying in the same general orientation.
+//
+NvFaceInfo* NvStripifier::FindGoodResetPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos){
+	// we hop into different areas of the mesh to try to get
+	// other large open spans done.  Areas of small strips can
+	// just be left to triangle lists added at the end.
+	NvFaceInfo *result = NULL;
+	
+	if(result == NULL)
+	{
+		int numFaces   = faceInfos.size();
+      if (numFaces == 0)
+         return NULL;
+
+		int startPoint;
+		if(bFirstTimeResetPoint)
+		{
+			//first time, find a face with few neighbors (look for an edge of the mesh)
+			startPoint = FindStartPoint(faceInfos, edgeInfos);
+			bFirstTimeResetPoint = false;
+		}
+		else
+			startPoint = (int)(((float) numFaces - 1) * meshJump);
+		
+		if(startPoint == -1)
+		{
+			startPoint = (int)(((float) numFaces - 1) * meshJump);
+			
+			//meshJump += 0.1f;
+			//if (meshJump > 1.0f)
+			//	meshJump = .05f;
+		}
+
+		int i = startPoint;
+		do {
+			
+			// if this guy isn't visited, try him
+			if (faceInfos[i]->m_stripId < 0){
+				result = faceInfos[i];
+				break;
+			}
+			
+			// update the index and clamp to 0-(numFaces-1)
+			if (++i >= numFaces)
+				i = 0;
+			
+		} while (i != startPoint);
+		
+		// update the meshJump
+		meshJump += 0.1f;
+		if (meshJump > 1.0f)
+			meshJump = .05f;
+	}
+	
+	// return the best face we found
+	return result;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// GetUniqueVertexInB()
+//
+// Returns the vertex unique to faceB
+//
+int NvStripifier::GetUniqueVertexInB(NvFaceInfo *faceA, NvFaceInfo *faceB){
+	
+	int facev0 = faceB->m_v0;
+	if (facev0 != faceA->m_v0 &&
+		facev0 != faceA->m_v1 &&
+		facev0 != faceA->m_v2)
+		return facev0;
+	
+	int facev1 = faceB->m_v1;
+	if (facev1 != faceA->m_v0 &&
+		facev1 != faceA->m_v1 &&
+		facev1 != faceA->m_v2)
+		return facev1;
+	
+	int facev2 = faceB->m_v2;
+	if (facev2 != faceA->m_v0 &&
+		facev2 != faceA->m_v1 &&
+		facev2 != faceA->m_v2)
+		return facev2;
+	
+	// nothing is different
+	return -1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// GetSharedVertices()
+//
+// Returns the (at most) two vertices shared between the two faces
+//
+void NvStripifier::GetSharedVertices(NvFaceInfo *faceA, NvFaceInfo *faceB, int* vertex0, int* vertex1)
+{
+	*vertex0 = -1;
+	*vertex1 = -1;
+
+	int facev0 = faceB->m_v0;
+	if (facev0 == faceA->m_v0 ||
+		facev0 == faceA->m_v1 ||
+		facev0 == faceA->m_v2)
+	{
+		if(*vertex0 == -1)
+			*vertex0 = facev0;
+		else
+		{
+			*vertex1 = facev0;
+			return;
+		}
+	}
+	
+	int facev1 = faceB->m_v1;
+	if (facev1 == faceA->m_v0 ||
+		facev1 == faceA->m_v1 ||
+		facev1 == faceA->m_v2)
+	{
+		if(*vertex0 == -1)
+			*vertex0 = facev1;
+		else
+		{
+			*vertex1 = facev1;
+			return;
+		}
+	}	
+
+	int facev2 = faceB->m_v2;
+	if (facev2 == faceA->m_v0 ||
+		facev2 == faceA->m_v1 ||
+		facev2 == faceA->m_v2)
+	{
+		if(*vertex0 == -1)
+			*vertex0 = facev2;
+		else
+		{
+			*vertex1 = facev2;
+			return;
+		}
+	}
+
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// GetNextIndex()
+//
+// Returns vertex of the input face which is "next" in the input index list
+//
+inline int NvStripifier::GetNextIndex(const WordVec &indices, NvFaceInfo *face){
+	
+	int numIndices = indices.size();
+	assert(numIndices >= 2);
+	
+	int v0  = indices[numIndices-2];
+	int v1  = indices[numIndices-1];
+	
+	int fv0 = face->m_v0;
+	int fv1 = face->m_v1;
+	int fv2 = face->m_v2;
+	
+	if (fv0 != v0 && fv0 != v1){
+		if ((fv1 != v0 && fv1 != v1) || (fv2 != v0 && fv2 != v1)){
+			printf("GetNextIndex: Triangle doesn't have all of its vertices\n");
+			printf("GetNextIndex: Duplicate triangle probably got us derailed\n");
+		}
+		return fv0;
+	}
+	if (fv1 != v0 && fv1 != v1){
+		if ((fv0 != v0 && fv0 != v1) || (fv2 != v0 && fv2 != v1)){
+			printf("GetNextIndex: Triangle doesn't have all of its vertices\n");
+			printf("GetNextIndex: Duplicate triangle probably got us derailed\n");
+		}
+		return fv1;
+	}
+	if (fv2 != v0 && fv2 != v1){
+		if ((fv0 != v0 && fv0 != v1) || (fv1 != v0 && fv1 != v1)){
+			printf("GetNextIndex: Triangle doesn't have all of its vertices\n");
+			printf("GetNextIndex: Duplicate triangle probably got us derailed\n");
+		}
+		return fv2;
+	}
+	
+	// shouldn't get here, but let's try and fail gracefully
+	if( (fv0 == fv1) || (fv0 == fv2) )
+		return fv0;
+	else if( (fv1 == fv0) || (fv1 == fv2) )
+		return fv1;
+	else if( (fv2 == fv0) || (fv2 == fv1) )
+		return fv2;
+	else
+		return -1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// IsMarked()
+//
+// If either the faceInfo has a real strip index because it is
+// already assign to a committed strip OR it is assigned in an
+// experiment and the experiment index is the one we are building
+// for, then it is marked and unavailable
+inline bool NvStripInfo::IsMarked(NvFaceInfo *faceInfo){
+	return (faceInfo->m_stripId >= 0) || (IsExperiment() && faceInfo->m_experimentId == m_experimentId);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// MarkTriangle()
+//
+// Marks the face with the current strip ID
+//
+inline void NvStripInfo::MarkTriangle(NvFaceInfo *faceInfo){
+	assert(!IsMarked(faceInfo));
+	if (IsExperiment()){
+		faceInfo->m_experimentId = m_experimentId;
+		faceInfo->m_testStripId  = m_stripId;
+    }
+	else{
+		assert(faceInfo->m_stripId == -1);
+		faceInfo->m_experimentId = -1;
+		faceInfo->m_stripId      = m_stripId;
+	}
+}
+
+
+bool NvStripInfo::Unique(NvFaceInfoVec& faceVec, NvFaceInfo* face)
+{
+	bool bv0, bv1, bv2; //bools to indicate whether a vertex is in the faceVec or not
+	bv0 = bv1 = bv2 = false;
+
+	for(size_t i = 0; i < faceVec.size(); i++)
+	{
+		if(!bv0)
+		{
+			if( (faceVec[i]->m_v0 == face->m_v0) || 
+				(faceVec[i]->m_v1 == face->m_v0) ||
+				(faceVec[i]->m_v2 == face->m_v0) )
+				bv0 = true;
+		}
+
+		if(!bv1)
+		{
+			if( (faceVec[i]->m_v0 == face->m_v1) || 
+				(faceVec[i]->m_v1 == face->m_v1) ||
+				(faceVec[i]->m_v2 == face->m_v1) )
+				bv1 = true;
+		}
+
+		if(!bv2)
+		{
+			if( (faceVec[i]->m_v0 == face->m_v2) || 
+				(faceVec[i]->m_v1 == face->m_v2) ||
+				(faceVec[i]->m_v2 == face->m_v2) )
+				bv2 = true;
+		}
+
+		//the face is not unique, all it's vertices exist in the face vector
+		if(bv0 && bv1 && bv2)
+			return false;
+	}
+	
+	//if we get out here, it's unique
+	return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// Build()
+//
+// Builds a strip forward as far as we can go, then builds backwards, and joins the two lists
+//
+void NvStripInfo::Build(NvEdgeInfoVec &edgeInfos, NvFaceInfoVec &faceInfos)
+{
+	// used in building the strips forward and backward
+	WordVec scratchIndices;
+	
+	// build forward... start with the initial face
+	NvFaceInfoVec forwardFaces, backwardFaces;
+	forwardFaces.push_back(m_startInfo.m_startFace);
+
+	MarkTriangle(m_startInfo.m_startFace);
+	
+	int v0 = (m_startInfo.m_toV1 ? m_startInfo.m_startEdge->m_v0 : m_startInfo.m_startEdge->m_v1);
+	int v1 = (m_startInfo.m_toV1 ? m_startInfo.m_startEdge->m_v1 : m_startInfo.m_startEdge->m_v0);
+	
+	// easiest way to get v2 is to use this function which requires the
+	// other indices to already be in the list.
+	scratchIndices.push_back(v0);
+	scratchIndices.push_back(v1);
+	int v2 = NvStripifier::GetNextIndex(scratchIndices, m_startInfo.m_startFace);
+	scratchIndices.push_back(v2);
+
+	//
+	// build the forward list
+	//
+	int nv0 = v1;
+	int nv1 = v2;
+
+	NvFaceInfo *nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, m_startInfo.m_startFace);
+	while (nextFace != NULL && !IsMarked(nextFace))
+	{
+		//check to see if this next face is going to cause us to die soon
+		int testnv0 = nv1;
+		int testnv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
+		
+		NvFaceInfo* nextNextFace = NvStripifier::FindOtherFace(edgeInfos, testnv0, testnv1, nextFace);
+
+		if( (nextNextFace == NULL) || (IsMarked(nextNextFace)) )
+		{
+			//uh, oh, we're following a dead end, try swapping
+			NvFaceInfo* testNextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, testnv1, nextFace);
+
+			if( ((testNextFace != NULL) && !IsMarked(testNextFace)) )
+			{
+				//we only swap if it buys us something
+				
+				//add a "fake" degenerate face
+				NvFaceInfo* tempFace = new NvFaceInfo(nv0, nv1, nv0, true);
+				
+				forwardFaces.push_back(tempFace);
+				MarkTriangle(tempFace);
+
+				scratchIndices.push_back(nv0);
+				testnv0 = nv0;
+
+				++m_numDegenerates;
+			}
+
+		}
+
+		// add this to the strip
+		forwardFaces.push_back(nextFace);
+
+		MarkTriangle(nextFace);
+		
+		// add the index
+		//nv0 = nv1;
+		//nv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
+		scratchIndices.push_back(testnv1);
+		
+		// and get the next face
+		nv0 = testnv0;
+		nv1 = testnv1;
+
+		nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, nextFace);
+	
+	}
+	
+	// tempAllFaces is going to be forwardFaces + backwardFaces
+	// it's used for Unique()
+	NvFaceInfoVec tempAllFaces;
+	for(size_t i = 0; i < forwardFaces.size(); i++)
+		tempAllFaces.push_back(forwardFaces[i]);
+
+	//
+	// reset the indices for building the strip backwards and do so
+	//
+	scratchIndices.resize(0);
+	scratchIndices.push_back(v2);
+	scratchIndices.push_back(v1);
+	scratchIndices.push_back(v0);
+	nv0 = v1;
+	nv1 = v0;
+	nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, m_startInfo.m_startFace);
+	while (nextFace != NULL && !IsMarked(nextFace))
+	{
+		//this tests to see if a face is "unique", meaning that its vertices aren't already in the list
+		// so, strips which "wrap-around" are not allowed
+		if(!Unique(tempAllFaces, nextFace))
+			break;
+
+		//check to see if this next face is going to cause us to die soon
+		int testnv0 = nv1;
+		int testnv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
+		
+		NvFaceInfo* nextNextFace = NvStripifier::FindOtherFace(edgeInfos, testnv0, testnv1, nextFace);
+
+		if( (nextNextFace == NULL) || (IsMarked(nextNextFace)) )
+		{
+			//uh, oh, we're following a dead end, try swapping
+			NvFaceInfo* testNextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, testnv1, nextFace);
+			if( ((testNextFace != NULL) && !IsMarked(testNextFace)) )
+			{
+				//we only swap if it buys us something
+				
+				//add a "fake" degenerate face
+				NvFaceInfo* tempFace = new NvFaceInfo(nv0, nv1, nv0, true);
+
+				backwardFaces.push_back(tempFace);
+				MarkTriangle(tempFace);
+				scratchIndices.push_back(nv0);
+				testnv0 = nv0;
+
+				++m_numDegenerates;
+			}
+				
+		}
+
+		// add this to the strip
+		backwardFaces.push_back(nextFace);
+		
+		//this is just so Unique() will work
+		tempAllFaces.push_back(nextFace);
+
+		MarkTriangle(nextFace);
+		
+		// add the index
+		//nv0 = nv1;
+		//nv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
+		scratchIndices.push_back(testnv1);
+		
+		// and get the next face
+		nv0 = testnv0;
+		nv1 = testnv1;
+		nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, nextFace);
+	}
+	
+	// Combine the forward and backwards stripification lists and put into our own face vector
+	Combine(forwardFaces, backwardFaces);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// Combine()
+//
+// Combines the two input face vectors and puts the result into m_faces
+//
+void NvStripInfo::Combine(const NvFaceInfoVec &forward, const NvFaceInfoVec &backward){
+	
+	// add backward faces
+	int numFaces = backward.size();
+	for (int i = numFaces - 1; i >= 0; i--)
+		m_faces.push_back(backward[i]);
+	
+	// add forward faces
+	numFaces = forward.size();
+	for (int i = 0; i < numFaces; i++)
+		m_faces.push_back(forward[i]);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// SharesEdge()
+//
+// Returns true if the input face and the current strip share an edge
+//
+bool NvStripInfo::SharesEdge(const NvFaceInfo* faceInfo, NvEdgeInfoVec &edgeInfos)
+{
+	//check v0->v1 edge
+	NvEdgeInfo* currEdge = NvStripifier::FindEdgeInfo(edgeInfos, faceInfo->m_v0, faceInfo->m_v1);
+	
+	if(IsInStrip(currEdge->m_face0) || IsInStrip(currEdge->m_face1))
+		return true;
+	
+	//check v1->v2 edge
+	currEdge = NvStripifier::FindEdgeInfo(edgeInfos, faceInfo->m_v1, faceInfo->m_v2);
+	
+	if(IsInStrip(currEdge->m_face0) || IsInStrip(currEdge->m_face1))
+		return true;
+	
+	//check v2->v0 edge
+	currEdge = NvStripifier::FindEdgeInfo(edgeInfos, faceInfo->m_v2, faceInfo->m_v0);
+	
+	if(IsInStrip(currEdge->m_face0) || IsInStrip(currEdge->m_face1))
+		return true;
+	
+	return false;
+	
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// CommitStrips()
+//
+// "Commits" the input strips by setting their m_experimentId to -1 and adding to the allStrips
+//  vector
+//
+void NvStripifier::CommitStrips(NvStripInfoVec &allStrips, const NvStripInfoVec &strips)
+{	
+	// Iterate through strips
+	int numStrips = strips.size();
+	for (int i = 0; i < numStrips; i++){
+
+		// Tell the strip that it is now real
+		NvStripInfo *strip = strips[i];
+		strip->m_experimentId = -1;
+		
+		// add to the list of real strips
+		allStrips.push_back(strip);
+		
+		// Iterate through the faces of the strip
+		// Tell the faces of the strip that they belong to a real strip now
+		const NvFaceInfoVec &faces = strips[i]->m_faces;
+		int numFaces = faces.size();
+
+		for (int j = 0; j < numFaces; j++)
+		{
+			strip->MarkTriangle(faces[j]);
+		}
+	}
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// FindTraversal()
+//
+// Finds the next face to start the next strip on.
+//
+bool NvStripifier::FindTraversal(NvFaceInfoVec &faceInfos,
+								 NvEdgeInfoVec    &edgeInfos,
+								 NvStripInfo      *strip,
+								 NvStripStartInfo &startInfo){
+	
+	// if the strip was v0->v1 on the edge, then v1 will be a vertex in the next edge.
+	int v = (strip->m_startInfo.m_toV1 ? strip->m_startInfo.m_startEdge->m_v1 : strip->m_startInfo.m_startEdge->m_v0);
+	
+	NvFaceInfo *untouchedFace = NULL;
+	NvEdgeInfo *edgeIter      = edgeInfos[v];
+	while (edgeIter != NULL){
+		NvFaceInfo *face0 = edgeIter->m_face0;
+		NvFaceInfo *face1 = edgeIter->m_face1;
+		if ((face0 != NULL && !strip->IsInStrip(face0)) && face1 != NULL && !strip->IsMarked(face1))
+		{
+			untouchedFace = face1;
+			break;
+		}
+		if ((face1 != NULL && !strip->IsInStrip(face1)) && face0 != NULL && !strip->IsMarked(face0)){
+			untouchedFace = face0;
+			break;
+		}
+		
+		// find the next edgeIter
+		edgeIter = (edgeIter->m_v0 == v ? edgeIter->m_nextV0 : edgeIter->m_nextV1);
+	}
+	
+	startInfo.m_startFace = untouchedFace;
+	startInfo.m_startEdge = edgeIter;
+	if (edgeIter != NULL)
+	{
+		if(strip->SharesEdge(startInfo.m_startFace, edgeInfos))
+			startInfo.m_toV1 = (edgeIter->m_v0 == v);  //note! used to be m_v1
+		else
+			startInfo.m_toV1 = (edgeIter->m_v1 == v);
+	}
+	return (startInfo.m_startFace != NULL);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RemoveSmallStrips()
+//
+// allStrips is the whole strip vector...all small strips will be deleted from this list, to avoid leaking mem
+// allBigStrips is an out parameter which will contain all strips above minStripLength
+// faceList is an out parameter which will contain all faces which were removed from the striplist
+//
+void NvStripifier::RemoveSmallStrips(NvStripInfoVec& allStrips, NvStripInfoVec& allBigStrips, NvFaceInfoVec& faceList)
+{
+	faceList.clear();
+	allBigStrips.clear();  //make sure these are empty
+	NvFaceInfoVec tempFaceList;
+	
+	for(size_t i = 0; i < allStrips.size(); i++)
+	{
+		if(allStrips[i]->m_faces.size() < size_t(minStripLength))
+		{
+			//strip is too small, add faces to faceList
+			for(size_t j = 0; j < allStrips[i]->m_faces.size(); j++)
+				tempFaceList.push_back(allStrips[i]->m_faces[j]);
+			
+			//and free memory
+			delete allStrips[i];
+		}
+		else
+		{
+			allBigStrips.push_back(allStrips[i]);
+		}
+	}
+	
+	if(tempFaceList.size())
+	{
+		bool *bVisitedList = new bool[tempFaceList.size()];
+		memset(bVisitedList, 0, tempFaceList.size()*sizeof(bool));
+		
+		VertexCache* vcache = new VertexCache(cacheSize);
+		
+		int bestNumHits = -1;
+		int numHits;
+		int bestIndex = -1;
+		
+		while(1)
+		{
+			bestNumHits = -1;
+			
+			//find best face to add next, given the current cache
+			for(size_t i = 0; i < tempFaceList.size(); i++)
+			{
+				if(bVisitedList[i])
+					continue;
+				
+				numHits = CalcNumHitsFace(vcache, tempFaceList[i]);
+				if(numHits > bestNumHits)
+				{
+					bestNumHits = numHits;
+					bestIndex = i;
+				}
+			}
+			
+			if(bestNumHits == -1.0f)
+				break;
+			bVisitedList[bestIndex] = true;
+			UpdateCacheFace(vcache, tempFaceList[bestIndex]);
+			faceList.push_back(tempFaceList[bestIndex]);
+		}
+		
+		delete vcache;
+		delete[] bVisitedList;
+	}
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// NextIsCW()
+//
+// Returns true if the next face should be ordered in CW fashion
+//
+bool NvStripifier::NextIsCW(const int numIndices)
+{
+	return ((numIndices % 2) == 0);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// IsCW()
+//
+// Returns true if the face is ordered in CW fashion
+//
+bool NvStripifier::IsCW(NvFaceInfo *faceInfo, int v0, int v1)
+{
+	if (faceInfo->m_v0 == v0)
+		return (faceInfo->m_v1 == v1);
+	
+	else if (faceInfo->m_v1 == v0)
+		return (faceInfo->m_v2 == v1);
+	
+	else 
+		return (faceInfo->m_v0 == v1);
+	
+	// shouldn't get here
+	assert(0);
+	return false;
+}
+
+bool NvStripifier::FaceContainsIndex(const NvFaceInfo& face, const unsigned int index)
+{
+	return ( (size_t(face.m_v0) == index) || (size_t(face.m_v1) == index) || (size_t(face.m_v2) == index) );
+}
+
+bool NvStripifier::IsMoneyFace(const NvFaceInfo& face)
+{
+	if(FaceContainsIndex(face, 800) &&
+	   FaceContainsIndex(face, 812) &&
+	   FaceContainsIndex(face, 731))
+	   return true;
+
+	return false;
+}
+				
+////////////////////////////////////////////////////////////////////////////////////////
+// CreateStrips()
+//
+// Generates actual strips from the list-in-strip-order.
+//
+void NvStripifier::CreateStrips(const NvStripInfoVec& allStrips, IntVec& stripIndices, 
+								const bool bStitchStrips, unsigned int& numSeparateStrips, 
+								const bool bRestart, const unsigned int restartVal)
+{
+	assert(numSeparateStrips == 0);
+
+	NvFaceInfo tLastFace(0, 0, 0);
+	NvFaceInfo tPrevStripLastFace(0, 0, 0);
+	int nStripCount = allStrips.size();
+	assert(nStripCount > 0);
+
+	//we infer the cw/ccw ordering depending on the number of indices
+	//this is screwed up by the fact that we insert -1s to denote changing strips
+	//this is to account for that
+	int accountForNegatives = 0;
+
+	for (int i = 0; i < nStripCount; i++)
+	{
+		NvStripInfo *strip = allStrips[i];
+		int nStripFaceCount = strip->m_faces.size();
+		assert(nStripFaceCount > 0);
+
+		// Handle the first face in the strip
+		{
+			NvFaceInfo tFirstFace(strip->m_faces[0]->m_v0, strip->m_faces[0]->m_v1, strip->m_faces[0]->m_v2);
+
+			// If there is a second face, reorder vertices such that the
+			// unique vertex is first
+			if (nStripFaceCount > 1)
+			{
+				int nUnique = NvStripifier::GetUniqueVertexInB(strip->m_faces[1], &tFirstFace);
+				if (nUnique == tFirstFace.m_v1)
+				{
+					SWAP(tFirstFace.m_v0, tFirstFace.m_v1);
+				}
+				else if (nUnique == tFirstFace.m_v2)
+				{
+					SWAP(tFirstFace.m_v0, tFirstFace.m_v2);
+				}
+
+				// If there is a third face, reorder vertices such that the
+				// shared vertex is last
+				if (nStripFaceCount > 2)
+				{
+					if(IsDegenerate(strip->m_faces[1]))
+					{
+						int pivot = strip->m_faces[1]->m_v1;
+						if(tFirstFace.m_v1 == pivot)
+						{
+							SWAP(tFirstFace.m_v1, tFirstFace.m_v2);
+						}
+					}
+					else
+					{
+						int nShared0, nShared1;
+						GetSharedVertices(strip->m_faces[2], &tFirstFace, &nShared0, &nShared1);
+						if ( (nShared0 == tFirstFace.m_v1) && (nShared1 == -1) )
+						{
+							SWAP(tFirstFace.m_v1, tFirstFace.m_v2);
+						}
+					}
+				}
+			}
+
+			if( (i == 0) || !bStitchStrips || bRestart)
+			{
+				if(!IsCW(strip->m_faces[0], tFirstFace.m_v0, tFirstFace.m_v1))
+					stripIndices.push_back(tFirstFace.m_v0);
+			}
+			else
+			{
+				// Double tap the first in the new strip
+				stripIndices.push_back(tFirstFace.m_v0);
+	
+				// Check CW/CCW ordering
+				if (NextIsCW(stripIndices.size() - accountForNegatives) != IsCW(strip->m_faces[0], tFirstFace.m_v0, tFirstFace.m_v1))
+				{
+					stripIndices.push_back(tFirstFace.m_v0);
+				}
+			}
+
+			stripIndices.push_back(tFirstFace.m_v0);
+			stripIndices.push_back(tFirstFace.m_v1);
+			stripIndices.push_back(tFirstFace.m_v2);
+
+			// Update last face info
+			tLastFace = tFirstFace;
+		}
+
+		for (int j = 1; j < nStripFaceCount; j++)
+		{
+			int nUnique = GetUniqueVertexInB(&tLastFace, strip->m_faces[j]);
+			if (nUnique != -1)
+			{
+				stripIndices.push_back(nUnique);
+
+				// Update last face info
+				tLastFace.m_v0 = tLastFace.m_v1;
+				tLastFace.m_v1 = tLastFace.m_v2;
+				tLastFace.m_v2 = nUnique;
+			}
+			else
+			{
+				//we've hit a degenerate
+				stripIndices.push_back(strip->m_faces[j]->m_v2);
+				tLastFace.m_v0 = strip->m_faces[j]->m_v0;//tLastFace.m_v1;
+				tLastFace.m_v1 = strip->m_faces[j]->m_v1;//tLastFace.m_v2;
+				tLastFace.m_v2 = strip->m_faces[j]->m_v2;//tLastFace.m_v1;
+
+			}
+		}
+
+		// Double tap between strips.
+		if (bStitchStrips && !bRestart) 
+		{
+			if (i != nStripCount - 1)
+				stripIndices.push_back(tLastFace.m_v2);
+		} 
+		else if (bRestart) 
+		{
+			stripIndices.push_back(restartVal);
+		} 
+		else 
+		{
+			//-1 index indicates next strip
+			stripIndices.push_back(-1);
+			accountForNegatives++;
+			numSeparateStrips++;
+		}
+
+		// Update last face info
+		tLastFace.m_v0 = tLastFace.m_v1;
+		tLastFace.m_v1 = tLastFace.m_v2;
+		tLastFace.m_v2 = tLastFace.m_v2;
+	}
+	
+	if(bStitchStrips || bRestart)
+		numSeparateStrips = 1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// Stripify()
+//
+//
+// in_indices are the input indices of the mesh to stripify
+// in_cacheSize is the target cache size 
+//
+void NvStripifier::Stripify(const WordVec &in_indices, const int in_cacheSize, 
+							const int in_minStripLength, const unsigned short maxIndex, 
+							NvStripInfoVec &outStrips, NvFaceInfoVec& outFaceList)
+{
+	meshJump = 0.0f;
+	bFirstTimeResetPoint = true; //used in FindGoodResetPoint()
+
+	//the number of times to run the experiments
+	int numSamples = 10;
+	
+	//the cache size, clamped to one
+	if ( in_cacheSize - CACHE_INEFFICIENCY < 1 ) cacheSize = 1;
+	else cacheSize = in_cacheSize - CACHE_INEFFICIENCY;
+	
+	minStripLength = in_minStripLength;  //this is the strip size threshold below which we dump the strip into a list
+	
+	indices = in_indices;
+	
+	// build the stripification info
+	NvFaceInfoVec allFaceInfos;
+	NvEdgeInfoVec allEdgeInfos;
+	
+	BuildStripifyInfo(allFaceInfos, allEdgeInfos, maxIndex);
+	
+	NvStripInfoVec allStrips;
+
+	// stripify
+	FindAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples);
+
+	//split up the strips into cache friendly pieces, optimize them, then dump these into outStrips
+	SplitUpStripsAndOptimize(allStrips, outStrips, allEdgeInfos, outFaceList);
+
+	//clean up
+	for(size_t i = 0; i < allStrips.size(); i++)
+	{
+		delete allStrips[i];
+	}
+	
+	for (size_t i = 0; i < allEdgeInfos.size(); i++)
+	{
+		NvEdgeInfo *info = allEdgeInfos[i];
+		while (info != NULL)
+		{
+			NvEdgeInfo *next = (size_t(info->m_v0) == i ? info->m_nextV0 : info->m_nextV1);
+			info->Unref();
+			info = next;
+		}
+	}
+	
+}
+
+
+bool NvStripifier::IsDegenerate(const NvFaceInfo* face)
+{
+	if(face->m_v0 == face->m_v1)
+		return true;
+	else if(face->m_v0 == face->m_v2)
+		return true;
+	else if(face->m_v1 == face->m_v2)
+		return true;
+	else
+		return false;
+}
+
+bool NvStripifier::IsDegenerate(const unsigned short v0, const unsigned short v1, const unsigned short v2)
+{
+	if(v0 == v1)
+		return true;
+	else if(v0 == v2)
+		return true;
+	else if(v1 == v2)
+		return true;
+	else
+		return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// SplitUpStripsAndOptimize()
+//
+// Splits the input vector of strips (allBigStrips) into smaller, cache friendly pieces, then
+//  reorders these pieces to maximize cache hits
+// The final strips are output through outStrips
+//
+void NvStripifier::SplitUpStripsAndOptimize(NvStripInfoVec &allStrips, NvStripInfoVec &outStrips,
+                                            NvEdgeInfoVec& edgeInfos, NvFaceInfoVec& outFaceList)
+{
+	int threshold = cacheSize;
+	NvStripInfoVec tempStrips;
+	
+	//split up strips into threshold-sized pieces
+	for(size_t i = 0; i < allStrips.size(); i++)
+	{
+		NvStripInfo* currentStrip;
+		NvStripStartInfo startInfo(NULL, NULL, false);
+	
+		int actualStripSize = 0;
+		for(size_t j = 0; j < allStrips[i]->m_faces.size(); ++j)
+		{
+			if( !IsDegenerate(allStrips[i]->m_faces[j]) )
+				actualStripSize++;
+		}
+		
+		if(actualStripSize /*allStrips[i]->m_faces.size()*/ > threshold)
+		{
+			
+			int numTimes    = actualStripSize /*allStrips[i]->m_faces.size()*/ / threshold;
+			int numLeftover = actualStripSize /*allStrips[i]->m_faces.size()*/ % threshold;
+
+			int degenerateCount = 0;
+			int j;
+			for(j = 0; j < numTimes; j++)
+			{
+				currentStrip = new NvStripInfo(startInfo, 0, -1);
+				
+				int faceCtr = j*threshold + degenerateCount;
+				bool bFirstTime = true;
+				while(faceCtr < threshold+(j*threshold)+degenerateCount)
+				{
+					if(IsDegenerate(allStrips[i]->m_faces[faceCtr]))
+					{
+						degenerateCount++;
+						
+						//last time or first time through, no need for a degenerate
+						if( (((faceCtr + 1) != threshold+(j*threshold)+degenerateCount) ||
+							 ((j == numTimes - 1) && (numLeftover < 4) && (numLeftover > 0))) && 
+							 !bFirstTime)
+						{
+							currentStrip->m_faces.push_back(allStrips[i]->m_faces[faceCtr++]);
+						}
+						else
+						{
+							//but, we do need to delete the degenerate, if it's marked fake, to avoid leaking
+							if(allStrips[i]->m_faces[faceCtr]->m_bIsFake)
+							{
+								delete allStrips[i]->m_faces[faceCtr], allStrips[i]->m_faces[faceCtr] = NULL;
+							}
+							++faceCtr;
+						}
+					}
+					else
+					{
+						currentStrip->m_faces.push_back(allStrips[i]->m_faces[faceCtr++]);
+						bFirstTime = false;
+					}
+				}
+				/*
+				for(int faceCtr = j*threshold; faceCtr < threshold+(j*threshold); faceCtr++)
+				{
+					currentStrip->m_faces.push_back(allStrips[i]->m_faces[faceCtr]);
+				}
+				*/
+				if(j == numTimes - 1) //last time through
+				{
+					if( (numLeftover < 4) && (numLeftover > 0) ) //way too small
+					{
+						//just add to last strip
+						int ctr = 0;
+						while(ctr < numLeftover)
+						{
+							IsDegenerate( allStrips[i]->m_faces[faceCtr] ) ? ++degenerateCount : ++ctr;
+							currentStrip->m_faces.push_back(allStrips[i]->m_faces[faceCtr++]);
+						}
+						numLeftover = 0;
+					}
+				}
+				tempStrips.push_back(currentStrip);
+			}
+			
+			int leftOff = j * threshold + degenerateCount;
+			
+			if(numLeftover != 0)
+			{
+				currentStrip = new NvStripInfo(startInfo, 0, -1);   
+				
+				int ctr = 0;
+				bool bFirstTime = true;
+				while(ctr < numLeftover)
+				{
+					if( !IsDegenerate(allStrips[i]->m_faces[leftOff]) )
+					{
+						ctr++;
+						bFirstTime = false;
+						currentStrip->m_faces.push_back(allStrips[i]->m_faces[leftOff++]);
+					}
+					else if(!bFirstTime)
+						currentStrip->m_faces.push_back(allStrips[i]->m_faces[leftOff++]);
+					else
+					{
+						//don't leak
+						if(allStrips[i]->m_faces[leftOff]->m_bIsFake)
+						{
+							delete allStrips[i]->m_faces[leftOff], allStrips[i]->m_faces[leftOff] = NULL;
+						}
+
+						leftOff++;
+					}
+				}
+				/*
+				for(int k = 0; k < numLeftover; k++)
+				{
+					currentStrip->m_faces.push_back(allStrips[i]->m_faces[leftOff++]);
+				}
+				*/
+				
+				tempStrips.push_back(currentStrip);
+			}
+		}
+		else
+		{
+			//we're not just doing a tempStrips.push_back(allBigStrips[i]) because
+			// this way we can delete allBigStrips later to free the memory
+			currentStrip = new NvStripInfo(startInfo, 0, -1);
+			
+			for(size_t j = 0; j < allStrips[i]->m_faces.size(); j++)
+				currentStrip->m_faces.push_back(allStrips[i]->m_faces[j]);
+			
+			tempStrips.push_back(currentStrip);
+		}
+	}
+
+	//add small strips to face list
+	NvStripInfoVec tempStrips2;
+	RemoveSmallStrips(tempStrips, tempStrips2, outFaceList);
+	
+	outStrips.clear();
+	//screw optimization for now
+//	for(i = 0; i < tempStrips.size(); ++i)
+//    outStrips.push_back(tempStrips[i]);
+	
+	if(tempStrips2.size() != 0)
+	{
+		//Optimize for the vertex cache
+		VertexCache* vcache = new VertexCache(cacheSize);
+		
+		float bestNumHits = -1.0f;
+		float numHits;
+		int bestIndex = -1;
+		//bool done = false;
+		
+		int firstIndex = 0;
+		float minCost = 10000.0f;
+		
+		for(size_t i = 0; i < tempStrips2.size(); i++)
+		{
+			int numNeighbors = 0;
+			
+			//find strip with least number of neighbors per face
+			for(size_t j = 0; j < tempStrips2[i]->m_faces.size(); j++)
+			{
+				numNeighbors += NumNeighbors(tempStrips2[i]->m_faces[j], edgeInfos);
+			}
+			
+			float currCost = (float)numNeighbors / (float)tempStrips2[i]->m_faces.size();
+			if(currCost < minCost)
+			{
+				minCost = currCost;
+				firstIndex = i;
+			}
+		}
+		
+		UpdateCacheStrip(vcache, tempStrips2[firstIndex]);
+		outStrips.push_back(tempStrips2[firstIndex]);
+		
+		tempStrips2[firstIndex]->visited = true;
+		
+		bool bWantsCW = (tempStrips2[firstIndex]->m_faces.size() % 2) == 0;
+
+		//this n^2 algo is what slows down stripification so much....
+		// needs to be improved
+		while(1)
+		{
+			bestNumHits = -1.0f;
+			
+			//find best strip to add next, given the current cache
+			for(size_t i = 0; i < tempStrips2.size(); i++)
+			{
+				if(tempStrips2[i]->visited)
+					continue;
+
+				numHits = CalcNumHitsStrip(vcache, tempStrips2[i]);
+				if(numHits > bestNumHits)
+				{
+					bestNumHits = numHits;
+					bestIndex = i;
+				}
+				else if(numHits >= bestNumHits)
+				{
+					//check previous strip to see if this one requires it to switch polarity
+					NvStripInfo *strip = tempStrips2[i];
+					int nStripFaceCount = strip->m_faces.size();
+					
+					NvFaceInfo tFirstFace(strip->m_faces[0]->m_v0, strip->m_faces[0]->m_v1, strip->m_faces[0]->m_v2);
+					
+					// If there is a second face, reorder vertices such that the
+					// unique vertex is first
+					if (nStripFaceCount > 1)
+					{
+						int nUnique = NvStripifier::GetUniqueVertexInB(strip->m_faces[1], &tFirstFace);
+						if (nUnique == tFirstFace.m_v1)
+						{
+							SWAP(tFirstFace.m_v0, tFirstFace.m_v1);
+						}
+						else if (nUnique == tFirstFace.m_v2)
+						{
+							SWAP(tFirstFace.m_v0, tFirstFace.m_v2);
+						}
+						
+						// If there is a third face, reorder vertices such that the
+						// shared vertex is last
+						if (nStripFaceCount > 2)
+						{
+							int nShared0, nShared1;
+							GetSharedVertices(strip->m_faces[2], &tFirstFace, &nShared0, &nShared1);
+							if ( (nShared0 == tFirstFace.m_v1) && (nShared1 == -1) )
+							{
+								SWAP(tFirstFace.m_v1, tFirstFace.m_v2);
+							}
+						}
+					}
+						
+					// Check CW/CCW ordering
+					if (bWantsCW == IsCW(strip->m_faces[0], tFirstFace.m_v0, tFirstFace.m_v1))
+					{
+						//I like this one!
+						bestIndex = i;
+					}
+				}
+			}
+			
+			if(bestNumHits == -1.0f)
+				break;
+			tempStrips2[bestIndex]->visited = true;
+			UpdateCacheStrip(vcache, tempStrips2[bestIndex]);
+			outStrips.push_back(tempStrips2[bestIndex]);
+			bWantsCW = (tempStrips2[bestIndex]->m_faces.size() % 2 == 0) ? bWantsCW : !bWantsCW;
+		}
+		
+		delete vcache;	
+	}
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// UpdateCacheStrip()
+//
+// Updates the input vertex cache with this strip's vertices
+//
+void NvStripifier::UpdateCacheStrip(VertexCache* vcache, NvStripInfo* strip)
+{
+	for(size_t i = 0; i < strip->m_faces.size(); ++i)
+	{
+		if(!vcache->InCache(strip->m_faces[i]->m_v0))
+			vcache->AddEntry(strip->m_faces[i]->m_v0);
+		
+		if(!vcache->InCache(strip->m_faces[i]->m_v1))
+			vcache->AddEntry(strip->m_faces[i]->m_v1);
+		
+		if(!vcache->InCache(strip->m_faces[i]->m_v2))
+			vcache->AddEntry(strip->m_faces[i]->m_v2);
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// UpdateCacheFace()
+//
+// Updates the input vertex cache with this face's vertices
+//
+void NvStripifier::UpdateCacheFace(VertexCache* vcache, NvFaceInfo* face)
+{
+	if(!vcache->InCache(face->m_v0))
+		vcache->AddEntry(face->m_v0);
+		
+	if(!vcache->InCache(face->m_v1))
+		vcache->AddEntry(face->m_v1);
+		
+	if(!vcache->InCache(face->m_v2))
+		vcache->AddEntry(face->m_v2);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// CalcNumHitsStrip()
+//
+// returns the number of cache hits per face in the strip
+//
+float NvStripifier::CalcNumHitsStrip(VertexCache* vcache, NvStripInfo* strip)
+{
+	int numHits = 0;
+	int numFaces = 0;
+	
+	for(size_t i = 0; i < strip->m_faces.size(); i++)
+	{
+		if(vcache->InCache(strip->m_faces[i]->m_v0))
+			++numHits;
+		
+		if(vcache->InCache(strip->m_faces[i]->m_v1))
+			++numHits;
+		
+		if(vcache->InCache(strip->m_faces[i]->m_v2))
+			++numHits;
+		
+		numFaces++;
+	}
+	
+	return ((float)numHits / (float)numFaces);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// CalcNumHitsFace()
+//
+// returns the number of cache hits in the face
+//
+int NvStripifier::CalcNumHitsFace(VertexCache* vcache, NvFaceInfo* face)
+{
+	int numHits = 0;
+
+	if(vcache->InCache(face->m_v0))
+		numHits++;
+		
+	if(vcache->InCache(face->m_v1))
+		numHits++;
+		
+	if(vcache->InCache(face->m_v2))
+		numHits++;
+		
+	return numHits;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// NumNeighbors()
+//
+// Returns the number of neighbors that this face has
+//
+int NvStripifier::NumNeighbors(NvFaceInfo* face, NvEdgeInfoVec& edgeInfoVec)
+{
+	int numNeighbors = 0;
+	
+	if(FindOtherFace(edgeInfoVec, face->m_v0, face->m_v1, face) != NULL)
+	{
+		numNeighbors++;
+	}
+	
+	if(FindOtherFace(edgeInfoVec, face->m_v1, face->m_v2, face) != NULL)
+	{
+		numNeighbors++;
+	}
+	
+	if(FindOtherFace(edgeInfoVec, face->m_v2, face->m_v0, face) != NULL)
+	{
+		numNeighbors++;
+	}
+	
+	return numNeighbors;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// AvgStripSize()
+//
+// Finds the average strip size of the input vector of strips
+//
+float NvStripifier::AvgStripSize(const NvStripInfoVec &strips){
+	int sizeAccum = 0;
+	int numStrips = strips.size();
+	for (int i = 0; i < numStrips; i++){
+		NvStripInfo *strip = strips[i];
+		sizeAccum += strip->m_faces.size();
+		sizeAccum -= strip->m_numDegenerates;
+	}
+	return ((float)sizeAccum) / ((float)numStrips);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// FindAllStrips()
+//
+// Does the stripification, puts output strips into vector allStrips
+//
+// Works by setting runnning a number of experiments in different areas of the mesh, and
+//  accepting the one which results in the longest strips.  It then accepts this, and moves
+//  on to a different area of the mesh.  We try to jump around the mesh some, to ensure that
+//  large open spans of strips get generated.
+//
+void NvStripifier::FindAllStrips(NvStripInfoVec &allStrips,
+								 NvFaceInfoVec &allFaceInfos,
+								 NvEdgeInfoVec &allEdgeInfos,
+								 int numSamples){
+	// the experiments
+	int experimentId = 0;
+	int stripId      = 0;
+	bool done        = false;
+
+	int loopCtr = 0;
+	
+	while (!done)
+	{
+		loopCtr++;
+		
+		//
+		// PHASE 1: Set up numSamples * numEdges experiments
+		//
+		NvStripInfoVec *experiments = new NvStripInfoVec [numSamples * 6];
+		int experimentIndex = 0;
+		std::set   <NvFaceInfo*>  resetPoints;
+		for (int i = 0; i < numSamples; i++)
+		{
+			
+			// Try to find another good reset point.
+			// If there are none to be found, we are done
+			NvFaceInfo *nextFace = FindGoodResetPoint(allFaceInfos, allEdgeInfos);
+			if (nextFace == NULL){
+				done = true;
+				break;
+			}
+			// If we have already evaluated starting at this face in this slew
+			// of experiments, then skip going any further
+			else if (resetPoints.find(nextFace) != resetPoints.end()){
+				continue;
+			}
+			
+			// trying it now...
+			resetPoints.insert(nextFace);
+			
+			// otherwise, we shall now try experiments for starting on the 01,12, and 20 edges
+			assert(nextFace->m_stripId < 0);
+			
+			// build the strip off of this face's 0-1 edge
+			NvEdgeInfo *edge01 = FindEdgeInfo(allEdgeInfos, nextFace->m_v0, nextFace->m_v1);
+			NvStripInfo *strip01 = new NvStripInfo(NvStripStartInfo(nextFace, edge01, true), stripId++, experimentId++);
+			experiments[experimentIndex++].push_back(strip01);
+			
+			// build the strip off of this face's 1-0 edge
+			NvEdgeInfo *edge10 = FindEdgeInfo(allEdgeInfos, nextFace->m_v0, nextFace->m_v1);
+			NvStripInfo *strip10 = new NvStripInfo(NvStripStartInfo(nextFace, edge10, false), stripId++, experimentId++);
+			experiments[experimentIndex++].push_back(strip10);
+			
+			// build the strip off of this face's 1-2 edge
+			NvEdgeInfo *edge12 = FindEdgeInfo(allEdgeInfos, nextFace->m_v1, nextFace->m_v2);
+			NvStripInfo *strip12 = new NvStripInfo(NvStripStartInfo(nextFace, edge12, true), stripId++, experimentId++);
+			experiments[experimentIndex++].push_back(strip12);
+			
+			// build the strip off of this face's 2-1 edge
+			NvEdgeInfo *edge21 = FindEdgeInfo(allEdgeInfos, nextFace->m_v1, nextFace->m_v2);
+			NvStripInfo *strip21 = new NvStripInfo(NvStripStartInfo(nextFace, edge21, false), stripId++, experimentId++);
+			experiments[experimentIndex++].push_back(strip21);
+			
+			// build the strip off of this face's 2-0 edge
+			NvEdgeInfo *edge20 = FindEdgeInfo(allEdgeInfos, nextFace->m_v2, nextFace->m_v0);
+			NvStripInfo *strip20 = new NvStripInfo(NvStripStartInfo(nextFace, edge20, true), stripId++, experimentId++);
+			experiments[experimentIndex++].push_back(strip20);
+			
+			// build the strip off of this face's 0-2 edge
+			NvEdgeInfo *edge02 = FindEdgeInfo(allEdgeInfos, nextFace->m_v2, nextFace->m_v0);
+			NvStripInfo *strip02 = new NvStripInfo(NvStripStartInfo(nextFace, edge02, false), stripId++, experimentId++);
+			experiments[experimentIndex++].push_back(strip02);
+		}
+		
+		//
+		// PHASE 2: Iterate through that we setup in the last phase
+		// and really build each of the strips and strips that follow to see how
+		// far we get
+		//
+		int numExperiments = experimentIndex;
+		for (int i = 0; i < numExperiments; i++){
+			
+			// get the strip set
+			
+			// build the first strip of the list
+			experiments[i][0]->Build(allEdgeInfos, allFaceInfos);
+			int experimentId = experiments[i][0]->m_experimentId;
+			
+			NvStripInfo *stripIter = experiments[i][0];
+			NvStripStartInfo startInfo(NULL, NULL, false);
+			while (FindTraversal(allFaceInfos, allEdgeInfos, stripIter, startInfo)){
+				
+				// create the new strip info
+				stripIter = new NvStripInfo(startInfo, stripId++, experimentId);
+				
+				// build the next strip
+				stripIter->Build(allEdgeInfos, allFaceInfos);
+				
+				// add it to the list
+				experiments[i].push_back(stripIter);
+			}
+		}
+		
+		//
+		// Phase 3: Find the experiment that has the most promise
+		//
+		int bestIndex = 0;
+		double bestValue = 0;
+		for (int i = 0; i < numExperiments; i++)
+		{
+			const float avgStripSizeWeight = 1.0f;
+			//const float numTrisWeight      = 0.0f;
+			const float numStripsWeight    = 0.0f;
+			float avgStripSize = AvgStripSize(experiments[i]);
+			float numStrips    = (float) experiments[i].size();
+			float value        = avgStripSize * avgStripSizeWeight + (numStrips * numStripsWeight);
+			//float value = 1.f / numStrips;
+			//float value = numStrips * avgStripSize;
+				
+			if (value > bestValue)
+			{
+				bestValue = value;
+				bestIndex = i;
+			}
+		}
+
+		//
+		// Phase 4: commit the best experiment of the bunch
+		//
+		CommitStrips(allStrips, experiments[bestIndex]);
+		
+		// and destroy all of the others
+		for (int i = 0; i < numExperiments; i++)
+		{
+			if (i != bestIndex)
+			{
+				int numStrips = experiments[i].size();
+				for (int j = 0; j < numStrips; j++)
+				{
+					NvStripInfo* currStrip = experiments[i][j];
+					//delete all bogus faces in the experiments
+					for (size_t k = 0; k < currStrip->m_faces.size(); ++k)
+					{
+						if(currStrip->m_faces[k]->m_bIsFake)
+						{
+							delete currStrip->m_faces[k], currStrip->m_faces[k] = NULL;
+						}
+					}
+					delete currStrip, currStrip = NULL, experiments[i][j] = NULL;
+				}
+			}
+		}
+		
+		// delete the array that we used for all experiments
+		delete [] experiments;
+  }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// CountRemainingTris()
+//
+// This will count the number of triangles left in the
+// strip list starting at iter and finishing up at end
+//
+int NvStripifier::CountRemainingTris(std::list<NvStripInfo*>::iterator iter,
+											std::list<NvStripInfo*>::iterator  end){
+	int count = 0;
+	while (iter != end){
+		count += (*iter)->m_faces.size();
+		iter++;
+	}
+	return count;
+}
+
diff --git a/NvTriStrip/NvTriStripObjects.h b/NvTriStrip/NvTriStripObjects.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4cc1e6a3e2ef860f33207c574eda0892651cedd
--- /dev/null
+++ b/NvTriStrip/NvTriStripObjects.h
@@ -0,0 +1,243 @@
+
+#ifndef NV_TRISTRIP_OBJECTS_H
+#define NV_TRISTRIP_OBJECTS_H
+
+#include <assert.h>
+#include <vector>
+#include <list>
+#include "VertexCache.h"
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// Types defined for stripification
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+struct MyVertex {
+	float x, y, z;
+	float nx, ny, nz;
+};
+
+typedef MyVertex MyVector;
+
+struct MyFace {
+	int v1, v2, v3;
+	float nx, ny, nz;
+};
+
+
+class NvFaceInfo {
+public:
+	
+	// vertex indices
+	NvFaceInfo(int v0, int v1, int v2, bool bIsFake = false){
+		m_v0 = v0; m_v1 = v1; m_v2 = v2;
+		m_stripId      = -1;
+		m_testStripId  = -1;
+		m_experimentId = -1;
+		m_bIsFake = bIsFake;
+	}
+	
+	// data members are left public
+	int   m_v0, m_v1, m_v2;
+	int   m_stripId;      // real strip Id
+	int   m_testStripId;  // strip Id in an experiment
+	int   m_experimentId; // in what experiment was it given an experiment Id?
+	bool  m_bIsFake;      //if true, will be deleted when the strip it's in is deleted
+};
+
+// nice and dumb edge class that points knows its
+// indices, the two faces, and the next edge using
+// the lesser of the indices
+class NvEdgeInfo {
+public:
+	
+	// constructor puts 1 ref on us
+	NvEdgeInfo (int v0, int v1){
+		m_v0       = v0;
+		m_v1       = v1;
+		m_face0    = NULL;
+		m_face1    = NULL;
+		m_nextV0   = NULL;
+		m_nextV1   = NULL;
+		
+		// we will appear in 2 lists.  this is a good
+		// way to make sure we delete it the second time
+		// we hit it in the edge infos
+		m_refCount = 2;    
+		
+	}
+	
+	// ref and unref
+	void Unref () { if (--m_refCount == 0) delete this; }
+	
+	// data members are left public
+	unsigned int m_refCount;
+	NvFaceInfo  *m_face0, *m_face1;
+	int          m_v0, m_v1;
+	NvEdgeInfo  *m_nextV0, *m_nextV1;
+};
+
+
+// This class is a quick summary of parameters used
+// to begin a triangle strip.  Some operations may
+// want to create lists of such items, so they were
+// pulled out into a class
+class NvStripStartInfo {
+public:
+	NvStripStartInfo(NvFaceInfo *startFace, NvEdgeInfo *startEdge, bool toV1){
+		m_startFace    = startFace;
+		m_startEdge    = startEdge;
+		m_toV1         = toV1;
+	}
+	NvFaceInfo    *m_startFace;
+	NvEdgeInfo    *m_startEdge;
+	bool           m_toV1;      
+};
+
+
+typedef std::vector<NvFaceInfo*>     NvFaceInfoVec;
+typedef std::list  <NvFaceInfo*>     NvFaceInfoList;
+typedef std::list  <NvFaceInfoVec*>  NvStripList;
+typedef std::vector<NvEdgeInfo*>     NvEdgeInfoVec;
+
+typedef std::vector<short> WordVec;
+typedef std::vector<int> IntVec;
+typedef std::vector<MyVertex> MyVertexVec;
+typedef std::vector<MyFace> MyFaceVec;
+
+template<class T> 
+inline void SWAP(T& first, T& second) 
+{
+	T temp = first;
+	first = second;
+	second = temp;
+}
+
+// This is a summary of a strip that has been built
+class NvStripInfo {
+public:
+	
+	// A little information about the creation of the triangle strips
+	NvStripInfo(const NvStripStartInfo &startInfo, int stripId, int experimentId = -1) :
+	  m_startInfo(startInfo)
+	{
+		m_stripId      = stripId;
+		m_experimentId = experimentId;
+		visited = false;
+		m_numDegenerates = 0;
+	}
+
+	// This is an experiment if the experiment id is >= 0
+	inline bool IsExperiment () const { return m_experimentId >= 0; }
+	  
+	inline bool IsInStrip (const NvFaceInfo *faceInfo) const 
+	{
+		if(faceInfo == NULL)
+			return false;
+		  
+		return (m_experimentId >= 0 ? faceInfo->m_testStripId == m_stripId : faceInfo->m_stripId == m_stripId);
+	}
+	  
+	bool SharesEdge(const NvFaceInfo* faceInfo, NvEdgeInfoVec &edgeInfos);
+	  
+	// take the given forward and backward strips and combine them together
+	void Combine(const NvFaceInfoVec &forward, const NvFaceInfoVec &backward);
+	  
+	//returns true if the face is "unique", i.e. has a vertex which doesn't exist in the faceVec
+	bool Unique(NvFaceInfoVec& faceVec, NvFaceInfo* face);
+	  
+	// mark the triangle as taken by this strip
+	bool IsMarked    (NvFaceInfo *faceInfo);
+	void MarkTriangle(NvFaceInfo *faceInfo);
+	  
+	// build the strip
+	void Build(NvEdgeInfoVec &edgeInfos, NvFaceInfoVec &faceInfos);
+	  
+	// public data members
+	NvStripStartInfo m_startInfo;
+	NvFaceInfoVec    m_faces;
+	int              m_stripId;
+	int              m_experimentId;
+	  
+	bool visited;
+
+	int m_numDegenerates;
+};
+
+typedef std::vector<NvStripInfo*>    NvStripInfoVec;
+
+
+//The actual stripifier
+class NvStripifier {
+public:
+	
+	// Constructor
+	NvStripifier();
+	~NvStripifier();
+	
+	//the target vertex cache size, the structure to place the strips in, and the input indices
+	void Stripify(const WordVec &in_indices, const int in_cacheSize, const int in_minStripLength, 
+				  const unsigned short maxIndex, NvStripInfoVec &allStrips, NvFaceInfoVec &allFaces);
+	void CreateStrips(const NvStripInfoVec& allStrips, IntVec& stripIndices, const bool bStitchStrips, unsigned int& numSeparateStrips, const bool bRestart, const unsigned int restartVal);
+	
+	static int GetUniqueVertexInB(NvFaceInfo *faceA, NvFaceInfo *faceB);
+	//static int GetSharedVertex(NvFaceInfo *faceA, NvFaceInfo *faceB);
+	static void GetSharedVertices(NvFaceInfo *faceA, NvFaceInfo *faceB, int* vertex0, int* vertex1);
+
+	static bool IsDegenerate(const NvFaceInfo* face);
+	static bool IsDegenerate(const unsigned short v0, const unsigned short v1, const unsigned short v2);
+	
+protected:
+	
+	WordVec indices;
+	int cacheSize;
+	int minStripLength;
+	float meshJump;
+	bool bFirstTimeResetPoint;
+	
+	/////////////////////////////////////////////////////////////////////////////////
+	//
+	// Big mess of functions called during stripification
+	//
+	/////////////////////////////////////////////////////////////////////////////////
+
+	//********************
+	bool IsMoneyFace(const NvFaceInfo& face);
+	bool FaceContainsIndex(const NvFaceInfo& face, const unsigned int index);
+
+	bool IsCW(NvFaceInfo *faceInfo, int v0, int v1);
+	bool NextIsCW(const int numIndices);
+	
+	static int  GetNextIndex(const WordVec &indices, NvFaceInfo *face);
+	static NvEdgeInfo *FindEdgeInfo(NvEdgeInfoVec &edgeInfos, int v0, int v1);
+	static NvFaceInfo *FindOtherFace(NvEdgeInfoVec &edgeInfos, int v0, int v1, NvFaceInfo *faceInfo);
+	NvFaceInfo *FindGoodResetPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos);
+	
+	void FindAllStrips(NvStripInfoVec &allStrips, NvFaceInfoVec &allFaceInfos, NvEdgeInfoVec &allEdgeInfos, int numSamples);
+	void SplitUpStripsAndOptimize(NvStripInfoVec &allStrips, NvStripInfoVec &outStrips, NvEdgeInfoVec& edgeInfos, NvFaceInfoVec& outFaceList);
+	void RemoveSmallStrips(NvStripInfoVec& allStrips, NvStripInfoVec& allBigStrips, NvFaceInfoVec& faceList);
+	
+	bool FindTraversal(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos, NvStripInfo *strip, NvStripStartInfo &startInfo);
+	int  CountRemainingTris(std::list<NvStripInfo*>::iterator iter, std::list<NvStripInfo*>::iterator  end);
+	
+	void CommitStrips(NvStripInfoVec &allStrips, const NvStripInfoVec &strips);
+	
+	float AvgStripSize(const NvStripInfoVec &strips);
+	int FindStartPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos);
+	
+	void UpdateCacheStrip(VertexCache* vcache, NvStripInfo* strip);
+	void UpdateCacheFace(VertexCache* vcache, NvFaceInfo* face);
+	float CalcNumHitsStrip(VertexCache* vcache, NvStripInfo* strip);
+	int CalcNumHitsFace(VertexCache* vcache, NvFaceInfo* face);
+	int NumNeighbors(NvFaceInfo* face, NvEdgeInfoVec& edgeInfoVec);
+	
+	void BuildStripifyInfo(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos, const unsigned short maxIndex);
+	bool AlreadyExists(NvFaceInfo* faceInfo, NvFaceInfoVec& faceInfos);
+	
+	// let our strip info classes and the other classes get
+	// to these protected stripificaton methods if they want
+	friend class NvStripInfo;
+};
+
+#endif
diff --git a/NvTriStrip/VertexCache.cpp b/NvTriStrip/VertexCache.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a65f87fe9d5008b67bd26c6e4e6a2fb1f116d4b7
--- /dev/null
+++ b/NvTriStrip/VertexCache.cpp
@@ -0,0 +1,88 @@
+
+
+#include "VertexCache.h"
+
+VertexCache::VertexCache()
+{
+  VertexCache(16);
+}
+
+
+VertexCache::VertexCache(int size)
+{
+  numEntries = size;
+
+  entries = new int[numEntries];
+
+  for(int i = 0; i < numEntries; i++)
+    entries[i] = -1;
+}
+
+
+VertexCache::~VertexCache()
+{
+  delete[] entries;
+}
+
+
+int VertexCache::At(int index)
+{
+  return entries[index];
+}
+
+
+void VertexCache::Set(int index, int value)
+{
+  entries[index] = value;
+}
+
+
+void VertexCache::Clear()
+{
+  for(int i = 0; i < numEntries; i++)
+    entries[i] = -1;
+}
+
+void VertexCache::Copy(VertexCache* inVcache)
+{
+  for(int i = 0; i < numEntries; i++)
+  {
+    inVcache->Set(i, entries[i]);
+  }
+}
+
+bool VertexCache::InCache(int entry)
+{
+  bool returnVal = false;
+
+  for(int i = 0; i < numEntries; i++)
+  {
+    if(entries[i] == entry)
+    {
+      returnVal = true;
+      break;
+    }
+  }
+
+  return returnVal;
+}
+
+
+int VertexCache::AddEntry(int entry)
+{
+  int removed;
+
+  removed = entries[numEntries - 1];
+
+  //push everything right one
+  for(int i = numEntries - 2; i >= 0; i--)
+  {
+    entries[i + 1] = entries[i];
+  }
+
+  entries[0] = entry;
+
+  return removed;
+}
+
+  
\ No newline at end of file
diff --git a/NvTriStrip/VertexCache.h b/NvTriStrip/VertexCache.h
new file mode 100644
index 0000000000000000000000000000000000000000..5a1ea4caff3f39f152108b4e8e0f6aae75754226
--- /dev/null
+++ b/NvTriStrip/VertexCache.h
@@ -0,0 +1,27 @@
+  
+#ifndef VERTEX_CACHE_H
+
+#define VERTEX_CACHE_H
+
+class VertexCache
+{
+	
+public:
+	VertexCache(int size);
+	VertexCache();
+	~VertexCache();	
+	bool InCache(int entry);
+	int AddEntry(int entry);
+	void Clear();
+	void Copy(VertexCache* inVcache) ;
+	int At(int index);
+	void Set(int index, int value);
+
+private:
+
+  int *entries;
+  int numEntries;
+
+};
+
+#endif
diff --git a/TriStripper/connectivity_graph.cpp b/TriStripper/connectivity_graph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81def731b26c805dcc39ca310032edd8b814e7ef
--- /dev/null
+++ b/TriStripper/connectivity_graph.cpp
@@ -0,0 +1,132 @@
+//
+// Copyright (C) 2004 Tanguy Fautré.
+// For conditions of distribution and use,
+// see copyright notice in tri_stripper.h
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: connectivity_graph.cpp 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+#include "detail/connectivity_graph.h"
+
+#include <algorithm>
+
+
+
+
+namespace triangle_stripper {
+
+	namespace detail {
+
+
+
+
+namespace
+{
+
+	class tri_edge : public triangle_edge
+	{
+	public:
+		tri_edge(index A, index B, size_t TriPos)
+			: triangle_edge(A, B), m_TriPos(TriPos) { }
+
+		size_t TriPos() const { return m_TriPos; }
+
+	private:
+		size_t	m_TriPos;
+	};
+
+
+	class cmp_tri_edge_lt
+	{
+	public:
+		bool operator() (const tri_edge & a, const tri_edge & b) const;
+	};
+
+
+	typedef std::vector<tri_edge> edge_map;
+
+
+	void LinkNeighbours(graph_array<triangle> & Triangles, const edge_map & EdgeMap, const tri_edge Edge);
+
+}
+
+
+
+
+void make_connectivity_graph(graph_array<triangle> & Triangles, const indices & Indices)
+{
+	assert(Triangles.size() == (Indices.size() / 3));
+
+	// Fill the triangle data
+	for (size_t i = 0; i < Triangles.size(); ++i)
+		Triangles[i] = triangle(Indices[i * 3 + 0], Indices[i * 3 + 1], Indices[i * 3 + 2]);
+
+	// Build an edge lookup table
+	edge_map EdgeMap;
+	EdgeMap.reserve(Triangles.size() * 3);
+
+	for (size_t i = 0; i < Triangles.size(); ++i) {
+
+		const triangle & Tri = * Triangles[i];
+
+		EdgeMap.push_back(tri_edge(Tri.A(), Tri.B(), i)); 
+		EdgeMap.push_back(tri_edge(Tri.B(), Tri.C(), i)); 
+		EdgeMap.push_back(tri_edge(Tri.C(), Tri.A(), i)); 
+	}
+
+	std::sort(EdgeMap.begin(), EdgeMap.end(), cmp_tri_edge_lt());
+
+	// Link neighbour triangles together using the lookup table
+	for (size_t i = 0; i < Triangles.size(); ++i) {
+
+		const triangle & Tri = * Triangles[i];
+
+		LinkNeighbours(Triangles, EdgeMap, tri_edge(Tri.B(), Tri.A(), i)); 
+		LinkNeighbours(Triangles, EdgeMap, tri_edge(Tri.C(), Tri.B(), i)); 
+		LinkNeighbours(Triangles, EdgeMap, tri_edge(Tri.A(), Tri.C(), i)); 
+	}
+}
+
+
+
+namespace
+{
+	
+	inline bool cmp_tri_edge_lt::operator() (const tri_edge & a, const tri_edge & b) const
+	{
+		const index A1 = a.A();
+		const index B1 = a.B();
+		const index A2 = b.A();
+		const index B2 = b.B();
+
+		if ((A1 < A2) || ((A1 == A2) && (B1 < B2)))
+			return true;
+		else
+			return false;
+	}
+
+
+	void LinkNeighbours(graph_array<triangle> & Triangles, const edge_map & EdgeMap, const tri_edge Edge)
+	{
+		// Find the first edge equal to Edge
+		edge_map::const_iterator it = std::lower_bound(EdgeMap.begin(), EdgeMap.end(), Edge, cmp_tri_edge_lt());
+
+		// See if there are any other edges that are equal
+		// (if so, it means that more than 2 triangles are sharing the same edge,
+		//  which is unlikely but not impossible)
+		for (; (it != EdgeMap.end()) && (Edge == (* it)); ++it)
+			Triangles.insert_arc(Edge.TriPos(), it->TriPos());
+
+		// Note: degenerated triangles will also point themselves as neighbour triangles
+	}
+
+}
+
+
+
+
+	} // namespace detail
+
+} // namespace detail
+
diff --git a/TriStripper/detail/cache_simulator.h b/TriStripper/detail/cache_simulator.h
new file mode 100644
index 0000000000000000000000000000000000000000..a05769a15ae585ab2f7cdabf9310e3d0c71220d1
--- /dev/null
+++ b/TriStripper/detail/cache_simulator.h
@@ -0,0 +1,154 @@
+//
+// Copyright (C) 2004 Tanguy Fautré.
+// For conditions of distribution and use,
+// see copyright notice in tri_stripper.h
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: cache_simulator.h 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+#ifndef TRI_STRIPPER_HEADER_GUARD_CACHE_SIMULATOR_H
+#define TRI_STRIPPER_HEADER_GUARD_CACHE_SIMULATOR_H
+
+#include <algorithm>
+#include <limits>
+#include <deque>
+
+
+
+
+namespace triangle_stripper {
+
+	namespace detail {
+
+
+
+
+class cache_simulator
+{
+public:
+	cache_simulator();
+
+	void clear();
+	void resize(size_t Size);
+	void reset();
+	void push_cache_hits(bool Enabled = true);
+	size_t size() const;
+
+	void push(index i, bool CountCacheHit = false);
+	void merge(const cache_simulator & Backward, size_t PossibleOverlap);
+
+	void reset_hitcount();
+	size_t hitcount() const;
+
+protected:
+	typedef std::deque<index> indices_deque;
+
+	indices_deque	m_Cache;
+	size_t			m_NbHits;
+	bool			m_PushHits;
+};
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// cache_simulator inline functions
+//////////////////////////////////////////////////////////////////////////
+
+inline cache_simulator::cache_simulator()
+	: m_NbHits(0),
+	  m_PushHits(true)
+{
+
+}
+
+
+inline void cache_simulator::clear()
+{
+	reset_hitcount();
+	m_Cache.clear();
+}
+
+
+inline void cache_simulator::resize(const size_t Size)
+{
+	m_Cache.resize(Size, std::numeric_limits<index>::max());
+}
+
+
+inline void cache_simulator::reset()
+{
+	std::fill(m_Cache.begin(), m_Cache.end(), std::numeric_limits<index>::max());
+	reset_hitcount();
+}
+
+
+inline void cache_simulator::push_cache_hits(bool Enabled)
+{
+	m_PushHits = Enabled;
+}
+
+
+inline size_t cache_simulator::size() const
+{
+	return m_Cache.size();
+}
+
+
+inline void cache_simulator::push(const index i, const bool CountCacheHit)
+{
+	if (CountCacheHit || m_PushHits) {
+
+		if (std::find(m_Cache.begin(), m_Cache.end(), i) != m_Cache.end()) {
+
+			// Should we count the cache hits?
+			if (CountCacheHit)
+				++m_NbHits;
+			
+			// Should we not push the index into the cache if it's a cache hit?
+			if (! m_PushHits)
+				return;
+		}
+	}
+	    
+	// Manage the indices cache as a FIFO structure
+	m_Cache.push_front(i);
+	m_Cache.pop_back();
+}
+
+
+inline void cache_simulator::merge(const cache_simulator & Backward, const size_t PossibleOverlap)
+{
+	const size_t Overlap = std::min(PossibleOverlap, size());
+
+	for (size_t i = 0; i < Overlap; ++i)
+		push(Backward.m_Cache[i], true);
+
+	m_NbHits += Backward.m_NbHits;
+}
+
+
+inline void cache_simulator::reset_hitcount()
+{
+	m_NbHits = 0;
+}
+
+
+inline size_t cache_simulator::hitcount() const
+{
+	return m_NbHits;
+}
+
+
+
+
+	} // namespace detail
+
+} // namespace triangle_stripper
+
+
+
+
+#endif // TRI_STRIPPER_HEADER_GUARD_CACHE_SIMULATOR_H
diff --git a/TriStripper/detail/connectivity_graph.h b/TriStripper/detail/connectivity_graph.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4c6c3abe9287ce001d3e632053f8b0580b4a722
--- /dev/null
+++ b/TriStripper/detail/connectivity_graph.h
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2004 Tanguy Fautré.
+// For conditions of distribution and use,
+// see copyright notice in tri_stripper.h
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: connectivity_graph.h 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+#ifndef TRI_STRIPPER_HEADER_GUARD_CONNECTIVITY_GRAPH_H
+#define TRI_STRIPPER_HEADER_GUARD_CONNECTIVITY_GRAPH_H
+
+#include "public_types.h"
+
+#include "graph_array.h"
+#include "types.h"
+
+
+
+
+namespace triangle_stripper
+{
+
+	namespace detail
+	{
+
+		void make_connectivity_graph(graph_array<triangle> & Triangles, const indices & Indices);
+
+	}
+
+}
+
+
+
+
+#endif // TRI_STRIPPER_HEADER_GUARD_CONNECTIVITY_GRAPH_H
diff --git a/TriStripper/detail/graph_array.h b/TriStripper/detail/graph_array.h
new file mode 100644
index 0000000000000000000000000000000000000000..92782d3553cf12030721188aa2a38e9bf86f43f8
--- /dev/null
+++ b/TriStripper/detail/graph_array.h
@@ -0,0 +1,460 @@
+//
+// Copyright (C) 2004 Tanguy Fautré.
+// For conditions of distribution and use,
+// see copyright notice in tri_stripper.h
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: graph_array.h 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+#ifndef TRI_STRIPPER_HEADER_GUARD_GRAPH_ARRAY_H
+#define TRI_STRIPPER_HEADER_GUARD_GRAPH_ARRAY_H
+
+#include <cassert>
+#include <algorithm>
+#include <functional>
+#include <limits>
+#include <vector>
+
+
+
+
+namespace triangle_stripper {
+
+	namespace detail {
+
+
+
+
+// graph_array main class
+template <class nodetype>
+class graph_array 
+{
+public:
+
+	class arc;
+	class node;
+
+	// New types
+	typedef size_t											nodeid;
+	typedef nodetype										value_type;
+	typedef std::vector<node>								node_vector;
+	typedef typename node_vector::iterator					node_iterator;
+	typedef typename node_vector::const_iterator			const_node_iterator;
+	typedef typename node_vector::reverse_iterator			node_reverse_iterator;
+	typedef typename node_vector::const_reverse_iterator	const_node_reverse_iterator;
+
+	typedef graph_array<nodetype> graph_type;
+	
+
+	// graph_array::arc class
+	class arc
+	{
+	public:
+		node_iterator terminal() const;
+
+	protected:
+		friend class graph_array<nodetype>;
+
+		arc(node_iterator Terminal);
+	
+		node_iterator	m_Terminal;
+	};
+
+
+	// New types
+	typedef std::vector<arc>					arc_list;
+	typedef typename arc_list::iterator			out_arc_iterator;
+	typedef typename arc_list::const_iterator	const_out_arc_iterator;
+
+
+	// graph_array::node class
+	class node
+	{
+	public:
+		void mark();
+		void unmark();
+		bool marked() const;
+
+		bool out_empty() const;
+		size_t out_size() const;
+
+		out_arc_iterator out_begin();
+		out_arc_iterator out_end();
+		const_out_arc_iterator out_begin() const;
+		const_out_arc_iterator out_end() const;
+
+		value_type & operator * ();
+		value_type * operator -> ();
+		const value_type & operator * () const;
+		const value_type * operator -> () const;
+
+		value_type & operator = (const value_type & Elem);
+
+	protected:
+		friend class graph_array<nodetype>;
+		friend class std::vector<node>;
+
+		node(arc_list & Arcs);
+
+		arc_list &		m_Arcs;
+		size_t			m_Begin;
+		size_t			m_End;
+
+		value_type		m_Elem;
+		bool			m_Marker;
+	};
+
+
+	graph_array();
+	explicit graph_array(size_t NbNodes);
+
+	// Node related member functions
+	bool empty() const;
+	size_t size() const;
+
+	node & operator [] (nodeid i);
+	const node & operator [] (nodeid i) const;
+
+	node_iterator begin();
+	node_iterator end();
+	const_node_iterator begin() const;
+	const_node_iterator end() const;
+
+	node_reverse_iterator rbegin();
+	node_reverse_iterator rend();
+	const_node_reverse_iterator rbegin() const;
+	const_node_reverse_iterator rend() const;
+
+	// Arc related member functions
+	out_arc_iterator insert_arc(nodeid Initial, nodeid Terminal);
+	out_arc_iterator insert_arc(node_iterator Initial, node_iterator Terminal);
+
+	// Optimized (overloaded) functions
+	void swap(graph_type & Right);
+	friend void swap(graph_type & Left, graph_type & Right)										{ Left.swap(Right); }
+
+protected:
+	graph_array(const graph_type &);
+	graph_type & operator = (const graph_type &);
+
+	node_vector		m_Nodes;
+	arc_list		m_Arcs;
+};
+
+
+
+// Additional "low level", graph related, functions
+template <class nodetype>
+void unmark_nodes(graph_array<nodetype> & G);
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// graph_array::arc inline functions
+//////////////////////////////////////////////////////////////////////////
+
+template <class N>
+inline graph_array<N>::arc::arc(node_iterator Terminal)
+	: m_Terminal(Terminal) { }
+
+
+template <class N>
+inline typename graph_array<N>::node_iterator graph_array<N>::arc::terminal() const
+{
+	return m_Terminal;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// graph_array::node inline functions
+//////////////////////////////////////////////////////////////////////////
+
+template <class N>
+inline graph_array<N>::node::node(arc_list & Arcs)
+	: m_Arcs(Arcs),
+	  m_Begin(std::numeric_limits<size_t>::max()),
+	  m_End(std::numeric_limits<size_t>::max()),
+	  m_Marker(false)
+{
+
+}
+
+
+template <class N>
+inline void graph_array<N>::node::mark()
+{
+	m_Marker = true;
+}
+
+
+template <class N>
+inline void graph_array<N>::node::unmark()
+{
+	m_Marker = false;
+}
+
+
+template <class N>
+inline bool graph_array<N>::node::marked() const
+{
+	return m_Marker;
+}
+
+
+template <class N>
+inline bool graph_array<N>::node::out_empty() const
+{
+	return (m_Begin == m_End);
+}
+
+
+template <class N>
+inline size_t graph_array<N>::node::out_size() const
+{
+	return (m_End - m_Begin);
+}
+
+
+template <class N>
+inline typename graph_array<N>::out_arc_iterator graph_array<N>::node::out_begin()
+{
+	return (m_Arcs.begin() + m_Begin);
+}
+
+
+template <class N>
+inline typename graph_array<N>::out_arc_iterator graph_array<N>::node::out_end()
+{
+	return (m_Arcs.begin() + m_End);
+}
+
+
+template <class N>
+inline typename graph_array<N>::const_out_arc_iterator graph_array<N>::node::out_begin() const
+{
+	return (m_Arcs.begin() + m_Begin);
+}
+
+
+template <class N>
+inline typename graph_array<N>::const_out_arc_iterator graph_array<N>::node::out_end() const
+{
+	return (m_Arcs.begin() + m_End);
+}
+
+
+template <class N>
+inline N & graph_array<N>::node::operator * ()
+{
+	return m_Elem;
+}
+
+
+template <class N>
+inline N * graph_array<N>::node::operator -> ()
+{
+	return &m_Elem;
+}
+
+
+template <class N>
+inline const N & graph_array<N>::node::operator * () const
+{
+	return m_Elem;
+}
+
+
+template <class N>
+inline const N * graph_array<N>::node::operator -> () const
+{
+	return &m_Elem;
+}
+
+
+template <class N>
+inline N & graph_array<N>::node::operator = (const N & Elem)
+{
+	return (m_Elem = Elem);
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// graph_array inline functions
+//////////////////////////////////////////////////////////////////////////
+
+template <class N>
+inline graph_array<N>::graph_array() { }
+
+
+template <class N>
+inline graph_array<N>::graph_array(const size_t NbNodes)
+	: m_Nodes(NbNodes, node(m_Arcs))
+{
+	// optimisation: we consider that, averagely, a triangle may have at least 2 neighbours
+	// otherwise we are just wasting a bit of memory, but not that much
+	m_Arcs.reserve(NbNodes * 2);
+}
+
+
+template <class N>
+inline bool graph_array<N>::empty() const
+{
+	return m_Nodes.empty();
+}
+
+
+template <class N>
+inline size_t graph_array<N>::size() const 
+{
+	return m_Nodes.size();
+}
+
+
+template <class N>
+inline typename graph_array<N>::node & graph_array<N>::operator [] (const nodeid i)
+{
+	assert(i < size());
+
+	return m_Nodes[i];
+}
+
+
+template <class N>
+inline const typename graph_array<N>::node & graph_array<N>::operator [] (const nodeid i) const
+{
+	assert(i < size());
+
+	return m_Nodes[i];
+}
+
+
+template <class N>
+inline typename graph_array<N>::node_iterator graph_array<N>::begin()
+{
+	return m_Nodes.begin();
+}
+
+
+template <class N>
+inline typename graph_array<N>::node_iterator graph_array<N>::end()
+{
+	return m_Nodes.end();
+}
+
+
+template <class N>
+inline typename graph_array<N>::const_node_iterator graph_array<N>::begin() const
+{
+	return m_Nodes.begin();
+}
+
+
+template <class N>
+inline typename graph_array<N>::const_node_iterator graph_array<N>::end() const
+{
+	return m_Nodes.end();
+}
+
+
+template <class N>
+inline typename graph_array<N>::node_reverse_iterator graph_array<N>::rbegin()
+{
+	return m_Nodes.rbegin();
+}
+
+
+template <class N>
+inline typename graph_array<N>::node_reverse_iterator graph_array<N>::rend()
+{
+	return m_Nodes.rend();
+}
+
+
+template <class N>
+inline typename graph_array<N>::const_node_reverse_iterator graph_array<N>::rbegin() const
+{
+	return m_Nodes.rbegin();
+}
+
+
+template <class N>
+inline typename graph_array<N>::const_node_reverse_iterator graph_array<N>::rend() const
+{
+	return m_Nodes.rend();
+}
+
+
+template <class N>
+inline typename graph_array<N>::out_arc_iterator graph_array<N>::insert_arc(const nodeid Initial, const nodeid Terminal)
+{
+	assert(Initial < size());
+	assert(Terminal < size());
+
+	return insert_arc(m_Nodes.begin() + Initial, m_Nodes.begin() + Terminal);
+}
+
+
+template <class N>
+inline typename graph_array<N>::out_arc_iterator graph_array<N>::insert_arc(const node_iterator Initial, const node_iterator Terminal)
+{
+	assert((Initial >= begin()) && (Initial < end()));
+	assert((Terminal >= begin()) && (Terminal < end()));
+
+	node & Node = * Initial;
+
+	if (Node.out_empty()) {
+
+		Node.m_Begin = m_Arcs.size();
+		Node.m_End = m_Arcs.size() + 1;
+
+	} else {
+
+		// we optimise here for make_connectivity_graph()
+		// we know all the arcs for a given node are successively and sequentially added
+		assert(Node.m_End == m_Arcs.size());
+		
+		++(Node.m_End);
+	}
+
+	m_Arcs.push_back(arc(Terminal));
+
+	out_arc_iterator it = m_Arcs.end();
+	return (--it);
+}
+
+
+template <class N>
+inline void graph_array<N>::swap(graph_type & Right)
+{
+	std::swap(m_Nodes, Right.m_Nodes);
+	std::swap(m_Arcs, Right.m_Arcs);
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// additional functions
+//////////////////////////////////////////////////////////////////////////
+
+template <class N>
+inline void unmark_nodes(graph_array<N> & G)
+{
+	std::for_each(G.begin(), G.end(), std::mem_fun_ref(&graph_array<N>::node::unmark));
+}
+
+
+
+
+	} // namespace detail
+
+} // namespace triangle_stripper
+
+
+
+
+#endif // TRI_STRIPPER_HEADER_GUARD_GRAPH_ARRAY_H
diff --git a/TriStripper/detail/heap_array.h b/TriStripper/detail/heap_array.h
new file mode 100644
index 0000000000000000000000000000000000000000..971b805bdd4bcc97de9c0e0f025f540f9b91151a
--- /dev/null
+++ b/TriStripper/detail/heap_array.h
@@ -0,0 +1,297 @@
+//
+// Copyright (C) 2004 Tanguy Fautré.
+// For conditions of distribution and use,
+// see copyright notice in tri_stripper.h
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: heap_array.h 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+#ifndef TRI_STRIPPER_HEADER_GUARD_HEAP_ARRAY_H
+#define TRI_STRIPPER_HEADER_GUARD_HEAP_ARRAY_H
+
+#include <vector>
+
+
+
+
+namespace triangle_stripper {
+
+	namespace detail {
+
+
+
+
+// mutable heap
+// can be interfaced pretty muck like an array
+template <class T, class CmpT = std::less<T> > 
+class heap_array
+{
+public:
+
+	// Pre = PreCondition, Post = PostCondition 
+
+	heap_array() : m_Locked(false) { }		// Post: ((size() == 0) && ! locked())
+
+	void clear();							// Post: ((size() == 0) && ! locked())
+
+	void reserve(size_t Size);
+	size_t size() const;
+
+	bool empty() const;
+	bool locked() const;
+	bool removed(size_t i) const;			// Pre: (valid(i))
+	bool valid(size_t i) const;
+
+	size_t position(size_t i) const;		// Pre: (valid(i))
+
+	const T & top() const;					// Pre: (! empty())
+	const T & peek(size_t i) const;			// Pre: (! removed(i))
+	const T & operator [] (size_t i) const;	// Pre: (! removed(i))
+
+	void lock();							// Pre: (! locked())   Post: (locked())
+	size_t push(const T & Elem);			// Pre: (! locked())
+
+	void pop();								// Pre: (locked() && ! empty())
+	void erase(size_t i);					// Pre: (locked() && ! removed(i))
+	void update(size_t i, const T & Elem);	// Pre: (locked() && ! removed(i))
+
+protected:
+
+	heap_array(const heap_array &);
+	heap_array & operator = (const heap_array &);
+
+	class linker
+	{
+	public:
+		linker(const T & Elem, size_t i)
+			: m_Elem(Elem), m_Index(i) { }
+
+		T		m_Elem;
+		size_t	m_Index;
+	};
+
+	typedef std::vector<linker> linked_heap;
+	typedef std::vector<size_t> finder;
+
+	void Adjust(size_t i);
+	void Swap(size_t a, size_t b);
+	bool Less(const linker & a, const linker & b) const;
+
+	linked_heap	m_Heap;
+	finder		m_Finder;
+	CmpT		m_Compare;
+	bool		m_Locked;
+};
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// heap_indexed inline functions
+//////////////////////////////////////////////////////////////////////////
+
+template <class T, class CmpT> 
+inline void heap_array<T, CmpT>::clear()
+{
+	m_Heap.clear();
+	m_Finder.clear();
+	m_Locked = false;
+}
+
+
+template <class T, class CmpT> 
+inline bool heap_array<T, CmpT>::empty() const
+{
+	return m_Heap.empty();
+}
+
+
+template <class T, class CmpT>
+inline bool heap_array<T, CmpT>::locked() const
+{
+	return m_Locked;
+}
+
+
+template <class T, class CmpT>
+inline void heap_array<T, CmpT>::reserve(const size_t Size)
+{
+	m_Heap.reserve(Size);
+	m_Finder.reserve(Size);
+}
+
+
+template <class T, class CmpT> 
+inline size_t heap_array<T, CmpT>::size() const
+{
+	return m_Heap.size();
+}
+
+
+template <class T, class CmpT> 
+inline const T & heap_array<T, CmpT>::top() const
+{
+	assert(! empty());
+
+	return m_Heap.front().m_Elem;
+}
+
+
+template <class T, class CmpT> 
+inline const T & heap_array<T, CmpT>::peek(const size_t i) const
+{
+	assert(! removed(i));
+
+	return (m_Heap[m_Finder[i]].m_Elem);
+}
+
+
+template <class T, class CmpT> 
+inline const T & heap_array<T, CmpT>::operator [] (const size_t i) const
+{
+	return peek(i);
+}
+
+
+template <class T, class CmpT> 
+inline void heap_array<T, CmpT>::pop()
+{
+	assert(locked());
+	assert(! empty());
+
+	Swap(0, size() - 1);
+	m_Heap.pop_back();
+
+	if (! empty())
+		Adjust(0);
+}
+
+
+template <class T, class CmpT>
+inline void heap_array<T, CmpT>::lock()
+{
+	assert(! locked());
+
+	m_Locked =true;
+}
+
+
+template <class T, class CmpT> 
+inline size_t heap_array<T, CmpT>::push(const T & Elem)
+{
+	assert(! locked());
+
+	const size_t Id = size();
+	m_Finder.push_back(Id);
+	m_Heap.push_back(linker(Elem, Id));
+	Adjust(Id);
+
+	return Id;
+}
+
+
+template <class T, class CmpT>
+inline void heap_array<T, CmpT>::erase(const size_t i)
+{
+	assert(locked());
+	assert(! removed(i));
+
+	const size_t j = m_Finder[i];
+	Swap(j, size() - 1);
+	m_Heap.pop_back();
+
+	if (j != size())
+		Adjust(j);
+}
+
+
+template <class T, class CmpT>
+inline bool heap_array<T, CmpT>::removed(const size_t i) const
+{
+	assert(valid(i));
+
+	return (m_Finder[i] >= m_Heap.size());
+}
+
+
+template <class T, class CmpT>
+inline bool heap_array<T, CmpT>::valid(const size_t i) const
+{
+	return (i < m_Finder.size());
+}
+
+
+template <class T, class CmpT>
+inline size_t heap_array<T, CmpT>::position(const size_t i) const
+{
+	assert(valid(i));
+
+	return (m_Heap[i].m_Index);
+}
+
+
+template <class T, class CmpT> 
+inline void heap_array<T, CmpT>::update(const size_t i, const T & Elem)
+{
+	assert(locked());
+	assert(! removed(i));
+
+	const size_t j = m_Finder[i];
+	m_Heap[j].m_Elem = Elem;
+	Adjust(j);
+}
+
+
+template <class T, class CmpT> 
+inline void heap_array<T, CmpT>::Adjust(size_t i)
+{
+	assert(i < m_Heap.size());
+
+	size_t j;
+
+	// Check the upper part of the heap
+	for (j = i; (j > 0) && (Less(m_Heap[(j - 1) / 2], m_Heap[j])); j = ((j - 1) / 2))
+		Swap(j, (j - 1) / 2);
+
+	// Check the lower part of the heap
+	for (i = j; (j = 2 * i + 1) < size(); i = j) {
+ 		if ((j + 1 < size()) && (Less(m_Heap[j], m_Heap[j + 1])))
+			++j;
+
+		if (Less(m_Heap[j], m_Heap[i]))
+			return;
+
+		Swap(i, j);
+	}
+}
+
+
+template <class T, class CmpT> 
+inline void heap_array<T, CmpT>::Swap(const size_t a, const size_t b)
+{
+	std::swap(m_Heap[a], m_Heap[b]);
+
+	m_Finder[(m_Heap[a].m_Index)] = a;
+	m_Finder[(m_Heap[b].m_Index)] = b;
+}
+
+
+template <class T, class CmpT>
+inline bool heap_array<T, CmpT>::Less(const linker & a, const linker & b) const
+{
+	return m_Compare(a.m_Elem, b.m_Elem);
+}
+
+
+
+
+	} // namespace detail
+
+} // namespace triangle_stripper
+
+
+
+
+#endif // TRI_STRIPPER_HEADER_GUARD_HEAP_ARRAY_H
diff --git a/TriStripper/detail/policy.h b/TriStripper/detail/policy.h
new file mode 100644
index 0000000000000000000000000000000000000000..4c126c598cabaf3b7d8cc5311de8e320bf0886c1
--- /dev/null
+++ b/TriStripper/detail/policy.h
@@ -0,0 +1,66 @@
+//
+// Copyright (C) 2004 Tanguy Fautré.
+// For conditions of distribution and use,
+// see copyright notice in tri_stripper.h
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: policy.h 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+#ifndef TRI_STRIPPER_HEADER_GUARD_POLICY_H
+#define TRI_STRIPPER_HEADER_GUARD_POLICY_H
+
+#include "public_types.h"
+#include "types.h"
+
+
+
+
+namespace triangle_stripper {
+
+	namespace detail {
+
+
+
+
+class policy
+{
+public:
+	policy(size_t MinStripSize, bool Cache);
+
+	strip BestStrip() const;
+	void Challenge(strip Strip, size_t Degree, size_t CacheHits);
+
+private:
+	strip	m_Strip;
+	size_t	m_Degree;
+	size_t	m_CacheHits;
+
+	const size_t	m_MinStripSize;
+	const bool		m_Cache;
+};
+
+
+
+
+
+inline policy::policy(size_t MinStripSize, bool Cache)
+: m_Degree(0), m_CacheHits(0), m_MinStripSize(MinStripSize), m_Cache(Cache) { }
+
+
+inline strip policy::BestStrip() const
+{
+	return m_Strip;
+}
+
+
+
+
+	} // namespace detail
+
+} // namespace triangle_stripper
+
+
+
+
+#endif // TRI_STRIPPER_HEADER_GUARD_POLICY_H
diff --git a/TriStripper/detail/types.h b/TriStripper/detail/types.h
new file mode 100644
index 0000000000000000000000000000000000000000..ff7b026a08e5f163dd92902093c22bf3cf949078
--- /dev/null
+++ b/TriStripper/detail/types.h
@@ -0,0 +1,101 @@
+//
+// Copyright (C) 2004 Tanguy Fautré.
+// For conditions of distribution and use,
+// see copyright notice in tri_stripper.h
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: types.h 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+#ifndef TRI_STRIPPER_HEADER_GUARD_TYPES_H
+#define TRI_STRIPPER_HEADER_GUARD_TYPES_H
+
+
+
+
+namespace triangle_stripper {
+
+	namespace detail {
+
+
+
+
+class triangle
+{
+public:
+	triangle() { }
+	triangle(index A, index B, index C)
+		: m_A(A), m_B(B), m_C(C), m_StripID(0) { }
+
+	void ResetStripID()							{ m_StripID = 0; }
+	void SetStripID(size_t StripID)				{ m_StripID = StripID; }	
+	size_t StripID() const						{ return m_StripID; }
+
+	index A() const								{ return m_A; }
+	index B() const								{ return m_B; }
+	index C() const								{ return m_C; }
+	
+private:
+	index	m_A;
+	index	m_B;
+	index	m_C;
+
+	size_t	m_StripID;
+};
+
+
+
+class triangle_edge
+{
+public:
+	triangle_edge(index A, index B)
+		: m_A(A), m_B(B) { }
+
+	index A() const								{ return m_A; }
+	index B() const								{ return m_B; }
+
+	bool operator == (const triangle_edge & Right) const {
+		return ((A() == Right.A()) && (B() == Right.B()));
+	}
+
+private:
+	index	m_A;
+	index	m_B;
+};
+
+
+
+enum triangle_order { ABC, BCA, CAB };
+
+
+
+class strip
+{
+public:
+	strip()
+		: m_Start(0), m_Order(ABC), m_Size(0) { }
+
+	strip(size_t Start, triangle_order Order, size_t Size)
+		: m_Start(Start), m_Order(Order), m_Size(Size) { }
+
+	size_t Start() const						{ return m_Start; }
+	triangle_order Order() const				{ return m_Order; }
+	size_t Size() const							{ return m_Size; }
+
+private:
+	size_t			m_Start;
+	triangle_order	m_Order;
+	size_t			m_Size;
+};
+
+
+
+
+	} // namespace detail
+
+} // namespace triangle_stripper
+
+
+
+
+#endif // TRI_STRIPPER_HEADER_GUARD_TYPES_H
diff --git a/TriStripper/policy.cpp b/TriStripper/policy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a0a8e101102df939f529d2af525aa1c2ebfd7c59
--- /dev/null
+++ b/TriStripper/policy.cpp
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2004 Tanguy Fautré.
+// For conditions of distribution and use,
+// see copyright notice in tri_stripper.h
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: policy.cpp 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+#include "detail/policy.h"
+
+
+
+
+namespace triangle_stripper {
+
+	namespace detail {
+
+
+
+
+void policy::Challenge(strip Strip, size_t Degree, size_t CacheHits)
+{
+	if (Strip.Size() < m_MinStripSize)
+		return;
+
+	// Cache is disabled, take the longest strip
+	if (! m_Cache) {
+
+		if (Strip.Size() > m_Strip.Size())
+			m_Strip = Strip;
+
+	// Cache simulator enabled
+	} else {
+
+		// Priority 1: Keep the strip with the best cache hit count
+		if (CacheHits > m_CacheHits) {
+			m_Strip = Strip;
+			m_Degree = Degree;
+			m_CacheHits = CacheHits;
+
+		} else if (CacheHits == m_CacheHits) {
+
+			// Priority 2: Keep the strip with the loneliest start triangle
+			if ((m_Strip.Size() != 0) && (Degree < m_Degree)) {
+				m_Strip = Strip;
+				m_Degree = Degree;
+
+			// Priority 3: Keep the longest strip 
+			} else if (Strip.Size() > m_Strip.Size()) {
+				m_Strip = Strip;
+				m_Degree = Degree;
+			}
+		}
+	}
+}
+
+
+
+
+	} // namespace detail
+
+} // namespace triangle_stripper
diff --git a/TriStripper/public_types.h b/TriStripper/public_types.h
new file mode 100644
index 0000000000000000000000000000000000000000..66cca5cf178a22cff1bdda2fa4fec50f5dc10fd0
--- /dev/null
+++ b/TriStripper/public_types.h
@@ -0,0 +1,43 @@
+//
+// Copyright (C) 2004 Tanguy Fautré.
+// For conditions of distribution and use,
+// see copyright notice in tri_stripper.h
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: public_types.h 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+#ifndef TRI_STRIPPER_HEADER_GUARD_PUBLIC_TYPES_H
+#define TRI_STRIPPER_HEADER_GUARD_PUBLIC_TYPES_H
+
+#include <vector>
+
+
+
+
+namespace triangle_stripper
+{
+
+	typedef size_t index;
+	typedef std::vector<index> indices;
+
+	enum primitive_type
+	{
+		TRIANGLES		= 0x0004,	// = GL_TRIANGLES
+		TRIANGLE_STRIP	= 0x0005	// = GL_TRIANGLE_STRIP
+	};
+
+	struct primitive_group
+	{
+		indices			Indices;
+		primitive_type	Type;
+	};
+
+	typedef std::vector<primitive_group> primitive_vector;
+
+}
+
+
+
+
+#endif // TRI_STRIPPER_HEADER_GUARD_PUBLIC_TYPES_H
diff --git a/TriStripper/tri_stripper.cpp b/TriStripper/tri_stripper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2f5a7f3c33c92df7fce0518ca3c69d749b6024e9
--- /dev/null
+++ b/TriStripper/tri_stripper.cpp
@@ -0,0 +1,554 @@
+//
+// Copyright (C) 2004 Tanguy Fautré.
+// For conditions of distribution and use,
+// see copyright notice in tri_stripper.h
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: tri_stripper.cpp 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+#include "tri_stripper.h"
+
+#include "detail/connectivity_graph.h"
+#include "detail/policy.h"
+
+#include <cassert>
+
+
+
+
+namespace triangle_stripper {
+
+	using namespace detail;
+
+
+
+
+tri_stripper::tri_stripper(const indices & TriIndices)
+	: m_Triangles(TriIndices.size() / 3), // Silently ignore extra indices if (Indices.size() % 3 != 0)
+	  m_StripID(0),
+	  m_FirstRun(true)
+{
+	SetCacheSize();
+	SetMinStripSize();
+	SetBackwardSearch();
+	SetPushCacheHits();
+
+	make_connectivity_graph(m_Triangles, TriIndices);
+}
+
+
+
+void tri_stripper::Strip(primitive_vector * out_pPrimitivesVector)
+{
+	assert(out_pPrimitivesVector);
+
+	if (! m_FirstRun) {
+		unmark_nodes(m_Triangles);
+		ResetStripIDs();
+		m_Cache.reset();
+		m_TriHeap.clear();
+		m_Candidates.clear();
+		m_StripID = 0;
+
+		m_FirstRun = false;
+	}
+
+	out_pPrimitivesVector->clear();
+
+	InitTriHeap();
+
+	Stripify();
+	AddLeftTriangles();
+	
+	std::swap(m_PrimitivesVector, (* out_pPrimitivesVector));
+}
+
+
+
+void tri_stripper::InitTriHeap()
+{
+	m_TriHeap.reserve(m_Triangles.size());
+
+	// Set up the triangles priority queue
+	// The lower the number of available neighbour triangles, the higher the priority.
+	for (size_t i = 0; i < m_Triangles.size(); ++i)
+		m_TriHeap.push(m_Triangles[i].out_size());
+
+	// We're not going to add new elements anymore
+	m_TriHeap.lock();
+
+	// Remove useless triangles
+	// Note: we had to put all of them into the heap before to ensure coherency of the heap_array object
+	while ((! m_TriHeap.empty()) && (m_TriHeap.top() == 0))
+		m_TriHeap.pop();
+}
+
+
+
+void tri_stripper::ResetStripIDs()
+{
+	for (triangle_graph::node_iterator it = m_Triangles.begin(); it != m_Triangles.end(); ++it)
+		(**it).ResetStripID();
+}
+
+
+
+void tri_stripper::Stripify()
+{
+	while (! m_TriHeap.empty()) {
+
+		// There is no triangle in the candidates list, refill it with the loneliest triangle
+		const size_t HeapTop = m_TriHeap.position(0);
+		m_Candidates.push_back(HeapTop);
+
+		while (! m_Candidates.empty()) {
+
+			// Note: FindBestStrip empties the candidate list, while BuildStrip refills it
+			const strip TriStrip = FindBestStrip();
+
+			if (TriStrip.Size() >= m_MinStripSize)
+				BuildStrip(TriStrip);
+		}
+
+		if (! m_TriHeap.removed(HeapTop))
+			m_TriHeap.erase(HeapTop);
+
+		// Eliminate all the triangles that have now become useless
+		while ((! m_TriHeap.empty()) && (m_TriHeap.top() == 0))
+			m_TriHeap.pop();
+	}
+}
+
+
+
+inline strip tri_stripper::FindBestStrip()
+{
+	// Allow to restore the cache (modified by ExtendTriToStrip) and implicitly reset the cache hit count
+	const cache_simulator CacheBackup = m_Cache;
+
+	policy Policy(m_MinStripSize, Cache());
+
+	while (! m_Candidates.empty()) {
+
+		const size_t Candidate = m_Candidates.back();
+		m_Candidates.pop_back();
+
+		// Discard useless triangles from the candidate list
+		if ((m_Triangles[Candidate].marked()) || (m_TriHeap[Candidate] == 0))
+			continue;		
+
+		// Try to extend the triangle in the 3 possible forward directions
+		for (size_t i = 0; i < 3; ++i) {
+
+			const strip Strip = ExtendToStrip(Candidate, triangle_order(i));
+			Policy.Challenge(Strip, m_TriHeap[Strip.Start()], m_Cache.hitcount());
+			
+			m_Cache = CacheBackup;
+		}
+
+		// Try to extend the triangle in the 6 possible backward directions
+		if (m_BackwardSearch) {
+
+			for (size_t i = 0; i < 3; ++i) {
+
+				const strip Strip = BackExtendToStrip(Candidate, triangle_order(i), false);
+				Policy.Challenge(Strip, m_TriHeap[Strip.Start()], m_Cache.hitcount());
+			
+				m_Cache = CacheBackup;
+			}
+
+			for (size_t i = 0; i < 3; ++i) {
+
+				const strip Strip = BackExtendToStrip(Candidate, triangle_order(i), true);
+				Policy.Challenge(Strip, m_TriHeap[Strip.Start()], m_Cache.hitcount());
+			
+				m_Cache = CacheBackup;
+			}
+		}
+
+	}
+
+	return Policy.BestStrip();
+}
+
+
+
+strip tri_stripper::ExtendToStrip(const size_t Start, triangle_order Order)
+{
+	const triangle_order StartOrder = Order;
+	
+	// Begin a new strip
+	m_Triangles[Start]->SetStripID(++m_StripID);
+	AddTriangle(* m_Triangles[Start], Order, false);
+
+	size_t Size = 1;
+	bool ClockWise = false;
+
+	// Loop while we can further extend the strip
+	for (tri_iterator Node = (m_Triangles.begin() + Start); 
+		(Node != m_Triangles.end()) && (!Cache() || ((Size + 2) < CacheSize()));
+		++Size) {
+
+		const const_link_iterator Link = LinkToNeighbour(Node, ClockWise, Order, false);
+
+		// Is it the end of the strip?
+		if (Link == Node->out_end()) {
+
+			Node = m_Triangles.end();
+			--Size;
+
+		} else {
+
+			Node = Link->terminal();
+			(* Node)->SetStripID(m_StripID);
+			ClockWise = ! ClockWise;
+
+		}
+	}
+
+	return strip(Start, StartOrder, Size);
+}
+
+
+
+strip tri_stripper::BackExtendToStrip(size_t Start, triangle_order Order, bool ClockWise)
+{
+	// Begin a new strip
+	m_Triangles[Start]->SetStripID(++m_StripID);
+	BackAddIndex(LastEdge(* m_Triangles[Start], Order).B());
+	size_t Size = 1;
+
+	tri_iterator Node;
+
+	// Loop while we can further extend the strip
+	for (Node = (m_Triangles.begin() + Start); 
+		!Cache() || ((Size + 2) < CacheSize());
+		++Size) {
+
+		const const_link_iterator Link = BackLinkToNeighbour(Node, ClockWise, Order);
+
+		// Is it the end of the strip?
+		if (Link == Node->out_end())
+			break;
+
+		else {
+			Node = Link->terminal();
+			(* Node)->SetStripID(m_StripID);
+			ClockWise = ! ClockWise;
+		}
+	}
+
+	// We have to start from a counterclockwise triangle.
+	// Simply return an empty strip in the case where the first triangle is clockwise.
+	// Even though we could discard the first triangle and start from the next counterclockwise triangle,
+	// this often leads to more lonely triangles afterward.
+	if (ClockWise)
+		return strip();
+
+	if (Cache()) {
+		m_Cache.merge(m_BackCache, Size);
+		m_BackCache.reset();
+	}
+
+	return strip(Node - m_Triangles.begin(), Order, Size);
+}
+
+
+
+void tri_stripper::BuildStrip(const strip Strip)
+{
+	const size_t Start = Strip.Start();
+
+	bool ClockWise = false;
+	triangle_order Order = Strip.Order();
+
+	// Create a new strip
+	m_PrimitivesVector.push_back(primitive_group());
+	m_PrimitivesVector.back().Type = TRIANGLE_STRIP;
+	AddTriangle(* m_Triangles[Start], Order, true);
+	MarkTriAsTaken(Start);
+
+	// Loop while we can further extend the strip
+	tri_iterator Node = (m_Triangles.begin() + Start);
+
+	for (size_t Size = 1; Size < Strip.Size(); ++Size) {
+
+		const const_link_iterator Link = LinkToNeighbour(Node, ClockWise, Order, true);
+
+		assert(Link != Node->out_end());
+
+		// Go to the next triangle
+		Node = Link->terminal();
+		MarkTriAsTaken(Node - m_Triangles.begin());
+		ClockWise = ! ClockWise;
+	}
+}
+
+
+
+inline tri_stripper::const_link_iterator tri_stripper::LinkToNeighbour(const const_tri_iterator Node, const bool ClockWise, triangle_order & Order, const bool NotSimulation)
+{
+	const triangle_edge Edge = LastEdge(** Node, Order);
+
+	for (const_link_iterator Link = Node->out_begin(); Link != Node->out_end(); ++Link) {
+
+		// Get the reference to the possible next triangle
+		const triangle & Tri = ** Link->terminal();
+
+		// Check whether it's already been used
+		if (NotSimulation || (Tri.StripID() != m_StripID)) {
+
+			if (! Link->terminal()->marked()) {
+
+				// Does the current candidate triangle match the required position for the strip?
+
+				if ((Edge.B() == Tri.A()) && (Edge.A() == Tri.B())) {
+					Order = (ClockWise) ? ABC : BCA;
+					AddIndex(Tri.C(), NotSimulation);
+					return Link;
+				}
+
+				else if ((Edge.B() == Tri.B()) && (Edge.A() == Tri.C())) {
+					Order = (ClockWise) ? BCA : CAB;
+					AddIndex(Tri.A(), NotSimulation);
+					return Link;
+				}
+
+				else if ((Edge.B() == Tri.C()) && (Edge.A() == Tri.A())) {
+					Order = (ClockWise) ? CAB : ABC;
+					AddIndex(Tri.B(), NotSimulation);
+					return Link;
+				}
+			}
+		}
+
+	}
+
+	return Node->out_end();
+}
+
+
+
+inline tri_stripper::const_link_iterator tri_stripper::BackLinkToNeighbour(const_tri_iterator Node, bool ClockWise, triangle_order & Order)
+{
+	const triangle_edge Edge = FirstEdge(** Node, Order);
+
+	for (const_link_iterator Link = Node->out_begin(); Link != Node->out_end(); ++Link) {
+
+		// Get the reference to the possible previous triangle
+		const triangle & Tri = ** Link->terminal();
+
+		// Check whether it's already been used
+		if ((Tri.StripID() != m_StripID) && ! Link->terminal()->marked()) {
+
+			// Does the current candidate triangle match the required position for the strip?
+
+			if ((Edge.B() == Tri.A()) && (Edge.A() == Tri.B())) {
+				Order = (ClockWise) ? CAB : BCA;
+				BackAddIndex(Tri.C());
+				return Link;
+			}
+
+			else if ((Edge.B() == Tri.B()) && (Edge.A() == Tri.C())) {
+				Order = (ClockWise) ? ABC : CAB;
+				BackAddIndex(Tri.A());
+				return Link;
+			}
+
+			else if ((Edge.B() == Tri.C()) && (Edge.A() == Tri.A())) {
+				Order = (ClockWise) ? BCA : ABC;
+				BackAddIndex(Tri.B());
+				return Link;
+			}
+		}
+
+	}
+
+	return Node->out_end();
+}
+
+
+
+void tri_stripper::MarkTriAsTaken(const size_t i)
+{
+	typedef triangle_graph::node_iterator tri_node_iter;
+	typedef triangle_graph::out_arc_iterator tri_link_iter;
+
+	// Mark the triangle node
+	m_Triangles[i].mark();
+
+	// Remove triangle from priority queue if it isn't yet
+	if (! m_TriHeap.removed(i))
+		m_TriHeap.erase(i);
+
+	// Adjust the degree of available neighbour triangles
+	for (tri_link_iter Link = m_Triangles[i].out_begin(); Link != m_Triangles[i].out_end(); ++Link) {
+
+		const size_t j = Link->terminal() - m_Triangles.begin();
+
+		if ((! m_Triangles[j].marked()) && (! m_TriHeap.removed(j))) {
+			size_t NewDegree = m_TriHeap.peek(j);
+			NewDegree = NewDegree - 1;
+			m_TriHeap.update(j, NewDegree);
+
+			// Update the candidate list if cache is enabled
+			if (Cache() && (NewDegree > 0))
+				m_Candidates.push_back(j);
+		}
+	}
+}
+
+
+
+inline triangle_edge tri_stripper::FirstEdge(const triangle & Triangle, const triangle_order Order)
+{
+	switch (Order)
+	{
+	case ABC:
+		return triangle_edge(Triangle.A(), Triangle.B());
+
+	case BCA:
+		return triangle_edge(Triangle.B(), Triangle.C());
+
+	case CAB:
+		return triangle_edge(Triangle.C(), Triangle.A());
+
+	default:
+		assert(false);
+		return triangle_edge(0, 0);
+	}
+}
+
+
+
+inline triangle_edge tri_stripper::LastEdge(const triangle & Triangle, const triangle_order Order)
+{
+	switch (Order)
+	{
+	case ABC:
+		return triangle_edge(Triangle.B(), Triangle.C());
+
+	case BCA:
+		return triangle_edge(Triangle.C(), Triangle.A());
+
+	case CAB:
+		return triangle_edge(Triangle.A(), Triangle.B());
+
+	default:
+		assert(false);
+		return triangle_edge(0, 0);
+	}
+}
+
+
+
+inline void tri_stripper::AddIndex(const index i, const bool NotSimulation)
+{
+	if (Cache())
+		m_Cache.push(i, ! NotSimulation);
+
+	if (NotSimulation)
+		m_PrimitivesVector.back().Indices.push_back(i);
+}
+
+
+
+inline void tri_stripper::BackAddIndex(const index i)
+{
+	if (Cache())
+		m_BackCache.push(i, true);
+}
+
+
+
+inline void tri_stripper::AddTriangle(const triangle & Tri, const triangle_order Order, const bool NotSimulation)
+{
+	switch (Order)
+	{
+	case ABC:
+		AddIndex(Tri.A(), NotSimulation);
+		AddIndex(Tri.B(), NotSimulation);
+		AddIndex(Tri.C(), NotSimulation);
+		break;
+
+	case BCA:
+		AddIndex(Tri.B(), NotSimulation);
+		AddIndex(Tri.C(), NotSimulation);
+		AddIndex(Tri.A(), NotSimulation);
+		break;
+
+	case CAB:
+		AddIndex(Tri.C(), NotSimulation);
+		AddIndex(Tri.A(), NotSimulation);
+		AddIndex(Tri.B(), NotSimulation);
+		break;
+	}
+}
+
+
+
+inline void tri_stripper::BackAddTriangle(const triangle & Tri, const triangle_order Order)
+{
+	switch (Order)
+	{
+	case ABC:
+		BackAddIndex(Tri.C());
+		BackAddIndex(Tri.B());
+		BackAddIndex(Tri.A());
+		break;
+
+	case BCA:
+		BackAddIndex(Tri.A());
+		BackAddIndex(Tri.C());
+		BackAddIndex(Tri.B());
+		break;
+
+	case CAB:
+		BackAddIndex(Tri.B());
+		BackAddIndex(Tri.A());
+		BackAddIndex(Tri.C());
+		break;
+	}
+}
+
+
+
+void tri_stripper::AddLeftTriangles()
+{
+	// Create the last indices array and fill it with all the triangles that couldn't be stripped
+	primitive_group Primitives;
+	Primitives.Type = TRIANGLES;
+	m_PrimitivesVector.push_back(Primitives);
+	indices & Indices = m_PrimitivesVector.back().Indices;
+
+	for (size_t i = 0; i < m_Triangles.size(); ++i)
+		if (! m_Triangles[i].marked()) {
+			Indices.push_back(m_Triangles[i]->A());
+			Indices.push_back(m_Triangles[i]->B());
+			Indices.push_back(m_Triangles[i]->C());
+		}
+
+	// Undo if useless
+	if (Indices.size() == 0)
+		m_PrimitivesVector.pop_back();
+}
+
+
+
+inline bool tri_stripper::Cache() const
+{
+	return (m_Cache.size() != 0);
+}
+
+
+
+inline size_t tri_stripper::CacheSize() const
+{
+	return m_Cache.size();
+}
+
+
+
+
+} // namespace triangle_stripper
diff --git a/TriStripper/tri_stripper.h b/TriStripper/tri_stripper.h
new file mode 100644
index 0000000000000000000000000000000000000000..4b114ed9eaef91c80dadbcefe7617a60a9be0686
--- /dev/null
+++ b/TriStripper/tri_stripper.h
@@ -0,0 +1,190 @@
+
+//////////////////////////////////////////////////////////////////////
+//
+//  Copyright (C) 2004 Tanguy Fautré.
+//
+//  This software is provided 'as-is', without any express or implied
+//  warranty.  In no event will the authors be held liable for any damages
+//  arising from the use of this software.
+//
+//  Permission is granted to anyone to use this software for any purpose,
+//  including commercial applications, and to alter it and redistribute it
+//  freely, subject to the following restrictions:
+//
+//  1. The origin of this software must not be misrepresented; you must not
+//     claim that you wrote the original software. If you use this software
+//     in a product, an acknowledgment in the product documentation would be
+//     appreciated but is not required.
+//  2. Altered source versions must be plainly marked as such, and must not be
+//     misrepresented as being the original software.
+//  3. This notice may not be removed or altered from any source distribution.
+//
+//  Tanguy Fautré
+//  softdev@telenet.be
+//
+//////////////////////////////////////////////////////////////////////
+//
+//							Tri Stripper
+//							************
+//
+// Post TnL cache aware triangle stripifier in O(n.log(n)).
+//          
+// History: see ChangeLog
+//
+//////////////////////////////////////////////////////////////////////
+// SVN: $Id: tri_stripper.h 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
+
+// Protection against old C habits
+#if defined(max)
+#error "'max' macro defined! It's against the C++ standard. Please use 'std::max' instead (undefine 'max' macro if it was defined in another library)."
+#endif
+
+// Protection against old C habits
+#if defined(min)
+#error "'min' macro defined! It's against the C++ standard. Please use 'std::min' instead (undefine 'min' macro if it was defined in another library)."
+#endif
+
+
+
+#ifndef TRI_STRIPPER_HEADER_GUARD_TRI_STRIPPER_H
+#define TRI_STRIPPER_HEADER_GUARD_TRI_STRIPPER_H
+
+#include "public_types.h"
+
+#include "detail/cache_simulator.h"
+#include "detail/graph_array.h"
+#include "detail/heap_array.h"
+#include "detail/types.h"
+
+
+
+
+namespace triangle_stripper {
+
+
+
+
+class tri_stripper
+{
+public:
+
+	tri_stripper(const indices & TriIndices);
+
+	void Strip(primitive_vector * out_pPrimitivesVector);
+
+	/* Stripifier Algorithm Settings */
+	
+	// Set the post-T&L cache size (0 disables the cache optimizer).
+	void SetCacheSize(size_t CacheSize = 10);
+
+	// Set the minimum size of a triangle strip (should be at least 2 triangles).
+	// The stripifier discard any candidate strips that does not satisfy the minimum size condition.
+	void SetMinStripSize(size_t MinStripSize = 2);
+
+	// Set the backward search mode in addition to the forward search mode.
+	// In forward mode, the candidate strips are build with the current candidate triangle being the first
+	// triangle of the strip. When the backward mode is enabled, the stripifier also tests candidate strips
+	// where the current candidate triangle is the last triangle of the strip.
+	// Enable this if you want better results at the expense of being slightly slower.
+	// Note: Do *NOT* use this when the cache optimizer is enabled; it only gives worse results.
+	void SetBackwardSearch(bool Enabled = false);
+	
+	// Set the cache simulator FIFO behavior (does nothing if the cache optimizer is disabled).
+	// When enabled, the cache is simulated as a simple FIFO structure. However, when
+	// disabled, indices that trigger cache hits are not pushed into the FIFO structure.
+	// This allows simulating some GPUs that do not duplicate cache entries (e.g. NV25 or greater).
+	void SetPushCacheHits(bool Enabled = true);
+
+	/* End Settings */
+
+private:
+
+	typedef detail::graph_array<detail::triangle> triangle_graph;
+	typedef detail::heap_array<size_t, std::greater<size_t> > triangle_heap;
+	typedef std::vector<size_t> candidates;
+	typedef triangle_graph::node_iterator tri_iterator;
+	typedef triangle_graph::const_node_iterator const_tri_iterator;
+	typedef triangle_graph::out_arc_iterator link_iterator;
+	typedef triangle_graph::const_out_arc_iterator const_link_iterator;
+
+	void InitTriHeap();
+	void Stripify();
+	void AddLeftTriangles();
+	void ResetStripIDs();
+
+	detail::strip FindBestStrip();
+	detail::strip ExtendToStrip(size_t Start, detail::triangle_order Order);
+	detail::strip BackExtendToStrip(size_t Start, detail::triangle_order Order, bool ClockWise);
+	const_link_iterator LinkToNeighbour(const_tri_iterator Node, bool ClockWise, detail::triangle_order & Order, bool NotSimulation);
+	const_link_iterator BackLinkToNeighbour(const_tri_iterator Node, bool ClockWise, detail::triangle_order & Order);
+	void BuildStrip(const detail::strip Strip);
+	void MarkTriAsTaken(size_t i);
+	void AddIndex(index i, bool NotSimulation);
+	void BackAddIndex(index i);
+	void AddTriangle(const detail::triangle & Tri, detail::triangle_order Order, bool NotSimulation);
+	void BackAddTriangle(const detail::triangle & Tri, detail::triangle_order Order);
+
+	bool Cache() const;
+	size_t CacheSize() const;
+
+	static detail::triangle_edge FirstEdge(const detail::triangle & Triangle, detail::triangle_order Order);
+	static detail::triangle_edge LastEdge(const detail::triangle & Triangle, detail::triangle_order Order);
+
+	primitive_vector			m_PrimitivesVector;
+	triangle_graph				m_Triangles;
+	triangle_heap				m_TriHeap;
+	candidates					m_Candidates;
+	detail::cache_simulator		m_Cache;
+	detail::cache_simulator		m_BackCache;
+	size_t						m_StripID;
+	size_t						m_MinStripSize;
+	bool						m_BackwardSearch;
+	bool						m_FirstRun;
+};
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// tri_stripper inline functions
+//////////////////////////////////////////////////////////////////////////
+
+inline void tri_stripper::SetCacheSize(const size_t CacheSize)
+{
+	m_Cache.resize(CacheSize);
+	m_BackCache.resize(CacheSize);
+}
+
+
+inline void tri_stripper::SetMinStripSize(const size_t MinStripSize)
+{
+	if (MinStripSize < 2)
+		m_MinStripSize = 2;
+	else
+		m_MinStripSize = MinStripSize;
+}
+
+
+inline void tri_stripper::SetBackwardSearch(const bool Enabled)
+{
+	m_BackwardSearch = Enabled;
+}
+
+
+
+inline void tri_stripper::SetPushCacheHits(bool Enabled)
+{
+	m_Cache.push_cache_hits(Enabled);
+}
+
+
+
+
+} // namespace triangle_stripper
+
+
+
+
+#endif // TRI_STRIPPER_HEADER_GUARD_TRI_STRIPPER_H
diff --git a/niflib.vcproj b/niflib.vcproj
index 563cf0b7725fd2024ec1cfef26b05ea2eca68b24..4fbe528de37c94ebae2e523e53e4cc463e9ff4f9 100644
--- a/niflib.vcproj
+++ b/niflib.vcproj
@@ -48,7 +48,6 @@
 				UsePrecompiledHeader="0"
 				PrecompiledHeaderThrough="$(ProjectDir)pch.h"
 				WarningLevel="3"
-				Detect64BitPortabilityProblems="true"
 				DebugInformationFormat="3"
 			/>
 			<Tool
@@ -114,7 +113,6 @@
 				UsePrecompiledHeader="2"
 				PrecompiledHeaderThrough="$(ProjectDir)pch.h"
 				WarningLevel="3"
-				Detect64BitPortabilityProblems="true"
 				DebugInformationFormat="3"
 			/>
 			<Tool
@@ -175,7 +173,6 @@
 				RuntimeLibrary="0"
 				UsePrecompiledHeader="0"
 				WarningLevel="3"
-				Detect64BitPortabilityProblems="true"
 				DebugInformationFormat="3"
 			/>
 			<Tool
@@ -239,7 +236,6 @@
 				UsePrecompiledHeader="2"
 				PrecompiledHeaderThrough="pch.h"
 				WarningLevel="3"
-				Detect64BitPortabilityProblems="true"
 				DebugInformationFormat="3"
 			/>
 			<Tool
@@ -314,7 +310,6 @@
 				UsePrecompiledHeader="2"
 				PrecompiledHeaderThrough="$(ProjectDir)pch.h"
 				WarningLevel="3"
-				Detect64BitPortabilityProblems="true"
 				DebugInformationFormat="4"
 			/>
 			<Tool
@@ -1392,6 +1387,38 @@
 					>
 				</File>
 			</Filter>
+			<Filter
+				Name="NvTriStrip"
+				>
+				<File
+					RelativePath=".\NvTriStrip\NvTriStrip.cpp"
+					>
+				</File>
+				<File
+					RelativePath=".\NvTriStrip\NvTriStripObjects.cpp"
+					>
+				</File>
+				<File
+					RelativePath=".\NvTriStrip\VertexCache.cpp"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="TriStripper"
+				>
+				<File
+					RelativePath=".\TriStripper\connectivity_graph.cpp"
+					>
+				</File>
+				<File
+					RelativePath=".\TriStripper\policy.cpp"
+					>
+				</File>
+				<File
+					RelativePath=".\TriStripper\tri_stripper.cpp"
+					>
+				</File>
+			</Filter>
 		</Filter>
 		<Filter
 			Name="Header Files"
@@ -2426,6 +2453,62 @@
 					>
 				</File>
 			</Filter>
+			<Filter
+				Name="TriStripper"
+				>
+				<File
+					RelativePath=".\TriStripper\public_types.h"
+					>
+				</File>
+				<File
+					RelativePath=".\TriStripper\tri_stripper.h"
+					>
+				</File>
+				<Filter
+					Name="detail"
+					>
+					<File
+						RelativePath=".\TriStripper\detail\cache_simulator.h"
+						>
+					</File>
+					<File
+						RelativePath=".\TriStripper\detail\connectivity_graph.h"
+						>
+					</File>
+					<File
+						RelativePath=".\TriStripper\detail\graph_array.h"
+						>
+					</File>
+					<File
+						RelativePath=".\TriStripper\detail\heap_array.h"
+						>
+					</File>
+					<File
+						RelativePath=".\TriStripper\detail\policy.h"
+						>
+					</File>
+					<File
+						RelativePath=".\TriStripper\detail\types.h"
+						>
+					</File>
+				</Filter>
+			</Filter>
+			<Filter
+				Name="NvTriStrip"
+				>
+				<File
+					RelativePath=".\NvTriStrip\NvTriStrip.h"
+					>
+				</File>
+				<File
+					RelativePath=".\NvTriStrip\NvTriStripObjects.h"
+					>
+				</File>
+				<File
+					RelativePath=".\NvTriStrip\VertexCache.h"
+					>
+				</File>
+			</Filter>
 		</Filter>
 		<Filter
 			Name="Resource Files"
diff --git a/obj/NiObject.h b/obj/NiObject.h
index a5786ded873cbd5883f304210f276891f7d1e8e5..a06db6cead61eae1bf0f764398e91a99c6e96200 100644
--- a/obj/NiObject.h
+++ b/obj/NiObject.h
@@ -159,7 +159,7 @@ template <class T> Ref<const T> StaticCast (const NiObject * object) {
 #endif
 
 template <class T> Ref<T> DynamicCast( NiObject * object ) {
-	if ( object->IsDerivedType(T::TypeConst()) ) {
+	if ( object && object->IsDerivedType(T::TypeConst()) ) {
 		return (T*)object;
 	} else {
 		return NULL;
@@ -169,7 +169,7 @@ template <class T> Ref<T> DynamicCast( NiObject * object ) {
 //SWIG doesn't want two versions of the same thing
 #ifndef SWIG
 template <class T> Ref<const T> DynamicCast( const NiObject * object ) {
-	if ( object->IsDerivedType(T::TypeConst()) ) {
+	if ( object && object->IsDerivedType(T::TypeConst()) ) {
 		return (const T*)object;
 	} else {
 		return NULL;
diff --git a/obj/NiSkinData.cpp b/obj/NiSkinData.cpp
index d40cb7867254fed863fc3e17d5b23f6800056e44..c0df7502c4c9e8dd485202e2b5f503543316e6b9 100644
--- a/obj/NiSkinData.cpp
+++ b/obj/NiSkinData.cpp
@@ -120,3 +120,10 @@ NiSkinData::NiSkinData( const Ref<NiTriBasedGeom> & owner ) NI_SKIN_DATA_CONSTRU
 		res_mat.Decompose( boneList[i].translation, boneList[i].rotation, boneList[i].scale );
 	}
 }
+
+Ref<NiSkinPartition> NiSkinData::GetSkinPartition() const {
+   return skinPartition;
+}
+void NiSkinData::SetSkinPartition(Ref<NiSkinPartition> value) {
+   skinPartition = value;
+}
diff --git a/obj/NiSkinData.h b/obj/NiSkinData.h
index 101ead61a12550e28259b5784e6efc8502f2a4d8..d79201454962f723ca6567f507cadd146c1e710a 100644
--- a/obj/NiSkinData.h
+++ b/obj/NiSkinData.h
@@ -53,6 +53,9 @@ public:
 	vector<SkinWeight> GetBoneWeights( uint bone_index ) const;
    void SetBoneWeights( uint bone_index, const vector<SkinWeight> & n, Vector3 center, float radius );
 
+   Ref<NiSkinPartition> GetSkinPartition() const;
+   void SetSkinPartition(Ref<NiSkinPartition> skinPartition);
+
 protected:
 	NI_SKIN_DATA_MEMBERS
 	STANDARD_INTERNAL_METHODS
diff --git a/obj/NiSkinPartition.cpp b/obj/NiSkinPartition.cpp
index 2ab7b963ac098593fc598e16ea120a153337c0fa..84f63342219b1df4b0efb8980e9c28490dde2157 100644
--- a/obj/NiSkinPartition.cpp
+++ b/obj/NiSkinPartition.cpp
@@ -3,8 +3,23 @@ All rights reserved.  Please see niflib.h for licence. */
 
 #include "NiSkinPartition.h"
 #include "../gen/SkinPartition.h"
+#include "NiSkinInstance.h"
+#include "NiSkinData.h"
+#include "NiTriBasedGeom.h"
+#include "NiTriBasedGeomData.h"
+#include "NiTriStripsData.h"
+#include "../gen/SkinWeight.h"
+#include "../NvTriStrip/NvTriStrip.h"
 using namespace Niflib;
 
+typedef vector<SkinWeight> SkinWeightList;
+typedef vector<SkinWeightList> BoneWeightList;
+typedef vector<float> WeightList;
+typedef vector<ushort> BoneList;
+typedef vector<ushort> Strip;
+typedef vector<Strip> Strips;
+typedef vector<Triangle> Triangles;
+
 //Definition of TYPE constant
 const Type NiSkinPartition::TYPE("NiSkinPartition", &NI_SKIN_PARTITION_PARENT::TypeConst() );
 
@@ -160,9 +175,10 @@ ushort NiSkinPartition::GetStripCount( int partition ) const {
 }
 
 void NiSkinPartition::SetStripCount( int partition, int n ) {
-   skinPartitionBlocks[partition].strips.resize(n);
-   skinPartitionBlocks[partition].stripLengths.resize(n);
-   skinPartitionBlocks[partition].hasStrips = (n!=0);
+   SkinPartition& part = skinPartitionBlocks[partition];
+   part.strips.resize(n);
+   part.stripLengths.resize(n);
+   part.hasStrips = (n!=0);
 }
 
 vector<ushort> NiSkinPartition::GetStrip( int partition, int index ) const {
@@ -196,3 +212,121 @@ void NiSkinPartition::SetTriangles( int partition, const vector<Triangle> & in )
    part.numTriangles = uint(in.size()) * 3;
 }
 
+NiSkinPartition::NiSkinPartition(Ref<NiTriBasedGeom> shape) {
+   NiSkinInstanceRef skinInst = shape->GetSkinInstance();
+   if ( skinInst == NULL ) {
+      throw runtime_error( "You must bind a skin before setting generating skin partitions.  No NiSkinInstance found." );
+   }
+   NiSkinDataRef skinData = skinInst->GetSkinData();
+   if ( skinData == NULL ) {
+      throw runtime_error( "You must bind a skin before setting generating skin partitions.  No NiSkinData found." );
+   }
+   NiTriBasedGeomDataRef geomData = shape->GetData();
+   if ( geomData == NULL ) {
+      throw runtime_error( "Attempted to generate a skin partition on a mesh with no geometry data." );
+   }
+
+   int nWeightsPerVertex = 0;
+   vector<WeightList> vertexWeights;
+   BoneList boneMap;
+   vector<ushort> vertexMap;
+   Strips strips;
+   vector<BoneList> boneIndexList;
+   Triangles triangles;
+
+   int totalBones = skinInst->GetBoneCount();
+   boneMap.resize(totalBones);
+
+   int nv = geomData->GetVertexCount();
+   vertexMap.resize(nv);
+   vertexWeights.resize(nv);
+   boneIndexList.resize(nv);
+
+   for (int i=0; i<totalBones; ++i) {
+      boneMap[i] = i;
+
+      vector<SkinWeight> skinWeights = skinData->GetBoneWeights(i);
+      for (vector<SkinWeight>::const_iterator skinWeight = skinWeights.begin(); skinWeight != skinWeights.end(); ++skinWeight) {
+         WeightList& vertexWeight = vertexWeights[skinWeight->index];
+         BoneList& boneIndex = boneIndexList[skinWeight->index];
+
+         vertexWeight.push_back(skinWeight->weight);
+         boneIndex.push_back(i);
+
+         // Adjust upper limit on number of weights per vertex if necessary.
+         int nWeights = vertexWeight.size();
+         if (nWeights > nWeightsPerVertex)
+            nWeightsPerVertex = nWeights;
+      }
+   }
+   if (nWeightsPerVertex == 0) {
+      throw runtime_error( "Attempted to generate a skin partition on a mesh with no weights specified." );
+   }
+   for (int i=0; i<nv; ++i) {
+      vertexMap[i] = i;
+
+      WeightList& vertexWeight = vertexWeights[i];
+      BoneList& boneIndex = boneIndexList[i];
+      vertexWeight.reserve(nWeightsPerVertex);
+      boneIndex.reserve(nWeightsPerVertex);
+      for (int j = nWeightsPerVertex - vertexWeight.size(); j>0; --j) {
+         vertexWeight.push_back(0.0f);
+         boneIndex.push_back(0);
+      }
+   }
+
+   SetNumPartitions(1);
+   SetWeightsPerVertex(0, nWeightsPerVertex);
+   SetBoneMap(0, boneMap);
+   SetNumVertices(0, ushort(vertexMap.size()) );
+   SetVertexMap(0, vertexMap);
+   EnableVertexWeights(0, true);
+   EnableVertexBoneIndices(0, true);
+   for (int i=0; i<nv; ++i) {
+      SetVertexWeights(0, i, vertexWeights[i]);
+      SetVertexBoneIndices(0, i, boneIndexList[i]);
+   }
+
+   // Special case for pre-stripped data
+   if (NiTriStripsDataRef stripData = DynamicCast<NiTriStripsData>(geomData)) {
+      ushort nstrips = stripData->GetStripCount();
+      SetStripCount(0, nstrips);
+      for (int i=0; i<int(nstrips); ++i) {
+         SetStrip(0, i, stripData->GetStrip(i));
+      }
+   } else {
+
+      SetTriangles(0, geomData->GetTriangles());
+
+      unsigned short *data = new unsigned short[triangles.size() * 3 * 2];
+      for (size_t i=0; i< triangles.size(); i++) {
+         data[i * 3 + 0] = triangles[i][0];
+         data[i * 3 + 1] = triangles[i][1];
+         data[i * 3 + 2] = triangles[i][2];
+      }
+      PrimitiveGroup * groups = 0;
+      unsigned short numGroups = 0;
+
+      // GF 3+
+      SetCacheSize(CACHESIZE_GEFORCE3);
+      // don't generate hundreds of strips
+      SetStitchStrips(true);
+      GenerateStrips(data, triangles.size()*3, &groups, &numGroups);
+
+      delete [] data;
+
+      if (groups) {
+         SetStripCount(0, numGroups);
+         for (int g=0; g<numGroups; g++) {
+            if (groups[g].type == PT_STRIP) {
+               vector<Niflib::ushort> strip(groups[g].numIndices);
+               for (size_t s=0; s<groups[g].numIndices; s++)
+                  strip[s] = groups[g].indices[s];
+               SetStrip(0, g, strip);
+            }
+         }
+         delete [] groups;
+      }
+   }
+  
+}
\ No newline at end of file
diff --git a/obj/NiSkinPartition.h b/obj/NiSkinPartition.h
index 7966f5ada927d3777a799ffe6f62ffa7cea2210c..b9656349ac3710355a7510e0ddf8bb5af3337407 100644
--- a/obj/NiSkinPartition.h
+++ b/obj/NiSkinPartition.h
@@ -14,6 +14,7 @@ namespace Niflib {
 #include "../gen/obj_defines.h"
 
 class NiSkinPartition;
+class NiTriBasedGeom;
 typedef Ref<NiSkinPartition> NiSkinPartitionRef;
 
 /*!
@@ -39,30 +40,20 @@ public:
 	virtual const Type & GetType() const;
 
    int GetNumPartitions() const;
-   void SetNumPartitions( int value );
 
    ushort GetWeightsPerVertex( int partition ) const;
-   void SetWeightsPerVertex( int partition, ushort value );
 
    ushort GetNumVertices( int partition ) const;
-   void SetNumVertices( int partition, ushort value );
 
    vector<ushort> GetVertexMap( int partition ) const;
-   void SetVertexMap( int partition, const vector<ushort>& vertexMap );
 
    vector<ushort> GetBoneMap( int partition ) const;
-   void SetBoneMap( int partition, const vector<ushort>& boneMap );
 
    bool HasVertexWeights( int partition ) const;
-   void EnableVertexWeights( int partition, bool enable);
-
    vector<float> GetVertexWeights( int partition, int vertex ) const;
-   void SetVertexWeights( int partition, int vertex, const vector<float> & n );
 
    bool HasVertexBoneIndices( int partition ) const;
-   void EnableVertexBoneIndices( int partition, bool enable);
    vector<ushort> GetVertexBoneIndices( int partition, int vertex ) const;
-   void SetVertexBoneIndices( int partition, int vertex, const vector<ushort>& boneList );
 
    /*! Used to get the number of triangle strips that this mesh is divided into.
    * \return The number of triangle strips used by this mesh.
@@ -70,12 +61,6 @@ public:
    */
    ushort GetStripCount( int partition ) const;
 
-   /*! Used to resize the triangle strips array.  If the new size is smaller, strips at the end of the array will be deleted.
-   * \param n The new size of the triangle strips array.
-   * \sa NiSkinPartition::GetStripCount
-   */
-   void SetStripCount( int partition, int n );
-
    /*! Used to retrieve all the triangles from a specific triangle strip.
    * \param index The index of the triangle strip to retrieve the triangles from.  This is a zero-based index which must be a positive number less than that returned by NiTriStripsData::GetStripCount.
    * \return A vector containing all the triangle faces from the triangle strip specified by index.
@@ -83,6 +68,31 @@ public:
    */
    vector<ushort> GetStrip( int partition, int index ) const;
    
+   vector<Triangle> GetTriangles( int partition ) const;
+
+
+protected:
+   friend class NiTriBasedGeom;
+   NiSkinPartition(Ref<NiTriBasedGeom> shape);
+
+   void SetNumPartitions( int value );
+   void SetWeightsPerVertex( int partition, ushort value );
+   void SetNumVertices( int partition, ushort value );
+   void SetVertexMap( int partition, const vector<ushort>& vertexMap );
+   void SetBoneMap( int partition, const vector<ushort>& boneMap );
+
+   void EnableVertexWeights( int partition, bool enable);
+   void SetVertexWeights( int partition, int vertex, const vector<float> & n );
+
+   void EnableVertexBoneIndices( int partition, bool enable);
+   void SetVertexBoneIndices( int partition, int vertex, const vector<ushort>& boneList );
+
+   /*! Used to resize the triangle strips array.  If the new size is smaller, strips at the end of the array will be deleted.
+   * \param n The new size of the triangle strips array.
+   * \sa NiSkinPartition::GetStripCount
+   */
+   void SetStripCount( int partition, int n );
+
    /*! Used to set the triangle face data in a specific triangle strip.
    * \param index The index of the triangle strip to set the face data for.  This is a zero-based index which must be a positive number less than that returned by NiTriStripsData::GetStripCount.
    * \param in The vertex indices that make up this strip, in standard OpenGL triangle strip order.
@@ -90,7 +100,6 @@ public:
    */
    void SetStrip( int partition, int index, const vector<ushort> & in );
 
-   vector<Triangle> GetTriangles( int partition ) const;
    void SetTriangles( int partition, const vector<Triangle> & in );
 
 protected:
diff --git a/obj/NiTriBasedGeom.cpp b/obj/NiTriBasedGeom.cpp
index a5b75d1bc0e11a178a9cac964f23ec780d73859f..e90ec6fe309f91770fd132bf775743bd6a93166c 100644
--- a/obj/NiTriBasedGeom.cpp
+++ b/obj/NiTriBasedGeom.cpp
@@ -6,6 +6,7 @@ All rights reserved.  Please see niflib.h for licence. */
 #include "NiSkinInstance.h"
 #include "NiObject.h"
 #include "NiSkinData.h"
+#include "NiSkinPartition.h"
 using namespace Niflib;
 
 //Definition of TYPE constant
@@ -355,4 +356,19 @@ void NiTriBasedGeom::SetBoneWeights( uint bone_index, const vector<SkinWeight> &
 	center = skinData->GetBoneTransform( bone_index ) * center;
 
 	skinData->SetBoneWeights( bone_index, n, center, radius );
+}
+
+void NiTriBasedGeom::GenHardwareSkinInfo( ) {
+   NiSkinPartitionRef skinPart = new NiSkinPartition( this );
+
+   // Set the partition info in both places and it will be handled when exported.
+   NiSkinInstanceRef skinInst = GetSkinInstance();
+   if ( skinInst != NULL ) {
+      skinInst->SetSkinPartition( skinPart );
+
+      NiSkinDataRef skinData = skinInst->GetSkinData();
+      if (skinData != NULL) {
+         skinData->SetSkinPartition( skinPart );
+      }
+   }
 }
\ No newline at end of file
diff --git a/obj/NiTriBasedGeom.h b/obj/NiTriBasedGeom.h
index 7137589822df8698c991dcb664ebf72e9d340a2f..f6464b1e47cab8de169f40feadaa2d44df263c05 100644
--- a/obj/NiTriBasedGeom.h
+++ b/obj/NiTriBasedGeom.h
@@ -56,6 +56,8 @@ public:
 	 */
 	void SetBoneWeights( uint bone_index, const vector<SkinWeight> & n );
 
+   void GenHardwareSkinInfo( );
+
 	Ref<NiSkinInstance> GetSkinInstance() const;
 
 	Ref<NiTriBasedGeomData> GetData() const;
diff --git a/obj/NiTriBasedGeomData.cpp b/obj/NiTriBasedGeomData.cpp
index 63420c911decd99a96b3d676c15f961590e23c36..2983d6455f236bbc37fcb0fd70098d1ee191726c 100644
--- a/obj/NiTriBasedGeomData.cpp
+++ b/obj/NiTriBasedGeomData.cpp
@@ -133,3 +133,7 @@ Vector3 NiTriBasedGeomData::GetCenter() const {
 float NiTriBasedGeomData::GetRadius() const {
 	return radius;
 }
+
+void NiTriBasedGeomData::SetTriangles( const vector<Triangle> & in ) {
+   throw runtime_error("SetTriangles is a must be implemented in the derived class.");
+}
\ No newline at end of file
diff --git a/obj/NiTriBasedGeomData.h b/obj/NiTriBasedGeomData.h
index 686b2910a5af2e9cec77da691807aae67eed7230..9bcec05fea9b0115204365d1d219cb0741865465 100644
--- a/obj/NiTriBasedGeomData.h
+++ b/obj/NiTriBasedGeomData.h
@@ -73,10 +73,16 @@ public:
 
 	/*! Returns the triangle faces that make up this mesh.
 	 * \return A vector containing the triangle faces that make up this mesh.
-	 * \sa ITriShapeData::SetTriangles
+	 * \sa SetTriangles
 	 */
 	virtual vector<Triangle> GetTriangles() const { return vector<Triangle>(); }  //TODO:  Make this pure virtual?
 
+   /*! Replaces the triangle face data in this mesh with new data.
+   * \param in A vector containing the new face data.  Maximum size is 65,535.
+   * \sa GetTriangles
+   */
+   virtual void SetTriangles( const vector<Triangle> & in );
+
 	/*! Used to retrive the vertices used by this mesh.  The size of the vector will be the same as the vertex count retrieved with the IShapeData::GetVertexCount function.
 	 * \return A vector cntaining the vertices used by this mesh.
 	 * \sa IShapeData::SetVertices, IShapeData::GetVertexCount, IShapeData::SetVertexCount.
diff --git a/obj/NiTriShapeData.h b/obj/NiTriShapeData.h
index 2fbe11f9f53cd226c75d30ee6937eec85b92f0ba..f4b576e6cddbfbd5cee5fba78198ae15f0772754 100644
--- a/obj/NiTriShapeData.h
+++ b/obj/NiTriShapeData.h
@@ -71,7 +71,8 @@ public:
 	 * \param in A vector containing the new face data.  Maximum size is 65,535.
 	 * \sa ITriShapeData::GetTriangles
 	 */
-	void SetTriangles( const vector<Triangle> & in );
+	virtual void SetTriangles( const vector<Triangle> & in );
+
 protected:
 	NI_TRI_SHAPE_DATA_MEMBERS
 	STANDARD_INTERNAL_METHODS
diff --git a/obj/NiTriStripsData.cpp b/obj/NiTriStripsData.cpp
index 83090282e58b6f622ff53e67a567a3b04f83ceec..c56c927b064e18681edff5db4f1d89882a7410da 100644
--- a/obj/NiTriStripsData.cpp
+++ b/obj/NiTriStripsData.cpp
@@ -2,6 +2,8 @@
 All rights reserved.  Please see niflib.h for licence. */
 
 #include "NiTriStripsData.h"
+#include "../NvTriStrip/NvTriStrip.h"
+
 using namespace Niflib;
 
 //Definition of TYPE constant
@@ -118,3 +120,46 @@ ushort NiTriStripsData::CalcTriangleCount() const {
 	return numTriangles;
 }
 
+void NiTriStripsData::SetTriangles( const vector<Triangle> & in ) {
+   if ( in.size() > 65535 || in.size() < 0 ) {
+      throw runtime_error("Invalid Triangle Count: must be between 0 and 65535.");
+   }
+   points.clear();
+   numTriangles = 0;
+
+   unsigned short *data = new unsigned short[in.size() * 3 * 2];
+   for (size_t i=0; i< in.size(); i++) {
+      data[i * 3 + 0] = in[i][0];
+      data[i * 3 + 1] = in[i][1];
+      data[i * 3 + 2] = in[i][2];
+   }
+
+   PrimitiveGroup * groups = 0;
+   unsigned short numGroups = 0;
+
+   // GF 3+
+   SetCacheSize(CACHESIZE_GEFORCE3);
+   // don't generate hundreds of strips
+   SetStitchStrips(true);
+   GenerateStrips(data, in.size()*3, &groups, &numGroups);
+
+   delete [] data;
+
+   if (!groups)
+      return;
+
+   SetStripCount(numGroups);
+   for (int g=0; g<numGroups; g++) {
+      if (groups[g].type == PT_STRIP) {
+         vector<Niflib::ushort> strip(groups[g].numIndices);
+         for (size_t s=0; s<groups[g].numIndices; s++)
+            strip[s] = groups[g].indices[s];
+         SetStrip(g, strip);
+      }
+   }
+   delete [] groups;
+
+   //Recalculate Triangle Count
+   numTriangles = CalcTriangleCount();
+}
+
diff --git a/obj/NiTriStripsData.h b/obj/NiTriStripsData.h
index 76f33a93361b9fe6d50e0e152276fed97d8d3e79..5893de6f1a8359e5b134c44eec530bbe29a80cd5 100644
--- a/obj/NiTriStripsData.h
+++ b/obj/NiTriStripsData.h
@@ -70,6 +70,12 @@ public:
 	 */
 	void SetStrip( int index, const vector<ushort> & in );
 
+   /*! Replaces the triangle face data in this mesh with new data.
+   * \param in A vector containing the new face data.  Maximum size is 65,535.
+   * \sa GetTriangles
+   */
+   virtual void SetTriangles( const vector<Triangle> & in );
+
 private:
 	ushort CalcTriangleCount() const;