From 3d41349a224e2e03e6d031908b691f48bc295faa Mon Sep 17 00:00:00 2001
From: Tazpn <tazpn@users.sourceforge.net>
Date: Mon, 21 Aug 2006 01:07:48 +0000
Subject: [PATCH] Replace the Tri Stripper with a new version. The old version
 had some bugs which came when compiled with VS 2005.

---
 MaxNifPlugins_Readme.txt                     |  24 +-
 NifExport/NifExport.rc                       |  15 +-
 NifExport/NifExport_VC80.vcproj              |  12 +
 NifExport/Strips.cpp                         |  18 +-
 NifExport/TriStripper/connectivity_graph.cpp | 132 ++++
 NifExport/TriStripper/policy.cpp             |  63 ++
 NifExport/TriStripper/public_types.h         |  43 ++
 NifExport/TriStripper/tri_stripper.cpp       | 668 +++++++++----------
 NifExport/TriStripper/tri_stripper.h         | 401 +++--------
 NifExport/pch.h                              |   3 +
 NifFurniture/NifFurniture.rc                 |  73 +-
 NifImport/MaxNifImport.rc                    |  15 +-
 NifProps/NifProps.rc                         | 178 +++--
 13 files changed, 863 insertions(+), 782 deletions(-)
 create mode 100644 NifExport/TriStripper/connectivity_graph.cpp
 create mode 100644 NifExport/TriStripper/policy.cpp
 create mode 100644 NifExport/TriStripper/public_types.h

diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt
index 971f29c..9573bf8 100644
--- a/MaxNifPlugins_Readme.txt
+++ b/MaxNifPlugins_Readme.txt
@@ -1,4 +1,4 @@
-                        MaxPlugins 0.1.5
+                        MaxPlugins 0.2.1
                         ================
 
  
@@ -27,7 +27,12 @@
 
     Change log
     ----------
-    
+      0.2.1
+      -----
+    o Exporter
+      - Replace the Tri Stripper with a new version.
+        o The old version had some bugs which came when compiled with VS 2005.
+        
       0.2
       -----
     o Importer
@@ -44,11 +49,15 @@
       - Dropped Official Max6 support because I do not have Max 6 to compile it
         o Try editing the MaxNifTools.ini and setting the MaxSDKVersion to 0x17700d00
       - Fixed issue with importing glossiness setting on textures.
-      - Fixed issues with export of vertex color. Alpha map is now exported as part of the normal color map.
+      - Fixed issues with export of vertex color. Alpha map is now exported
+         as part of the normal color map.
       - No longer exports meshes associated with bone or biped nodes.
-      - No longer exports extra NiNode when exporting NiTriGeom-based objects (can be reset in ini file)
-      - Mass, Restitution(Ellasticity), and Friction now share values with Reactor(Havok)
-      - Modified UPB export to actually export the values in the UserPropBuffer not just a fixed list.
+      - No longer exports extra NiNode when exporting NiTriGeom-based objects 
+         (can be reset in ini file)
+      - Mass, Restitution(Ellasticity), and Friction now share values with 
+         Reactor(Havok)
+      - Modified UPB export to actually export the values in the UserPropBuffer 
+         not just a fixed list.
       - Added Skin Modifier export
       - Added support for more material/texture properties
       - Added support for Civilization IV Shader, if installed
@@ -78,7 +87,8 @@
       
     o Importer
      - Fixed alignment issues when importing Morrowind Armor nifs
-     - Added initial animation support (only for animations internal to nif, no kf file support yet)
+     - Added initial animation support (only for animations internal to nif, 
+        no kf file support yet)
      - Fixed numerous issues with bone system (biped is still broken)
      - Fixed issues with skin and doac nifs
       
diff --git a/NifExport/NifExport.rc b/NifExport/NifExport.rc
index efbb97a..ddfcbb2 100755
--- a/NifExport/NifExport.rc
+++ b/NifExport/NifExport.rc
@@ -138,13 +138,13 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 0,2,0,0
- PRODUCTVERSION 0,2,0,0
- FILEFLAGSMASK 0x37L
+ FILEVERSION 0,2,1,0
+ PRODUCTVERSION 0,2,1,0
+ FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
- FILEFLAGS 0x21L
+ FILEFLAGS 0x1L
 #else
- FILEFLAGS 0x20L
+ FILEFLAGS 0x0L
 #endif
  FILEOS 0x4L
  FILETYPE 0x2L
@@ -155,13 +155,12 @@ BEGIN
         BLOCK "040904b0"
         BEGIN
             VALUE "FileDescription", "3ds Max Nif Exporter"
-            VALUE "FileVersion", "0, 2, 0, 0"
+            VALUE "FileVersion", "0, 2, 1, 0"
             VALUE "InternalName", "NifExport.dle"
             VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved."
             VALUE "OriginalFilename", "NifExport.dle"
             VALUE "ProductName", "3ds Max Nif Exporter"
-            VALUE "ProductVersion", "0, 2, 0, 0"
-            VALUE "SpecialBuild", "Alpha"
+            VALUE "ProductVersion", "0, 2, 1, 0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/NifExport/NifExport_VC80.vcproj b/NifExport/NifExport_VC80.vcproj
index dda568d..b128f28 100644
--- a/NifExport/NifExport_VC80.vcproj
+++ b/NifExport/NifExport_VC80.vcproj
@@ -898,6 +898,14 @@
 			<Filter
 				Name="TriStripper"
 				>
+				<File
+					RelativePath=".\TriStripper\connectivity_graph.cpp"
+					>
+				</File>
+				<File
+					RelativePath=".\TriStripper\policy.cpp"
+					>
+				</File>
 				<File
 					RelativePath="TriStripper\tri_stripper.cpp"
 					>
@@ -1003,6 +1011,10 @@
 					RelativePath="TriStripper\heap_array.h"
 					>
 				</File>
+				<File
+					RelativePath=".\TriStripper\public_types.h"
+					>
+				</File>
 				<File
 					RelativePath="TriStripper\tri_stripper.h"
 					>
diff --git a/NifExport/Strips.cpp b/NifExport/Strips.cpp
index 8443bb7..7ca4f35 100755
--- a/NifExport/Strips.cpp
+++ b/NifExport/Strips.cpp
@@ -15,7 +15,7 @@ void Exporter::strippify(TriStrips &strips, vector<Vector3> &verts, vector<Vecto
 	
 	tri_stripper stripper(idcs);
 
-	tri_stripper::primitives_vector groups;
+	primitive_vector groups;
 	stripper.Strip(&groups);
 
 	// triangles left over
@@ -23,22 +23,22 @@ void Exporter::strippify(TriStrips &strips, vector<Vector3> &verts, vector<Vecto
 
 	for (i=0; i<groups.size(); i++)
 	{
-		if (groups[i].m_Type == tri_stripper::PT_Triangle_Strip)
+		if (groups[i].Type == TRIANGLE_STRIP)
 		{			
-			strips.push_back(TriStrip(groups[i].m_Indices.size()));
+			strips.push_back(TriStrip(groups[i].Indices.size()));
 			TriStrip &strip = strips.back();
 
-			for (int j=0; j<groups[i].m_Indices.size(); j++)
-				strip[j] = groups[i].m_Indices[j];
+			for (int j=0; j<groups[i].Indices.size(); j++)
+				strip[j] = groups[i].Indices[j];
 		} else
 		{
 			int size = stris.size();
-			stris.resize(size + groups[i].m_Indices.size()/3);
+			stris.resize(size + groups[i].Indices.size()/3);
 			for (int j=(size>0)?(size-1):0; j<stris.size(); j++)
 			{
-				stris[j][0] = groups[i].m_Indices[j*3+0];
-				stris[j][1] = groups[i].m_Indices[j*3+1];
-				stris[j][2] = groups[i].m_Indices[j*3+2];
+				stris[j][0] = groups[i].Indices[j*3+0];
+				stris[j][1] = groups[i].Indices[j*3+1];
+				stris[j][2] = groups[i].Indices[j*3+2];
 			}
 		}
 	}
diff --git a/NifExport/TriStripper/connectivity_graph.cpp b/NifExport/TriStripper/connectivity_graph.cpp
new file mode 100644
index 0000000..9d0b389
--- /dev/null
+++ b/NifExport/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/NifExport/TriStripper/policy.cpp b/NifExport/TriStripper/policy.cpp
new file mode 100644
index 0000000..cd62563
--- /dev/null
+++ b/NifExport/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/NifExport/TriStripper/public_types.h b/NifExport/TriStripper/public_types.h
new file mode 100644
index 0000000..5fc7cd1
--- /dev/null
+++ b/NifExport/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/NifExport/TriStripper/tri_stripper.cpp b/NifExport/TriStripper/tri_stripper.cpp
index 3c1d44d..feb17ac 100755
--- a/NifExport/TriStripper/tri_stripper.cpp
+++ b/NifExport/TriStripper/tri_stripper.cpp
@@ -1,450 +1,379 @@
-// tri_stripper.cpp: implementation of the Tri Stripper class.
 //
-// Copyright (C) 2002 Tanguy Fautré.
+// Copyright (C) 2004 Tanguy Fautré.
 // For conditions of distribution and use,
 // see copyright notice in tri_stripper.h
 //
 //////////////////////////////////////////////////////////////////////
-
-
-#include <assert.h>
-#include <deque>
-#include <algorithm>
-#include <fstream>
-#include <iostream>
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
+// 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
-namespace triangle_stripper {
-
 
 
+namespace triangle_stripper {
 
-//////////////////////////////////////////////////////////////////////
-// Construction/Destruction
-//////////////////////////////////////////////////////////////////////
+	using namespace detail;
 
 
 
-//////////////////////////////////////////////////////////////////////
-// Members Functions
-//////////////////////////////////////////////////////////////////////
 
-void tri_stripper::Strip(primitives_vector * out_pPrimitivesVector)
+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)
 {
-	// verify that the number of indices is correct
-	if (m_TriIndices.size() % 3 != 0)
-		throw triangles_indices_error();
-
-	// clear possible garbage
-	m_PrimitivesVector.clear();
-	out_pPrimitivesVector->clear();
-
-	// Initialize the triangle graph
-	InitTriGraph();
-
-	// Initialize the triangle priority queue
-	InitTriHeap();
-
-	// Reset the cache simulator
-	InitCache();
-
-	// Launch the triangle strip generator
-	Stripify();
-
-	// Add the triangles that couldn't be stripped
-	AddLeftTriangles();
+	SetCacheSize();
+	SetMinStripSize();
+	SetBackwardSearch();
+	SetPushCacheHits();
 
-	// Free ressources
-	m_Triangles.clear();
-	
-	// Put the results into the user's vector
-	std::swap(m_PrimitivesVector, (* out_pPrimitivesVector));
+	make_connectivity_graph(m_Triangles, TriIndices);
 }
 
 
 
-void tri_stripper::InitTriGraph()
+void tri_stripper::Strip(primitive_vector * out_pPrimitivesVector)
 {
-	// Set up the graph size and complete the triangles data
-	// note: setsize() completely resets the graph as well as the node markers
-	m_Triangles.setsize(m_TriIndices.size() / 3);
-
-	for (size_t i = 0; i < m_Triangles.size(); ++i)
-		m_Triangles[i] = triangle(m_TriIndices[i * 3 + 0], m_TriIndices[i * 3 + 1], m_TriIndices[i * 3 + 2]);
-
-	// Build the edges lookup table
-	triangle_edges TriInterface;
-	TriInterface.reserve(m_Triangles.size() * 3);
+	assert(out_pPrimitivesVector);
 
-	for (size_t i = 0; i < m_Triangles.size(); ++i) {
-		TriInterface.push_back(triangle_edge(m_Triangles[i]->A(), m_Triangles[i]->B(), i)); 
-		TriInterface.push_back(triangle_edge(m_Triangles[i]->B(), m_Triangles[i]->C(), i)); 
-		TriInterface.push_back(triangle_edge(m_Triangles[i]->C(), m_Triangles[i]->A(), i)); 
-	}
-
-	// Sort the lookup table for faster searches
-	std::sort(TriInterface.begin(), TriInterface.end(), _cmp_tri_interface_lt());
-
-	// Link neighbour triangles together using the edges lookup table
-	for (size_t i = 0; i < m_Triangles.size(); ++i) {
+	if (! m_FirstRun) {
+		unmark_nodes(m_Triangles);
+		ResetStripIDs();
+		m_Cache.reset();
+		m_TriHeap.clear();
+		m_Candidates.clear();
+		m_StripID = 0;
 
-		const triangle_edge EdgeBA(m_Triangles[i]->B(), m_Triangles[i]->A(), i);
-		const triangle_edge EdgeCB(m_Triangles[i]->C(), m_Triangles[i]->B(), i);
-		const triangle_edge EdgeAC(m_Triangles[i]->A(), m_Triangles[i]->C(), i);
-
-		LinkNeighboursTri(TriInterface, EdgeBA);
-		LinkNeighboursTri(TriInterface, EdgeCB);
-		LinkNeighboursTri(TriInterface, EdgeAC);
+		m_FirstRun = false;
 	}
-}
 
+	out_pPrimitivesVector->clear();
 
+	InitTriHeap();
 
-void tri_stripper::LinkNeighboursTri(const triangle_edges & TriInterface, const triangle_edge Edge)
-{
-	typedef triangle_edges::const_iterator edge_const_it;
-
-	// Find the first edge equal to Edge
-	edge_const_it It = std::lower_bound(TriInterface.begin(), TriInterface.end(), Edge, _cmp_tri_interface_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 != TriInterface.end()) && ((It->A() == Edge.A()) && (It->B() == Edge.B())); ++It)
-		m_Triangles.insert(Edge.TriPos(), It->TriPos());
-
-	// Note: degenerated triangles will also point themselves as neighbour triangles
+	Stripify();
+	AddLeftTriangles();
+	
+	std::swap(m_PrimitivesVector, (* out_pPrimitivesVector));
 }
 
 
 
 void tri_stripper::InitTriHeap()
 {
-	m_TriHeap.clear();
 	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(triangle_degree(i, m_Triangles[i].number_of_out_arcs()));
+		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().Degree() == 0))
+	// 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::InitCache()
+void tri_stripper::ResetStripIDs()
 {
-	m_Cache.reset();
+	for (triangle_graph::node_iterator it = m_Triangles.begin(); it != m_Triangles.end(); ++it)
+		(**it).ResetStripID();
 }
 
 
 
 void tri_stripper::Stripify()
 {
-	// Reset the triangle strip id selector
-	m_StripID = 0;
-
-	// Reset the candidate list
-	m_NextCandidates.clear();
-
-	// Loop untill there is no available candidate triangle left
 	while (! m_TriHeap.empty()) {
 
 		// There is no triangle in the candidates list, refill it with the loneliest triangle
-		const size_t HeapTop = m_TriHeap.top().TriPos();
-		m_NextCandidates.push_back(HeapTop);
+		const size_t HeapTop = m_TriHeap.position(0);
+		m_Candidates.push_back(HeapTop);
 
-		// Loop while BuildStrip can find good candidates for us
-		while (! m_NextCandidates.empty()) {
+		while (! m_Candidates.empty()) {
 
-			// Choose the best strip containing that triangle
-			// Note: FindBestStrip empties m_NextCandidates
-			const triangle_strip TriStrip = FindBestStrip();
+			// Note: FindBestStrip empties the candidate list, while BuildStrip refills it
+			const strip TriStrip = FindBestStrip();
 
-			// Build it if it's long enough, otherwise discard it
-			// Note: BuildStrip refills m_NextCandidates
 			if (TriStrip.Size() >= m_MinStripSize)
 				BuildStrip(TriStrip);
 		}
 
-		// We must discard the triangle we inserted in the candidate list from the heap
-		// if it led to nothing. (We simply removed it if it hasn't been removed by BuildStrip() yet)
 		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().Degree() == 0))
+		while ((! m_TriHeap.empty()) && (m_TriHeap.top() == 0))
 			m_TriHeap.pop();
 	}
 }
 
 
 
-inline tri_stripper::triangle_strip tri_stripper::FindBestStrip()
+inline strip tri_stripper::FindBestStrip()
 {
-	triangle_strip BestStrip;
-	size_t BestStripDegree = 0;
-	size_t BestStripCacheHits = 0;
-
-	// Backup the cache, because it'll be erased during the simulations
+	// Allow to restore the cache (modified by ExtendTriToStrip) and implicitly reset the cache hit count
 	const cache_simulator CacheBackup = m_Cache;
 
-	while (! m_NextCandidates.empty()) {
+	policy Policy(m_MinStripSize, Cache());
 
-		// Discard useless triangles from the candidates list
-		if ((m_Triangles[m_NextCandidates.back()].marked()) || (m_TriHeap[m_NextCandidates.back()].Degree() == 0)) {
-			m_NextCandidates.pop_back();
+	while (! m_Candidates.empty()) {
 
-			// "continue" is evil! But it really makes things easier here.
-			// The useless triangle is discarded, and the "while" just rebegins again
-			continue;
-		}
+		const size_t Candidate = m_Candidates.back();
+		m_Candidates.pop_back();
 
-		// Invariant: (CandidateTri's Degree() >= 1) && (CandidateTri is not marked).
-		// So it can directly be used.
-		const size_t CandidateTri = m_NextCandidates.back();
-		m_NextCandidates.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 directions
+		// Try to extend the triangle in the 3 possible forward directions
 		for (size_t i = 0; i < 3; ++i) {
 
-			// Try a new strip with that triangle in a particular direction
-			const triangle_strip TempStrip = ExtendTriToStrip(CandidateTri, triangle_strip::start_order(i));
-
-			// We want to keep the best strip
-			// Discard strips that don't match the minimum required size
-			if (TempStrip.Size() >= m_MinStripSize) {
-
-				// Cache simulator disabled?
-				if (m_Cache.size() == 0) {
-
-					// Cache is disabled, take the longest strip
-					if (TempStrip.Size() > BestStrip.Size())
-						BestStrip = TempStrip;
+			const strip Strip = ExtendToStrip(Candidate, triangle_order(i));
+			Policy.Challenge(Strip, m_TriHeap[Strip.Start()], m_Cache.hitcount());
+			
+			m_Cache = CacheBackup;
+		}
 
-				// Cache simulator enabled
-				// Use other criteria to find the "best" strip
-				} else {
+		// Try to extend the triangle in the 6 possible backward directions
+		if (m_BackwardSearch) {
 
-					// Priority 1: Keep the strip with the best cache hit count
-					if (m_Cache.HitCount() > BestStripCacheHits) {
-						BestStrip = TempStrip;
-						BestStripDegree = m_TriHeap[TempStrip.StartTriPos()].Degree();
-						BestStripCacheHits = m_Cache.HitCount();
+			for (size_t i = 0; i < 3; ++i) {
 
-					} else if (m_Cache.HitCount() == BestStripCacheHits) {
+				const strip Strip = BackExtendToStrip(Candidate, triangle_order(i), false);
+				Policy.Challenge(Strip, m_TriHeap[Strip.Start()], m_Cache.hitcount());
+			
+				m_Cache = CacheBackup;
+			}
 
-						// Priority 2: Keep the strip with the loneliest start triangle
-						if ((BestStrip.Size() != 0) && (m_TriHeap[TempStrip.StartTriPos()].Degree() < BestStripDegree)) {
-							BestStrip = TempStrip;
-							BestStripDegree = m_TriHeap[TempStrip.StartTriPos()].Degree();
+			for (size_t i = 0; i < 3; ++i) {
 
-						// Priority 3: Keep the longest strip 
-						} else if (TempStrip.Size() > BestStrip.Size()) {
-							BestStrip = TempStrip;
-							BestStripDegree = m_TriHeap[TempStrip.StartTriPos()].Degree();
-						}
-					}
-				}
+				const strip Strip = BackExtendToStrip(Candidate, triangle_order(i), true);
+				Policy.Challenge(Strip, m_TriHeap[Strip.Start()], m_Cache.hitcount());
+			
+				m_Cache = CacheBackup;
 			}
-
-			// Restore the cache (modified by ExtendTriToStrip) and implicitly reset the cache hit count
-			m_Cache = CacheBackup;
 		}
 
 	}
 
-	return BestStrip;
+	return Policy.BestStrip();
 }
 
 
 
-tri_stripper::triangle_strip tri_stripper::ExtendTriToStrip(const size_t StartTriPos, const triangle_strip::start_order StartOrder)
+strip tri_stripper::ExtendToStrip(const size_t Start, triangle_order Order)
 {
-	typedef triangles_graph::const_out_arc_iterator const_tri_link_iter;
-	typedef triangles_graph::node_iterator tri_node_iter;
+	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;
-	triangle_strip::start_order Order = StartOrder;
 
-	// Begin a new strip
-	++m_StripID;
+	// 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) {
 
-	// Mark the first triangle as used for this strip
-	m_Triangles[StartTriPos]->SetStripID(m_StripID);
+		const const_link_iterator Link = LinkToNeighbour(Node, ClockWise, Order, false);
 
-	// Update the cache
-	AddTriToCache((* m_Triangles[StartTriPos]), Order);
+		// Is it the end of the strip?
+		if (Link == Node->out_end()) {
 
+			Node = m_Triangles.end();
+			--Size;
 
-	// Loop while we can further extend the strip
-	for (tri_node_iter TriNodeIt = (m_Triangles.begin() + StartTriPos); 
-		(TriNodeIt != m_Triangles.end()) && ((m_Cache.size() == 0) || ((Size + 2) < m_Cache.size()));
-		++Size) {
+		} else {
 
-		// Get the triangle edge that would lead to the next triangle
-		const triangle_edge Edge = GetLatestEdge(** TriNodeIt, Order);
+			Node = Link->terminal();
+			(* Node)->SetStripID(m_StripID);
+			ClockWise = ! ClockWise;
 
-		// Link to a neighbour triangle
-		const_tri_link_iter LinkIt;
-		for (LinkIt = TriNodeIt->out_begin(); LinkIt != TriNodeIt->out_end(); ++LinkIt) {
+		}
+	}
 
-			// Get the reference to the possible next triangle
-			const triangle & Tri = (** (LinkIt->terminal()));
+	return strip(Start, StartOrder, Size);
+}
 
-			// Check whether it's already been used
-			if ((Tri.StripID() != m_StripID) && (! (LinkIt->terminal()->marked()))) {
 
-				// Does the current candidate triangle match the required for the strip?
 
-				if ((Edge.B() == Tri.A()) && (Edge.A() == Tri.B())) {
-					Order = (ClockWise) ? triangle_strip::ABC : triangle_strip::BCA;
-					AddIndexToCache(Tri.C(), true);
-					break;
-				}
+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;
 
-				else if ((Edge.B() == Tri.B()) && (Edge.A() == Tri.C())) {
-					Order = (ClockWise) ? triangle_strip::BCA : triangle_strip::CAB;
-					AddIndexToCache(Tri.A(), true);
-					break;
-				}
+	tri_iterator Node;
 
-				else if ((Edge.B() == Tri.C()) && (Edge.A() == Tri.A())) {
-					Order = (ClockWise) ? triangle_strip::CAB : triangle_strip::ABC;
-					AddIndexToCache(Tri.B(), true);
-					break;
-				}
-			}
-		}
+	// 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 (LinkIt == TriNodeIt->out_end()) {
-			TriNodeIt = m_Triangles.end();
-			--Size;
-		} else {
-			TriNodeIt = LinkIt->terminal();
-    
-			// Setup for the next triangle
-			(* TriNodeIt)->SetStripID(m_StripID);
+		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 triangle_strip(StartTriPos, StartOrder, Size);
+	return strip(Node - m_Triangles.begin(), Order, Size);
 }
 
 
 
-inline tri_stripper::triangle_edge tri_stripper::GetLatestEdge(const triangle & Triangle, const triangle_strip::start_order Order) const
+void tri_stripper::BuildStrip(const strip Strip)
 {
-	switch (Order) {
-	case triangle_strip::ABC:
-		return triangle_edge(Triangle.B(), Triangle.C(), 0);
-	case triangle_strip::BCA:
-		return triangle_edge(Triangle.C(), Triangle.A(), 0);
-	case triangle_strip::CAB:
-		return triangle_edge(Triangle.A(), Triangle.B(), 0);
-	default:
-		return triangle_edge(0, 0, 0);
-	}
-}
+	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);
 
-void tri_stripper::BuildStrip(const triangle_strip TriStrip)
-{
-	typedef triangles_graph::const_out_arc_iterator const_tri_link_iter;
-	typedef triangles_graph::node_iterator tri_node_iter;
+	// Loop while we can further extend the strip
+	tri_iterator Node = (m_Triangles.begin() + Start);
 
-	const size_t StartTriPos = TriStrip.StartTriPos();
+	for (size_t Size = 1; Size < Strip.Size(); ++Size) {
 
-	bool ClockWise = false;
-	triangle_strip::start_order Order = TriStrip.StartOrder();
+		const const_link_iterator Link = LinkToNeighbour(Node, ClockWise, Order, true);
 
-	// Create a new strip
-	m_PrimitivesVector.push_back(primitives());
-	m_PrimitivesVector.back().m_Type = PT_Triangle_Strip;
+		assert(Link != Node->out_end());
 
-	// Put the first triangle into the strip
-	AddTriToIndices((* m_Triangles[StartTriPos]), Order);
+		// Go to the next triangle
+		Node = Link->terminal();
+		MarkTriAsTaken(Node - m_Triangles.begin());
+		ClockWise = ! ClockWise;
+	}
+}
 
-	// Mark the first triangle as used
-	MarkTriAsTaken(StartTriPos);
 
 
-	// Loop while we can further extend the strip
-	tri_node_iter TriNodeIt = (m_Triangles.begin() + StartTriPos);
+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 (size_t Size = 1; Size < TriStrip.Size(); ++Size) {
+	for (const_link_iterator Link = Node->out_begin(); Link != Node->out_end(); ++Link) {
 
-		// Get the triangle edge that would lead to the next triangle
-		const triangle_edge Edge = GetLatestEdge(** TriNodeIt, Order);
+		// Get the reference to the possible next triangle
+		const triangle & Tri = ** Link->terminal();
 
-		// Link to a neighbour triangle
-		const_tri_link_iter LinkIt;
-		for (LinkIt = TriNodeIt->out_begin(); LinkIt != TriNodeIt->out_end(); ++LinkIt) {
+		// Check whether it's already been used
+		if (NotSimulation || (Tri.StripID() != m_StripID)) {
 
-			// Get the reference to the possible next triangle
-			const triangle & Tri = (** (LinkIt->terminal()));
+			if (! Link->terminal()->marked()) {
 
-			// Check whether it's already been used
-			if (! (LinkIt->terminal()->marked())) {
+				// Does the current candidate triangle match the required position for the strip?
 
-				// Does the current candidate triangle match the required for the strip?
-				// If it does, then add it to the Indices
 				if ((Edge.B() == Tri.A()) && (Edge.A() == Tri.B())) {
-					Order = (ClockWise) ? triangle_strip::ABC : triangle_strip::BCA;
-					AddIndex(Tri.C());
-					break;
+					Order = (ClockWise) ? ABC : BCA;
+					AddIndex(Tri.C(), NotSimulation);
+					return Link;
 				}
 
 				else if ((Edge.B() == Tri.B()) && (Edge.A() == Tri.C())) {
-					Order = (ClockWise) ? triangle_strip::BCA : triangle_strip::CAB;
-					AddIndex(Tri.A());
-					break;
+					Order = (ClockWise) ? BCA : CAB;
+					AddIndex(Tri.A(), NotSimulation);
+					return Link;
 				}
 
 				else if ((Edge.B() == Tri.C()) && (Edge.A() == Tri.A())) {
-					Order = (ClockWise) ? triangle_strip::CAB : triangle_strip::ABC;
-					AddIndex(Tri.B());
-					break;
+					Order = (ClockWise) ? CAB : ABC;
+					AddIndex(Tri.B(), NotSimulation);
+					return Link;
 				}
 			}
 		}
 
-		// Debug check: we must have found the next triangle
-		assert(LinkIt != TriNodeIt->out_end());
+	}
+
+	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;
+			}
+		}
 
-		// Go to the next triangle
-		TriNodeIt = LinkIt->terminal();
-		MarkTriAsTaken(TriNodeIt - m_Triangles.begin());
-        
-		// Setup for the next triangle
-		ClockWise = ! ClockWise;
 	}
+
+	return Node->out_end();
 }
 
 
 
 void tri_stripper::MarkTriAsTaken(const size_t i)
 {
-	typedef triangles_graph::node_iterator tri_node_iter;
-	typedef triangles_graph::out_arc_iterator tri_link_iter;
+	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();
@@ -454,88 +383,132 @@ void tri_stripper::MarkTriAsTaken(const size_t i)
 		m_TriHeap.erase(i);
 
 	// Adjust the degree of available neighbour triangles
-	for (tri_link_iter LinkIt = m_Triangles[i].out_begin(); LinkIt != m_Triangles[i].out_end(); ++LinkIt) {
+	for (tri_link_iter Link = m_Triangles[i].out_begin(); Link != m_Triangles[i].out_end(); ++Link) {
 
-		const size_t j = LinkIt->terminal() - m_Triangles.begin();
+		const size_t j = Link->terminal() - m_Triangles.begin();
 
 		if ((! m_Triangles[j].marked()) && (! m_TriHeap.removed(j))) {
-			triangle_degree NewDegree = m_TriHeap.peek(j);
-			NewDegree.SetDegree(NewDegree.Degree() - 1);
+			size_t NewDegree = m_TriHeap.peek(j);
+			NewDegree = NewDegree - 1;
 			m_TriHeap.update(j, NewDegree);
 
 			// Update the candidate list if cache is enabled
-			if ((m_Cache.size() > 0) && (NewDegree.Degree() > 0))
-				m_NextCandidates.push_back(j);
+			if (Cache() && (NewDegree > 0))
+				m_Candidates.push_back(j);
 		}
 	}
 }
 
 
 
-inline void tri_stripper::AddIndexToCache(const index i, bool CacheHitCount)
+inline triangle_edge tri_stripper::FirstEdge(const triangle & Triangle, const triangle_order Order)
 {
-	// Cache simulator enabled?
-	if (m_Cache.size() > 0)
-		m_Cache.push(i, CacheHitCount);
+	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)
+inline void tri_stripper::AddIndex(const index i, const bool NotSimulation)
 {
-	// Add the index to the current indices array
-	m_PrimitivesVector.back().m_Indices.push_back(i);
+	if (Cache())
+		m_Cache.push(i, ! NotSimulation);
+
+	if (NotSimulation)
+		m_PrimitivesVector.back().Indices.push_back(i);
+}
+
+
 
-	// Run cache simulator
-	AddIndexToCache(i);
+inline void tri_stripper::BackAddIndex(const index i)
+{
+	if (Cache())
+		m_BackCache.push(i, true);
 }
 
 
 
-inline void tri_stripper::AddTriToCache(const triangle & Tri, const triangle_strip::start_order Order)
+inline void tri_stripper::AddTriangle(const triangle & Tri, const triangle_order Order, const bool NotSimulation)
 {
-	// Add Tri indices in the right order into the indices cache simulator.
-	// And enable the cache hit count
-	switch (Order) {
-	case triangle_strip::ABC:
-		AddIndexToCache(Tri.A(), true);
-		AddIndexToCache(Tri.B(), true);
-		AddIndexToCache(Tri.C(), true);
-		return;
-	case triangle_strip::BCA:
-		AddIndexToCache(Tri.B(), true);
-		AddIndexToCache(Tri.C(), true);
-		AddIndexToCache(Tri.A(), true);
-		return;
-	case triangle_strip::CAB:
-		AddIndexToCache(Tri.C(), true);
-		AddIndexToCache(Tri.A(), true);
-		AddIndexToCache(Tri.B(), true);
-		return;
+	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::AddTriToIndices(const triangle & Tri, const triangle_strip::start_order Order)
+inline void tri_stripper::BackAddTriangle(const triangle & Tri, const triangle_order Order)
 {
-	// Add Tri indices in the right order into the latest Indices vector.
-	switch (Order) {
-	case triangle_strip::ABC:
-		AddIndex(Tri.A());
-		AddIndex(Tri.B());
-		AddIndex(Tri.C());
-		return;
-	case triangle_strip::BCA:
-		AddIndex(Tri.B());
-		AddIndex(Tri.C());
-		AddIndex(Tri.A());
-		return;
-	case triangle_strip::CAB:
-		AddIndex(Tri.C());
-		AddIndex(Tri.A());
-		AddIndex(Tri.B());
-		return;
+	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;
 	}
 }
 
@@ -543,12 +516,11 @@ inline void tri_stripper::AddTriToIndices(const triangle & Tri, const triangle_s
 
 void tri_stripper::AddLeftTriangles()
 {
-	// Create the latest indices array
-	// and fill it with all the triangles that couldn't be stripped
-	primitives Primitives;
-	Primitives.m_Type = PT_Triangles;
+	// 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().m_Indices;
+	indices & Indices = m_PrimitivesVector.back().Indices;
 
 	for (size_t i = 0; i < m_Triangles.size(); ++i)
 		if (! m_Triangles[i].marked()) {
@@ -564,5 +536,19 @@ void tri_stripper::AddLeftTriangles()
 
 
 
+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/NifExport/TriStripper/tri_stripper.h b/NifExport/TriStripper/tri_stripper.h
index 45e0f7a..feacf5a 100755
--- a/NifExport/TriStripper/tri_stripper.h
+++ b/NifExport/TriStripper/tri_stripper.h
@@ -1,8 +1,7 @@
-// tri_stripper.h: interface for the tri_stripper class.
-//
+
 //////////////////////////////////////////////////////////////////////
 //
-//  Copyright (C) 2002 Tanguy Fautré.
+//  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
@@ -21,60 +20,48 @@
 //  3. This notice may not be removed or altered from any source distribution.
 //
 //  Tanguy Fautré
-//  softdev@pandora.be
+//  softdev@telenet.be
 //
 //////////////////////////////////////////////////////////////////////
 //
 //							Tri Stripper
 //							************
 //
-// Current version: 1.00 Final (23/04/2003)
-//
-// Comment: Triangle stripper in O(n.log(n)).
-//          
-//          Currently there are no protection against crazy values
-//          given via SetMinStripSize() and SetCacheSize().
-//          So be careful. (Min. strip size should be equal or greater
-//          than 2, cache size should be about 10 for GeForce 256/2
-//          and about 10-16 for GeForce 3/4.) 
+// Post TnL cache aware triangle stripifier in O(n.log(n)).
 //          
-// History: - 1.00 FINAL  (23/04/2003) - Separated cache simulator into another class
-//                                     - Fixed English: "indice" -> "index"
-//                                     - Fixed one or two points for better compatibility
-//                                       with newer compilers (VC++ .NET 2003)
-//          - 1.00 BETA 5 (10/12/2002) - Fixed a bug in Stripify() that could sometimes
-//                                       cause it to go into an infinite loop.
-//                                       (thanks to Remy for the bug report)
-//          - 1.00 BETA 4 (18/11/2002) - Removed the dependency on OpenGL:
-//                                       modified gl_primitives to primitives,
-//                                       and gl_primitives_vector to primitives_vector;
-//                                       and added primitive_type.
-//                                       (thanks to Patrik for noticing this useless dependency)
-//          - 1.00 BETA 3 (18/11/2002) - Fixed a bug in LinkNeightboursTri() that could cause a crash
-//                                       (thanks to Nicolas for finding it)
-//          - 1.00 BETA 2 (16/11/2002) - Improved portability
-//          - 1.00 BETA 1 (27/10/2002) - First public release
+// History: see ChangeLog
 //
 //////////////////////////////////////////////////////////////////////
+// SVN: $Id: tri_stripper.h 86 2005-06-08 17:47:27Z gpsnoopy $
+//////////////////////////////////////////////////////////////////////
 
-#pragma once
-
+// 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
 
 
-#include "cache_simulator.h"
 
+#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
-namespace triangle_stripper {
 
 
 
+namespace triangle_stripper {
 
-#include "graph_array.h"
-#include "heap_array.h"
 
 
 
@@ -82,304 +69,122 @@ class tri_stripper
 {
 public:
 
-	// New Public types
-	typedef unsigned int index;
-	typedef std::vector<index> indices;
-
-	enum primitive_type {
-		PT_Triangles		= 0x0004,	// = GL_TRIANGLES
-		PT_Triangle_Strip	= 0x0005	// = GL_TRIANGLE_STRIP
-	};
-
-	struct primitives
-	{
-		indices			m_Indices;
-		primitive_type	m_Type;
-	};
-
-	typedef std::vector<primitives> primitives_vector;
-
-	struct triangles_indices_error { };
+	tri_stripper(const indices & TriIndices);
 
+	void Strip(primitive_vector * out_pPrimitivesVector);
 
-	// constructor/initializer
-	tri_stripper(const indices & TriIndices);
+	/* 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);
 	
-	// Settings functions
-	void SetCacheSize(const size_t CacheSize = 16);			// = 0 will disable the cache optimizer
-	void SetMinStripSize(const size_t MinStripSize = 2);
+	// 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);
 
-	// Stripper
-	void Strip(primitives_vector * out_pPrimitivesVector);	// throw triangles_indices_error();
+	/* End Settings */
 
 private:
 
-	friend struct _cmp_tri_interface_lt;
-
-
-	class triangle
-	{
-	public:
-		triangle();
-		triangle(const index A, const index B, const index C);
-
-		void SetStripID(const size_t StripID);
-
-		index A() const;
-		index B() const;
-		index C() const;
-		size_t StripID() const;
-
-	private:
-		index m_A;
-		index m_B;
-		index m_C;
-		size_t m_StripID;
-	};
-
-
-	class triangle_edge
-	{
-	public:
-		triangle_edge(const index A, const index B, const size_t TriPos);
-
-		index A() const;
-		index B() const;
-		size_t TriPos() const;
-
-	private:
-		index m_A;
-		index m_B;
-		size_t m_TriPos;
-	};
-
-
-	class triangle_degree
-	{
-	public:
-		triangle_degree();
-		triangle_degree(const size_t TriPos, const size_t Degree);
-
-		size_t Degree() const;
-		size_t TriPos() const;
-
-		void SetDegree(const size_t Degree);
-
-	private:
-		size_t m_TriPos;
-		size_t m_Degree;
-	};
+	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;
 
-
-	class triangle_strip
-	{
-	public:
-		enum start_order { ABC = 0, BCA = 1, CAB = 2 };
-
-		triangle_strip();
-		triangle_strip(size_t StartTriPos, start_order StartOrder, size_t Size);
-
-		size_t StartTriPos() const;
-		start_order StartOrder() const;
-		size_t Size() const;
-
-	private:
-		size_t		m_StartTriPos;
-		start_order	m_StartOrder;
-		size_t		m_Size;
-	};
-
-
-	struct _cmp_tri_interface_lt
-	{
-		bool operator() (const triangle_edge & a, const triangle_edge & b) const;
-	};
-
-
-	struct _cmp_tri_degree_gt
-	{
-		bool operator () (const triangle_degree & a, const triangle_degree & b) const;
-	};
-
-
-	typedef common_structures::graph_array<triangle, char> triangles_graph;
-	typedef common_structures::heap_array<triangle_degree, _cmp_tri_degree_gt> triangles_heap;
-	typedef std::vector<triangle_edge> triangle_edges;
-	typedef std::vector<size_t> triangle_indices;
-	typedef std::deque<index> indices_cache; 
-
-
-	void InitCache();
-	void InitTriGraph();
 	void InitTriHeap();
 	void Stripify();
 	void AddLeftTriangles();
-
-	void LinkNeighboursTri(const triangle_edges & TriInterface, const triangle_edge Edge);
-	void MarkTriAsTaken(const size_t i);
-
-	triangle_edge GetLatestEdge(const triangle & Triangle, const triangle_strip::start_order Order) const;
-
-	triangle_strip FindBestStrip();
-	triangle_strip ExtendTriToStrip(const size_t StartTriPos, const triangle_strip::start_order StartOrder);
-	void BuildStrip(const triangle_strip TriStrip);
-	void AddIndex(const index i);
-	void AddIndexToCache(const index i, bool CacheHitCount = false);
-	void AddTriToCache(const triangle & Tri, const triangle_strip::start_order Order);
-	void AddTriToIndices(const triangle & Tri, const triangle_strip::start_order Order);
-
-	const indices &		m_TriIndices;
-
-	size_t				m_MinStripSize;
-//	size_t				m_CacheSize;
-
-	primitives_vector	m_PrimitivesVector;
-	triangles_graph		m_Triangles;
-	triangles_heap		m_TriHeap;
-	triangle_indices	m_NextCandidates;
-	cache_simulator		m_Cache;
-//	indices_cache		m_IndicesCache;
-	size_t				m_StripID;
-//	size_t				m_CacheHits;
+	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
+// tri_stripper inline functions
 //////////////////////////////////////////////////////////////////////////
 
-inline tri_stripper::tri_stripper(const indices & TriIndices) : m_TriIndices(TriIndices) {
-	SetCacheSize();
-	SetMinStripSize();
-}
-
-
-inline void tri_stripper::SetCacheSize(const size_t CacheSize) {
-//	m_CacheSize = CacheSize;
+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) {
-	m_MinStripSize = MinStripSize;
-}
-
-
-inline tri_stripper::triangle::triangle() { }
-
-
-inline tri_stripper::triangle::triangle(const index A, const index B, const index C) : m_A(A), m_B(B), m_C(C), m_StripID(0) { }
-
-
-inline void tri_stripper::triangle::SetStripID(const size_t StripID) {
-	m_StripID = StripID;
-}
-
-
-inline tri_stripper::index tri_stripper::triangle::A() const {
-	return m_A;
-}
-
-
-inline tri_stripper::index tri_stripper::triangle::B() const {
-	return m_B;
-}
-
-
-inline tri_stripper::index tri_stripper::triangle::C() const {
-	return m_C;
-}
-
-
-inline size_t tri_stripper::triangle::StripID() const {
-	return m_StripID;
-}
-
-
-inline tri_stripper::triangle_edge::triangle_edge(const index A, const index B, const size_t TriPos) : m_A(A), m_B(B), m_TriPos(TriPos) { }
-
-
-inline tri_stripper::index tri_stripper::triangle_edge::A() const {
-	return m_A;
-}
-
-
-inline tri_stripper::index tri_stripper::triangle_edge::B() const {
-	return m_B;
-}
-
-
-inline size_t tri_stripper::triangle_edge::TriPos() const {
-	return m_TriPos;
-}
-
-
-inline tri_stripper::triangle_degree::triangle_degree() { }
-
-
-inline tri_stripper::triangle_degree::triangle_degree(const size_t TriPos, const size_t Degree) : m_TriPos(TriPos), m_Degree(Degree) { }
-
-
-inline size_t tri_stripper::triangle_degree::Degree() const {
-	return m_Degree;
-}
-
-
-inline size_t tri_stripper::triangle_degree::TriPos() const {
-	return m_TriPos;
-}
-
-
-inline void tri_stripper::triangle_degree::SetDegree(const size_t Degree) {
-	m_Degree = Degree;
-}
-
-
-inline tri_stripper::triangle_strip::triangle_strip() : m_StartTriPos(0), m_StartOrder(ABC), m_Size(0) { }
-
-
-inline tri_stripper::triangle_strip::triangle_strip(const size_t StartTriPos, const start_order StartOrder, const size_t Size)
-	: m_StartTriPos(StartTriPos), m_StartOrder(StartOrder), m_Size(Size) { }
-
-
-inline size_t tri_stripper::triangle_strip::StartTriPos() const {
-	return m_StartTriPos;
-}
-
-
-inline tri_stripper::triangle_strip::start_order tri_stripper::triangle_strip::StartOrder() const {
-	return m_StartOrder;
+inline void tri_stripper::SetMinStripSize(const size_t MinStripSize)
+{
+	if (MinStripSize < 2)
+		m_MinStripSize = 2;
+	else
+		m_MinStripSize = MinStripSize;
 }
 
 
-inline size_t tri_stripper::triangle_strip::Size() const {
-	return m_Size;
+inline void tri_stripper::SetBackwardSearch(const bool Enabled)
+{
+	m_BackwardSearch = Enabled;
 }
 
 
-inline bool tri_stripper::_cmp_tri_interface_lt::operator() (const triangle_edge & a, const triangle_edge & b) const {
-	const tri_stripper::index A1 = a.A();
-	const tri_stripper::index B1 = a.B();
-	const tri_stripper::index A2 = b.A();
-	const tri_stripper::index B2 = b.B();
 
-	if ((A1 < A2) || ((A1 == A2) && (B1 < B2)))
-		return true;
-	else
-		return false;
+inline void tri_stripper::SetPushCacheHits(bool Enabled)
+{
+	m_Cache.push_cache_hits(Enabled);
 }
 
 
-inline bool tri_stripper::_cmp_tri_degree_gt::operator () (const triangle_degree & a, const triangle_degree & b) const {
-	// the triangle with a smaller degree has more priority 
-	return a.Degree() > b.Degree();
-}
 
 
+} // namespace triangle_stripper
 
 
-} // namespace triangle_stripper
 
 
+#endif // TRI_STRIPPER_HEADER_GUARD_TRI_STRIPPER_H
diff --git a/NifExport/pch.h b/NifExport/pch.h
index 3427c16..a965e1c 100755
--- a/NifExport/pch.h
+++ b/NifExport/pch.h
@@ -38,6 +38,9 @@
 #include "obj/NiStringExtraData.h"
 #include "obj/bhkRigidBodyT.h"
 
+// undef macros for tristripper
+#undef max
+#undef min
 #include "NvTriStrip/NvTriStrip.h"
 #include "TriStripper/tri_stripper.h"
 
diff --git a/NifFurniture/NifFurniture.rc b/NifFurniture/NifFurniture.rc
index b8a3219..ef6495d 100755
--- a/NifFurniture/NifFurniture.rc
+++ b/NifFurniture/NifFurniture.rc
@@ -13,7 +13,7 @@
 #undef APSTUDIO_READONLY_SYMBOLS
 
 /////////////////////////////////////////////////////////////////////////////
-// Deutsch (Deutschland) resources
+// German (Germany) resources
 
 #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
 #ifdef _WIN32
@@ -35,12 +35,12 @@ END
 
 #endif    // APSTUDIO_INVOKED
 
-#endif    // Deutsch (Deutschland) resources
+#endif    // German (Germany) resources
 /////////////////////////////////////////////////////////////////////////////
 
 
 /////////////////////////////////////////////////////////////////////////////
-// Englisch (USA) resources
+// English (U.S.) resources
 
 #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
 #ifdef _WIN32
@@ -48,6 +48,26 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 #pragma code_page(1252)
 #endif //_WIN32
 
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
 /////////////////////////////////////////////////////////////////////////////
 //
 // Dialog
@@ -57,8 +77,7 @@ IDD_PANEL DIALOGEX 0, 0, 108, 38
 STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE
 FONT 8, "MS Sans Serif", 0, 0, 0x0
 BEGIN
-    COMBOBOX        IDC_CB_TYPE,6,18,96,60,CBS_DROPDOWNLIST | WS_VSCROLL | 
-                    WS_TABSTOP
+    COMBOBOX        IDC_CB_TYPE,6,18,96,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
     LTEXT           "Type",IDC_STATIC,6,6,17,8
 END
 
@@ -82,25 +101,43 @@ END
 #endif    // APSTUDIO_INVOKED
 
 
-#ifdef APSTUDIO_INVOKED
 /////////////////////////////////////////////////////////////////////////////
 //
-// TEXTINCLUDE
+// Version
 //
 
-1 TEXTINCLUDE 
-BEGIN
-    "resource.h\0"
-END
-
-2 TEXTINCLUDE 
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,2,1,0
+ PRODUCTVERSION 0,2,1,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
 BEGIN
-    "#include ""afxres.h""\r\n"
-    "\0"
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "FileDescription", "3ds Max Nif Furniture Plugin"
+            VALUE "FileVersion", "0, 2, 1, 0"
+            VALUE "InternalName", "NifFurniture.dlu"
+            VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved."
+            VALUE "OriginalFilename", "NifFurniture.dlu"
+            VALUE "ProductName", "3ds Max Nif Furniture Plugin"
+            VALUE "ProductVersion", "0, 2, 1, 0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
 END
 
-#endif    // APSTUDIO_INVOKED
-
 
 /////////////////////////////////////////////////////////////////////////////
 //
@@ -117,7 +154,7 @@ BEGIN
     IDS_FURNITURE_MARKER    "Furniture Marker"
 END
 
-#endif    // Englisch (USA) resources
+#endif    // English (U.S.) resources
 /////////////////////////////////////////////////////////////////////////////
 
 
diff --git a/NifImport/MaxNifImport.rc b/NifImport/MaxNifImport.rc
index 639490e..bbbac52 100644
--- a/NifImport/MaxNifImport.rc
+++ b/NifImport/MaxNifImport.rc
@@ -108,13 +108,13 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 0,2,0,0
- PRODUCTVERSION 0,2,0,0
- FILEFLAGSMASK 0x37L
+ FILEVERSION 0,2,1,0
+ PRODUCTVERSION 0,2,1,0
+ FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
- FILEFLAGS 0x21L
+ FILEFLAGS 0x1L
 #else
- FILEFLAGS 0x20L
+ FILEFLAGS 0x0L
 #endif
  FILEOS 0x4L
  FILETYPE 0x2L
@@ -125,13 +125,12 @@ BEGIN
         BLOCK "040904b0"
         BEGIN
             VALUE "FileDescription", "3ds Max Nif Importer"
-            VALUE "FileVersion", "0, 2, 0, 0"
+            VALUE "FileVersion", "0, 2, 1, 0"
             VALUE "InternalName", "MaxNifImport.dli"
             VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved."
             VALUE "OriginalFilename", "MaxNifImport.dli"
             VALUE "ProductName", "3ds Max Nif Importer"
-            VALUE "ProductVersion", "0, 2, 0, 0"
-            VALUE "SpecialBuild", "Alpha"
+            VALUE "ProductVersion", "0, 2, 1, 0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/NifProps/NifProps.rc b/NifProps/NifProps.rc
index 67b87a2..38a094a 100755
--- a/NifProps/NifProps.rc
+++ b/NifProps/NifProps.rc
@@ -13,7 +13,7 @@
 #undef APSTUDIO_READONLY_SYMBOLS
 
 /////////////////////////////////////////////////////////////////////////////
-// Deutsch (Deutschland) resources
+// German (Germany) resources
 
 #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
 #ifdef _WIN32
@@ -35,12 +35,12 @@ END
 
 #endif    // APSTUDIO_INVOKED
 
-#endif    // Deutsch (Deutschland) resources
+#endif    // German (Germany) resources
 /////////////////////////////////////////////////////////////////////////////
 
 
 /////////////////////////////////////////////////////////////////////////////
-// Englisch (USA) resources
+// English (U.S.) resources
 
 #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
 #ifdef _WIN32
@@ -77,99 +77,53 @@ IDD_PANEL DIALOGEX 0, 0, 107, 401
 STYLE DS_SETFONT | DS_3DLOOK | WS_CHILD | WS_VISIBLE
 FONT 8, "MS Sans Serif", 0, 0, 0x0
 BEGIN
-    COMBOBOX        IDC_CB_MATERIAL,12,58,83,157,CBS_DROPDOWNLIST | 
-                    WS_DISABLED | WS_VSCROLL | WS_TABSTOP
-    CONTROL         "Material",IDC_LBL_MATERIAL,"Static",SS_LEFTNOWORDWRAP | 
-                    SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,12,50,83,8
-    COMBOBOX        IDC_CB_LAYER,12,82,83,171,CBS_DROPDOWNLIST | WS_DISABLED | 
-                    WS_VSCROLL | WS_TABSTOP
-    CONTROL         "Layer",IDC_LBL_LAYER,"Static",SS_LEFTNOWORDWRAP | 
-                    SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,12,74,83,8
+    COMBOBOX        IDC_CB_MATERIAL,12,58,83,157,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "Material",IDC_LBL_MATERIAL,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,12,50,83,8
+    COMBOBOX        IDC_CB_LAYER,12,82,83,171,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "Layer",IDC_LBL_LAYER,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,12,74,83,8
     GROUPBOX        "Havok",IDC_GRP_HAVOK,5,39,95,285,WS_DISABLED
     GROUPBOX        "Object",IDC_GRP_OBJECT,5,5,95,32
-    CONTROL         "Is Collision Mesh",IDC_CHK_ISCOLL,"Button",
-                    BS_AUTOCHECKBOX | WS_TABSTOP,14,18,80,10
+    CONTROL         "Is Collision Mesh",IDC_CHK_ISCOLL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,18,80,10
     GROUPBOX        "Global",IDC_STATIC,5,329,95,65,WS_DISABLED
-    EDITTEXT        IDC_ED_BSXFLAGS,13,352,80,12,ES_AUTOHSCROLL | 
-                    WS_DISABLED
-    CONTROL         "BSXFlags",IDC_LBL_BSXFLAGS,"Static",SS_LEFTNOWORDWRAP | 
-                    SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,13,340,80,8
-    EDITTEXT        IDC_ED_STRINGSEXTRA,13,374,80,12,ES_AUTOHSCROLL | 
-                    WS_DISABLED
-    CONTROL         "Strings Extra Data",IDC_LBL_STRINGSEXTRA,"Static",
-                    SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | 
-                    WS_GROUP,13,365,80,8
-    CONTROL         "Center",IDC_LBL_CENTER,"Static",SS_LEFTNOWORDWRAP | 
-                    SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,12,98,31,8
-    CONTROL         "Mass",IDC_LBL_MASS,"Static",SS_LEFTNOWORDWRAP | 
-                    WS_DISABLED | WS_GROUP,12,124,28,8
-    CONTROL         "Friction",IDC_LBL_FRICTION,"Static",SS_LEFTNOWORDWRAP | 
-                    WS_DISABLED | WS_GROUP,42,124,28,8
-    LTEXT           "Resti-\r\ntution",IDC_LBL_RESTITUTION,70,124,24,15,
-                    WS_DISABLED
-    CONTROL         "Motion System",IDC_LBL_MOTION_SYSTEM,"Static",
-                    SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | 
-                    WS_GROUP,12,263,67,8
-    COMBOBOX        IDC_CB_MOTION_SYSTEM,12,273,83,74,CBS_DROPDOWNLIST | 
-                    WS_DISABLED | WS_VSCROLL | WS_TABSTOP
-    CONTROL         "Quality Type",IDC_LBL_QUALITY_TYPE,"Static",
-                    SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | 
-                    WS_GROUP,12,289,43,8
-    COMBOBOX        IDC_CB_QUALITY_TYPE,12,298,83,63,CBS_DROPDOWN | 
-                    WS_DISABLED | WS_VSCROLL | WS_TABSTOP
-    CONTROL         "",IDC_ED_CENTER_X,"CustEdit",WS_DISABLED | WS_TABSTOP,
-                    12,109,20,10
-    CONTROL         "",IDC_SP_CENTER_X,"SpinnerControl",WS_DISABLED,30,109,7,
-                    10
-    CONTROL         "",IDC_ED_CENTER_Y,"CustEdit",WS_DISABLED | WS_TABSTOP,
-                    42,109,20,10
-    CONTROL         "",IDC_SP_CENTER_Y,"SpinnerControl",WS_DISABLED,60,109,7,
-                    10
-    CONTROL         "",IDC_ED_CENTER_Z,"CustEdit",WS_DISABLED | WS_TABSTOP,
-                    72,109,20,10
-    CONTROL         "",IDC_SP_CENTER_Z,"SpinnerControl",WS_DISABLED,92,109,7,
-                    10
-    CONTROL         "",IDC_ED_MASS,"CustEdit",WS_DISABLED | WS_TABSTOP,12,
-                    143,20,10
+    EDITTEXT        IDC_ED_BSXFLAGS,13,352,80,12,ES_AUTOHSCROLL | WS_DISABLED
+    CONTROL         "BSXFlags",IDC_LBL_BSXFLAGS,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,13,340,80,8
+    EDITTEXT        IDC_ED_STRINGSEXTRA,13,374,80,12,ES_AUTOHSCROLL | WS_DISABLED
+    CONTROL         "Strings Extra Data",IDC_LBL_STRINGSEXTRA,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,13,365,80,8
+    CONTROL         "Center",IDC_LBL_CENTER,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,12,98,31,8
+    CONTROL         "Mass",IDC_LBL_MASS,"Static",SS_LEFTNOWORDWRAP | WS_DISABLED | WS_GROUP,12,124,28,8
+    CONTROL         "Friction",IDC_LBL_FRICTION,"Static",SS_LEFTNOWORDWRAP | WS_DISABLED | WS_GROUP,42,124,28,8
+    LTEXT           "Resti-\r\ntution",IDC_LBL_RESTITUTION,70,124,24,15,WS_DISABLED
+    CONTROL         "Motion System",IDC_LBL_MOTION_SYSTEM,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,12,263,67,8
+    COMBOBOX        IDC_CB_MOTION_SYSTEM,12,273,83,74,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "Quality Type",IDC_LBL_QUALITY_TYPE,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,12,289,43,8
+    COMBOBOX        IDC_CB_QUALITY_TYPE,12,298,83,63,CBS_DROPDOWN | WS_DISABLED | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "",IDC_ED_CENTER_X,"CustEdit",WS_DISABLED | WS_TABSTOP,12,109,20,10
+    CONTROL         "",IDC_SP_CENTER_X,"SpinnerControl",WS_DISABLED,30,109,7,10
+    CONTROL         "",IDC_ED_CENTER_Y,"CustEdit",WS_DISABLED | WS_TABSTOP,42,109,20,10
+    CONTROL         "",IDC_SP_CENTER_Y,"SpinnerControl",WS_DISABLED,60,109,7,10
+    CONTROL         "",IDC_ED_CENTER_Z,"CustEdit",WS_DISABLED | WS_TABSTOP,72,109,20,10
+    CONTROL         "",IDC_SP_CENTER_Z,"SpinnerControl",WS_DISABLED,92,109,7,10
+    CONTROL         "",IDC_ED_MASS,"CustEdit",WS_DISABLED | WS_TABSTOP,12,143,20,10
     CONTROL         "",IDC_SP_MASS,"SpinnerControl",WS_DISABLED,30,143,7,10
-    CONTROL         "",IDC_ED_FRICTION,"CustEdit",WS_DISABLED | WS_TABSTOP,
-                    42,143,20,10
-    CONTROL         "",IDC_SP_FRICTION,"SpinnerControl",WS_DISABLED,60,143,7,
-                    10
-    CONTROL         "",IDC_ED_RESTITUTION,"CustEdit",WS_DISABLED | 
-                    WS_TABSTOP,70,143,20,10
-    CONTROL         "",IDC_SP_RESTITUTION,"SpinnerControl",WS_DISABLED,90,
-                    143,7,10
-    LTEXT           "Linear\r\nDamping",IDC_LBL_LINEAR_DAMPING,12,159,30,17,
-                    WS_DISABLED
-    CONTROL         "",IDC_ED_LINEAR_DAMPING,"CustEdit",WS_DISABLED | 
-                    WS_TABSTOP,12,178,35,10
-    CONTROL         "",IDC_SP_LINEAR_DAMPING,"SpinnerControl",WS_DISABLED,45,
-                    178,7,10
-    LTEXT           "Angular\r\nDamping",IDC_LBL_ANGULAR_DAMPING,55,159,45,
-                    17,WS_DISABLED
-    CONTROL         "",IDC_ED_ANGULAR_DAMPING,"CustEdit",WS_DISABLED | 
-                    WS_TABSTOP,55,178,35,10
-    CONTROL         "",IDC_SP_ANGULAR_DAMPING,"SpinnerControl",WS_DISABLED,
-                    90,178,7,10
-    LTEXT           "Max. Linear\r\nVelocity",IDC_LBL_MAX_LINEAR_VELOCITY,12,
-                    194,40,16,WS_DISABLED
-    CONTROL         "",IDC_ED_MAX_LINEAR_VELOCITY,"CustEdit",WS_DISABLED | 
-                    WS_TABSTOP,12,214,35,10
-    CONTROL         "",IDC_SP_MAX_LINEAR_VELOCITY,"SpinnerControl",
-                    WS_DISABLED,45,214,7,10
-    LTEXT           "Max. Angular\r\nVelocity",IDC_LBL_MAX_ANGULAR_VELOCITY,
-                    55,194,45,16,WS_DISABLED
-    CONTROL         "",IDC_ED_MAX_ANGULAR_VELOCITY,"CustEdit",WS_DISABLED | 
-                    WS_TABSTOP,55,214,35,10
-    CONTROL         "",IDC_SP_MAX_ANGULAR_VELOCITY,"SpinnerControl",
-                    WS_DISABLED,90,214,7,10
-    LTEXT           "Penetration\r\nDepth",IDC_LBL_PENETRATION_DEPTH,12,229,
-                    40,16,WS_DISABLED
-    CONTROL         "",IDC_ED_PENETRATION_DEPTH,"CustEdit",WS_DISABLED | 
-                    WS_TABSTOP,12,249,35,10
-    CONTROL         "",IDC_SP_PENETRATION_DEPTH,"SpinnerControl",WS_DISABLED,
-                    45,249,7,10
+    CONTROL         "",IDC_ED_FRICTION,"CustEdit",WS_DISABLED | WS_TABSTOP,42,143,20,10
+    CONTROL         "",IDC_SP_FRICTION,"SpinnerControl",WS_DISABLED,60,143,7,10
+    CONTROL         "",IDC_ED_RESTITUTION,"CustEdit",WS_DISABLED | WS_TABSTOP,70,143,20,10
+    CONTROL         "",IDC_SP_RESTITUTION,"SpinnerControl",WS_DISABLED,90,143,7,10
+    LTEXT           "Linear\r\nDamping",IDC_LBL_LINEAR_DAMPING,12,159,30,17,WS_DISABLED
+    CONTROL         "",IDC_ED_LINEAR_DAMPING,"CustEdit",WS_DISABLED | WS_TABSTOP,12,178,35,10
+    CONTROL         "",IDC_SP_LINEAR_DAMPING,"SpinnerControl",WS_DISABLED,45,178,7,10
+    LTEXT           "Angular\r\nDamping",IDC_LBL_ANGULAR_DAMPING,55,159,45,17,WS_DISABLED
+    CONTROL         "",IDC_ED_ANGULAR_DAMPING,"CustEdit",WS_DISABLED | WS_TABSTOP,55,178,35,10
+    CONTROL         "",IDC_SP_ANGULAR_DAMPING,"SpinnerControl",WS_DISABLED,90,178,7,10
+    LTEXT           "Max. Linear\r\nVelocity",IDC_LBL_MAX_LINEAR_VELOCITY,12,194,40,16,WS_DISABLED
+    CONTROL         "",IDC_ED_MAX_LINEAR_VELOCITY,"CustEdit",WS_DISABLED | WS_TABSTOP,12,214,35,10
+    CONTROL         "",IDC_SP_MAX_LINEAR_VELOCITY,"SpinnerControl",WS_DISABLED,45,214,7,10
+    LTEXT           "Max. Angular\r\nVelocity",IDC_LBL_MAX_ANGULAR_VELOCITY,55,194,45,16,WS_DISABLED
+    CONTROL         "",IDC_ED_MAX_ANGULAR_VELOCITY,"CustEdit",WS_DISABLED | WS_TABSTOP,55,214,35,10
+    CONTROL         "",IDC_SP_MAX_ANGULAR_VELOCITY,"SpinnerControl",WS_DISABLED,90,214,7,10
+    LTEXT           "Penetration\r\nDepth",IDC_LBL_PENETRATION_DEPTH,12,229,40,16,WS_DISABLED
+    CONTROL         "",IDC_ED_PENETRATION_DEPTH,"CustEdit",WS_DISABLED | WS_TABSTOP,12,249,35,10
+    CONTROL         "",IDC_SP_PENETRATION_DEPTH,"SpinnerControl",WS_DISABLED,45,249,7,10
 END
 
 
@@ -382,6 +336,44 @@ BEGIN
 END
 
 
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,2,1,0
+ PRODUCTVERSION 0,2,1,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "FileDescription", "3ds Max Nif Reactor Properites Plugin"
+            VALUE "FileVersion", "0, 2, 1, 0"
+            VALUE "InternalName", "NifProps.dlu"
+            VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved."
+            VALUE "OriginalFilename", "NifProps.dlu"
+            VALUE "ProductName", "3ds Max Nif Reactor Properites Plugin"
+            VALUE "ProductVersion", "0, 2, 1, 0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+
 /////////////////////////////////////////////////////////////////////////////
 //
 // String Table
@@ -396,7 +388,7 @@ BEGIN
     IDS_SPIN                "Spin"
 END
 
-#endif    // Englisch (USA) resources
+#endif    // English (U.S.) resources
 /////////////////////////////////////////////////////////////////////////////
 
 
-- 
GitLab