From 53fd4116151439480bb25242428d4b200ee7759d Mon Sep 17 00:00:00 2001
From: Tazpn <tazpn@users.sourceforge.net>
Date: Mon, 25 Jun 2007 04:58:26 +0000
Subject: [PATCH] 0.2.14 ----- o Exporter   - Fix issues with bhkConvexShape
 and bhkRigidBody o Exporter/Importer   - Introduce Morpher animation support

---
 MaxNifPlugins_Readme.txt      |   9 +-
 NifCommon/nimorph.cpp         | 125 +++++++++++++++
 NifCommon/niutils.h           |   1 +
 NifExport/Animation.cpp       | 121 +++++++++++++-
 NifExport/Exporter.cpp        |   2 +-
 NifExport/Exporter.h          |   3 +-
 NifExport/Mesh.cpp            | 287 +++++++++++++++++++++-------------
 NifImport/BaseImporter.h      |   2 +-
 NifImport/ImportAnimation.cpp |  38 +++--
 NifPlugins_VC80.vcproj        |  64 ++++----
 10 files changed, 483 insertions(+), 169 deletions(-)

diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt
index 3b3a97e..ccdd24b 100644
--- a/MaxNifPlugins_Readme.txt
+++ b/MaxNifPlugins_Readme.txt
@@ -36,10 +36,11 @@
       0.2.14
       -----
     o Exporter
-      - Fix a number of issues with bhkConvexShape and bhkRigidBody
-    o Importer
-      - Introduce Morph animation support
-      
+      - Fix issues with bhkConvexShape and bhkRigidBody
+    o Exporter/Importer
+      - Introduce Morpher animation support
+         
+          
       0.2.13
       -----
     o Properties
diff --git a/NifCommon/nimorph.cpp b/NifCommon/nimorph.cpp
index 9904715..5b971e2 100644
--- a/NifCommon/nimorph.cpp
+++ b/NifCommon/nimorph.cpp
@@ -542,4 +542,129 @@ INode *MorpherGetProgMorph(Modifier* mod, int index, int morphIdx)
 	pop_value_locals();
 	pop_alloc_frame();
 	return retval;
+}
+
+int MorpherGetNumVerts(Modifier* mod, int index)
+{
+	int retval = 0;
+	// Magic initialization stuff for maxscript.
+	static bool script_initialized = false;
+	if (!script_initialized) {
+		init_MAXScript();
+		script_initialized = TRUE;
+	}
+	init_thread_locals();
+	push_alloc_frame();
+	five_value_locals(name, fn, mod, index, result);
+	save_current_frames();
+	trace_back_active = FALSE;
+	try	{
+		// Create the name of the maxscript function we want.
+		// and look it up in the global names
+		vl.name = Name::intern(_T("WM3_MC_NumPts"));
+		vl.fn = globals->get(vl.name);
+
+		// For some reason we get a global thunk back, so lets
+		// check the cell which should point to the function.
+		// Just in case if it points to another global thunk
+		// try it again.
+		while (vl.fn != NULL && is_globalthunk(vl.fn))
+			vl.fn = static_cast<GlobalThunk*>(vl.fn)->cell;
+		while (vl.fn != NULL && is_constglobalthunk(vl.fn))
+			vl.fn = static_cast<ConstGlobalThunk*>(vl.fn)->cell;
+
+		// Now we should have a MAXScriptFunction, which we can
+		// call to do the actual conversion. If we didn't
+		// get a MAXScriptFunction, we can't convert.
+		// class_tag(MAXScriptFunction)
+		if (vl.fn != NULL && vl.fn->tag == class_tag(Primitive)) {
+			Value* args[2];
+
+			// Ok. WM3_MC_BuildFromNode takes three parameters
+			args[0] = vl.mod = MAXModifier::intern(mod);	// The original material
+			args[1] = vl.index = Integer::intern(index);
+
+			// Call the function and save the result.
+			vl.result = static_cast<Primitive*>(vl.fn)->apply(args, 2);
+			if (vl.result->tag == class_tag(Integer))
+				retval = vl.result->to_int();
+		}
+	} catch (...) {
+		clear_error_source_data();
+		restore_current_frames();
+		MAXScript_signals = 0;
+		if (progress_bar_up)
+			MAXScript_interface->ProgressEnd(), progress_bar_up = FALSE;
+	}
+
+	// Magic Max Script stuff to clear the frame and locals.
+	pop_value_locals();
+	pop_alloc_frame();
+	return retval;
+}
+
+void MorpherGetMorphVerts(Modifier* mod, int index, vector<Vector3>& verts)
+{
+	int nverts = MorpherGetNumVerts(mod, index);
+	verts.resize(nverts, Vector3(0.0f,0.0f,0.0f));
+	if (nverts == 0)
+		return;
+
+	// Magic initialization stuff for maxscript.
+	static bool script_initialized = false;
+	if (!script_initialized) {
+		init_MAXScript();
+		script_initialized = TRUE;
+	}
+	init_thread_locals();
+	push_alloc_frame();
+	six_value_locals(name, fn, mod, index, midx, result);
+	save_current_frames();
+	trace_back_active = FALSE;
+	try	{
+		// Create the name of the maxscript function we want.
+		// and look it up in the global names
+		vl.name = Name::intern(_T("WM3_MC_GetMorphPoint"));
+		vl.fn = globals->get(vl.name);
+
+		// For some reason we get a global thunk back, so lets
+		// check the cell which should point to the function.
+		// Just in case if it points to another global thunk
+		// try it again.
+		while (vl.fn != NULL && is_globalthunk(vl.fn))
+			vl.fn = static_cast<GlobalThunk*>(vl.fn)->cell;
+		while (vl.fn != NULL && is_constglobalthunk(vl.fn))
+			vl.fn = static_cast<ConstGlobalThunk*>(vl.fn)->cell;
+
+		// Now we should have a MAXScriptFunction, which we can
+		// call to do the actual conversion. If we didn't
+		// get a MAXScriptFunction, we can't convert.
+		// class_tag(MAXScriptFunction)
+		if (vl.fn != NULL && vl.fn->tag == class_tag(Primitive)) {
+			Value* args[3];
+
+			// Ok. WM3_MC_BuildFromNode takes three parameters
+			args[0] = vl.mod = MAXModifier::intern(mod);	// The original material
+			args[1] = vl.index = Integer::intern(index);
+
+			for (int i=0; i<nverts; ++i)
+			{
+				args[2] = vl.midx = Integer::intern(i);
+				// Call the function and save the result.
+				vl.result = static_cast<Primitive*>(vl.fn)->apply(args, 3);
+				if (vl.result->tag == class_tag(Point3Value))
+					verts[i] = TOVECTOR3(vl.result->to_point3());
+			}
+		}
+	} catch (...) {
+		clear_error_source_data();
+		restore_current_frames();
+		MAXScript_signals = 0;
+		if (progress_bar_up)
+			MAXScript_interface->ProgressEnd(), progress_bar_up = FALSE;
+	}
+
+	// Magic Max Script stuff to clear the frame and locals.
+	pop_value_locals();
+	pop_alloc_frame();
 }
\ No newline at end of file
diff --git a/NifCommon/niutils.h b/NifCommon/niutils.h
index b9ef787..362f64b 100644
--- a/NifCommon/niutils.h
+++ b/NifCommon/niutils.h
@@ -441,5 +441,6 @@ extern bool MorpherIsActive(Modifier* mod, int index);
 extern bool MorpherHasData(Modifier* mod, int index);
 extern int MorpherNumProgMorphs(Modifier* mod, int index);
 extern INode *MorpherGetProgMorph(Modifier* mod, int index, int morphIdx);
+extern void MorpherGetMorphVerts(Modifier* mod, int index, vector<Niflib::Vector3>& verts);
 
 #endif // _NIUTILS_H_
\ No newline at end of file
diff --git a/NifExport/Animation.cpp b/NifExport/Animation.cpp
index 8a5af4d..8ea410c 100644
--- a/NifExport/Animation.cpp
+++ b/NifExport/Animation.cpp
@@ -64,8 +64,7 @@ struct AnimationExport
    NiTimeControllerRef exportController(INode *node, Interval range, bool setTM );
    bool GetTextKeys(INode *node, vector<StringKey>& textKeys);
    Interval GetTimeRange(INode *n);
-   void GetTimeRange(Control *n, Interval& range);
-
+   void GetTimeRange(Control *c, Interval& range);
 
    Exporter &ne;
    Interval range;
@@ -205,6 +204,7 @@ NiNodeRef Exporter::createAccumNode(NiNodeRef parent, INode *node)
    }
    return accumNode;
 }
+
 Exporter::Result Exporter::doAnimExport(NiControllerSequenceRef root)
 {
    AnimationExport animExporter(*this);
@@ -247,7 +247,7 @@ bool Exporter::isNodeTracked(INode *node)
 }
 
 Exporter::Result Exporter::scanForAnimation(INode *node)
-{   
+{
 	if (NULL == node) 
 		return Exporter::Skip;
 
@@ -602,7 +602,7 @@ Control *AnimationExport::GetTMController(INode *n)
    return c;
 }
 
-void AnimationExport::GetTimeRange(Control *c, Interval& range)
+static void GetTimeRange(Control *c, Interval& range)
 {
    if (IKeyControl *ikeys = GetKeyControlInterface(c)){
       int n = ikeys->GetNumKeys();
@@ -620,6 +620,11 @@ void AnimationExport::GetTimeRange(Control *c, Interval& range)
       }
    }
 }
+void AnimationExport::GetTimeRange(Control *c, Interval& range)
+{
+	::GetTimeRange(c, range);
+}
+
 
 Interval AnimationExport::GetTimeRange(INode *node)
 {
@@ -1055,4 +1060,110 @@ bool AnimationExport::exportController(INode *node, bool hasAccum)
       ok |= exportController(child, false);
    }
    return ok;
-}
\ No newline at end of file
+}
+
+Exporter::Result Exporter::exportGeomMorpherControl(Modifier* mod, vector<Vector3>& baseVerts, NiObjectNETRef owner)
+{
+	// Check for morphs
+	//if ( mExportType != NIF_WO_ANIM ) 
+	if (mod != NULL)
+	{
+		if (mod->IsEnabled()) {
+			NiGeomMorpherControllerRef ctrl = new NiGeomMorpherController();
+			NiMorphDataRef data = new NiMorphData();
+			vector<NiInterpolatorRef> interpolators;
+			vector<int> indices;
+			for (int i=1; i<100; ++i) {
+				if (MorpherHasData(mod, i) && MorpherIsActive(mod, i)) {
+					indices.push_back(i);
+				}
+			}
+			data->SetMorphCount( indices.size() + 1);
+			data->SetFrameName(0, string("Base"));
+			data->SetMorphKeyType(0, LINEAR_KEY);
+
+			Interval range; range.SetEmpty();
+			
+			int nbaseverts = baseVerts.size();
+			data->SetVertexCount( nbaseverts );
+			data->SetMorphVerts(0, baseVerts);
+			if (NiFloatInterpolatorRef interp = new NiFloatInterpolator())
+			{
+				NiFloatDataRef fdata = new NiFloatData();
+				vector<FloatKey> keys;
+				keys.resize(2);
+				data->SetMorphKeyType(0, LINEAR_KEY);
+				data->SetMorphKeys(0, keys);
+				fdata->SetKeyType(LINEAR_KEY);
+				fdata->SetKeys(keys);
+				interp->SetFloatValue(FloatNegINF);
+				interp->SetData(fdata);
+				interpolators.push_back(interp);
+			}
+
+			for (int i=0; i<indices.size(); ++i) {
+				int idx = indices[i];
+				if (IParamBlock* pblock = (IParamBlock*)mod->GetReference(idx)) {
+					if (Control *c = pblock->GetController(0)) {
+						GetTimeRange(c, range);
+					}
+				}
+			}
+
+			for (int i=0; i<indices.size(); ++i) {
+				int idx = indices[i];
+
+				IParamBlock* pblock = (IParamBlock*)mod->GetReference(idx);
+				TSTR name = MorpherGetName(mod, idx);
+				data->SetFrameName(i+1, string(name));
+
+				KeyType keyType = LINEAR_KEY;
+				vector<FloatKey> keys;
+				if (Control *c = pblock->GetController(0)) {
+					if (c->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
+						GetKeys<FloatKey, ILinFloatKey>(c, keys, range);
+						keyType = LINEAR_KEY;
+					} else if (c->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
+						GetKeys<FloatKey, IBezFloatKey>(c, keys, range);
+						keyType = QUADRATIC_KEY;
+					} else if (c->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
+						GetKeys<FloatKey, ITCBFloatKey>(c, keys, range);
+						keyType = TBC_KEY;
+					} else {
+						GetKeys<FloatKey, IBezFloatKey>(c, keys, range);
+						keyType = QUADRATIC_KEY;
+					}
+				}
+				ScaleKeys(keys, 1.0f/100.0f);
+				if (NiFloatInterpolatorRef interp = new NiFloatInterpolator())
+				{
+					NiFloatDataRef fdata = new NiFloatData();
+					data->SetMorphKeyType(i+1, keyType);
+					data->SetMorphKeys(i+1, keys);
+					fdata->SetKeyType(keyType);
+					fdata->SetKeys(keys);
+					interp->SetFloatValue(FloatNegINF);
+					interp->SetData(fdata);
+					interpolators.push_back(interp);
+				}
+
+				vector<Vector3> verts;
+				MorpherGetMorphVerts(mod, idx, verts);
+				for (int j=0; j<verts.size(); j++)
+					verts[j] = verts[j] - baseVerts[j];
+				data->SetMorphVerts(i+1, verts);
+			}
+			ctrl->SetData(data);
+			if (Exporter::mNifVersionInt >= VER_10_1_0_106)
+				ctrl->SetInterpolators(interpolators);
+			ctrl->SetFlags( 0x000C );
+			ctrl->SetFrequency(1.0f);
+			ctrl->SetPhase(0.0f);
+			ctrl->SetStartTime( 0.0f );
+			ctrl->SetStopTime( FrameToTime( range.Duration()-1 ) );
+			owner->AddController(ctrl);
+		}
+	}
+	return Exporter::Ok;
+}
+
diff --git a/NifExport/Exporter.cpp b/NifExport/Exporter.cpp
index ac68da3..c9c21de 100755
--- a/NifExport/Exporter.cpp
+++ b/NifExport/Exporter.cpp
@@ -275,7 +275,7 @@ Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node)
             // Create node if using Extra Nodes or if exporting with anim and node has key values
             newParent = makeNode(nodeParent, node, local);
          } else {
-            // Else dont create a node
+            // Else don't create a node
             newParent = nodeParent;
          }
          // No need to export meshes when NIF is not exported.
diff --git a/NifExport/Exporter.h b/NifExport/Exporter.h
index feb1b66..16aafb0 100755
--- a/NifExport/Exporter.h
+++ b/NifExport/Exporter.h
@@ -207,7 +207,7 @@ public:
 	// adds a face to a face group
 	void					addFace(FaceGroup &grp, int face, const int vi[3], Mesh *mesh, const Matrix3 &texm, vector<Color4>& vertColors);
 	// creates face groups from faces with same sub material id
-	bool					splitMesh(INode *node, Mesh *, FaceGroups &grps, TimeValue t, vector<Color4>& vertColors);
+	bool					splitMesh(INode *node, Mesh &, FaceGroups &grps, TimeValue t, vector<Color4>& vertColors, bool noSplit);
 	// creates a NiTriStrips or NiTriShape hierarchy from a face group
 	NiTriBasedGeomRef makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp, bool exportStrips);
 	// splits mesh and converts it into nif blocks
@@ -271,6 +271,7 @@ public:
 	bool isNodeKeyed(INode *node);
 	Ref<NiTimeController> CreateController(INode *node, Interval range);
 	static void InitializeTimeController(Ref<NiTimeController> ctrl, NiNodeRef parent);
+	Result exportGeomMorpherControl(Modifier* mod, vector<Niflib::Vector3>& verts, NiObjectNETRef owner);
 
 	/* misc export */
 	bool exportUPB(NiNodeRef &root, INode *node);
diff --git a/NifExport/Mesh.cpp b/NifExport/Mesh.cpp
index f36fcf7..9ce3def 100755
--- a/NifExport/Mesh.cpp
+++ b/NifExport/Mesh.cpp
@@ -18,139 +18,150 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
 {
 	ObjectState os = node->EvalWorldState(t);
 
-   bool local = !mFlattenHierarchy;
+	bool local = !mFlattenHierarchy;
 
 	TriObject *tri = (TriObject *)os.obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0));
 	if (!tri)
 		return Error;
 
-   Mesh *copymesh = NULL;
+	Mesh *copymesh = NULL;
 	Mesh *mesh = &tri->GetMesh();
 
-   Matrix3 mtx(true);
-   if (Exporter::mCollapseTransforms)
-   {
-      mtx = GetNodeLocalTM(node, t);
-      //if ( fabs(mtx.GetRow(0)[0]) != fabs(mtx.GetRow(1)[1])
-      //   ||fabs(mtx.GetRow(0)[0]) != fabs(mtx.GetRow(2)[2])
-      //   )
-      {
-         mtx.NoTrans();    
-         mesh = copymesh = new Mesh(*mesh);     
-         int n = mesh->getNumVerts();
-         for ( unsigned int i = 0; i < n; ++i ) {
-            Point3& vert = mesh->getVert(i);
-            vert = mtx * vert;
-         }
-         mesh->checkNormals(TRUE);
+	Matrix3 mtx(true);
+	if (Exporter::mCollapseTransforms)
+	{
+		mtx = GetNodeLocalTM(node, t);
+		//if ( fabs(mtx.GetRow(0)[0]) != fabs(mtx.GetRow(1)[1])
+		//   ||fabs(mtx.GetRow(0)[0]) != fabs(mtx.GetRow(2)[2])
+		//   )
+		{
+			mtx.NoTrans();    
+			mesh = copymesh = new Mesh(*mesh);     
+			int n = mesh->getNumVerts();
+			for ( unsigned int i = 0; i < n; ++i ) {
+				Point3& vert = mesh->getVert(i);
+				vert = mtx * vert;
+			}
+			mesh->checkNormals(TRUE);
 #if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 6+
-         MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals ();
-         if (NULL != specNorms) {
-            specNorms->CheckNormals();
-            for ( unsigned int i = 0; i < specNorms->GetNumNormals(); ++i ) {
-               Point3& norm = specNorms->Normal(i);
-               norm = (mtx * norm).Normalize();
-            }
-         }
+			MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals ();
+			if (NULL != specNorms) {
+				specNorms->CheckNormals();
+				for ( unsigned int i = 0; i < specNorms->GetNumNormals(); ++i ) {
+					Point3& norm = specNorms->Normal(i);
+					norm = (mtx * norm).Normalize();
+				}
+			}
 #endif
-      }
-   }
-   // Note that calling setVCDisplayData will clear things like normals so we set this up first
-   vector<Color4> vertColors;
-   if (mVertexColors)
-   {
-      bool hasvc = false;
-      if (mesh->mapSupport(MAP_ALPHA))
-      {
-         mesh->setVCDisplayData(MAP_ALPHA);         int n = mesh->getNumVertCol();
-         if (n > vertColors.size())
-            vertColors.assign(n, Color4(1.0f, 1.0f, 1.0f, 1.0f));
-         VertColor *vertCol = mesh->vertColArray;
-         if (vertCol) {
-            for (int i=0; i<n; ++i) {
-               VertColor c = vertCol[ i ];
-               float a = (c.x + c.y + c.z) / 3.0f;
-               vertColors[i].a = a;
-               hasvc |= (a != 1.0f);
-            }
-         }
-      }
-      if (mesh->mapSupport(0))
-      {
-         mesh->setVCDisplayData(0);
-         VertColor *vertCol = mesh->vertColArray;
-         int n = mesh->getNumVertCol();
-         if (n > vertColors.size())
-            vertColors.assign(n, Color4(1.0f, 1.0f, 1.0f, 1.0f));
-         if (vertCol) {
-            for (int i=0; i<n; ++i) {
-               VertColor col = vertCol[ i ];
-               vertColors[i] = Color4(col.x, col.y, col.z, vertColors[i].a);
-               hasvc |= (col.x != 1.0f || col.y != 1.0f || col.z != 1.0f);
-            }
-         }
-      }
-      if (!hasvc) vertColors.clear();
-   }
-   
+		}
+	}
+	// Note that calling setVCDisplayData will clear things like normals so we set this up first
+	vector<Color4> vertColors;
+	if (mVertexColors)
+	{
+		bool hasvc = false;
+		if (mesh->mapSupport(MAP_ALPHA))
+		{
+			mesh->setVCDisplayData(MAP_ALPHA);         int n = mesh->getNumVertCol();
+			if (n > vertColors.size())
+				vertColors.assign(n, Color4(1.0f, 1.0f, 1.0f, 1.0f));
+			VertColor *vertCol = mesh->vertColArray;
+			if (vertCol) {
+				for (int i=0; i<n; ++i) {
+					VertColor c = vertCol[ i ];
+					float a = (c.x + c.y + c.z) / 3.0f;
+					vertColors[i].a = a;
+					hasvc |= (a != 1.0f);
+				}
+			}
+		}
+		if (mesh->mapSupport(0))
+		{
+			mesh->setVCDisplayData(0);
+			VertColor *vertCol = mesh->vertColArray;
+			int n = mesh->getNumVertCol();
+			if (n > vertColors.size())
+				vertColors.assign(n, Color4(1.0f, 1.0f, 1.0f, 1.0f));
+			if (vertCol) {
+				for (int i=0; i<n; ++i) {
+					VertColor col = vertCol[ i ];
+					vertColors[i] = Color4(col.x, col.y, col.z, vertColors[i].a);
+					hasvc |= (col.x != 1.0f || col.y != 1.0f || col.z != 1.0f);
+				}
+			}
+		}
+		if (!hasvc) vertColors.clear();
+	}
+
 #if VERSION_3DSMAX <= ((5000<<16)+(15<<8)+0) // Version 5
-   mesh->checkNormals(TRUE);
+	mesh->checkNormals(TRUE);
 #else
-   MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals ();
-   if (NULL != specNorms) {
-      specNorms->CheckNormals();
-      if (specNorms->GetNumNormals() == 0)
-         mesh->checkNormals(TRUE);
-   } else {
-      mesh->checkNormals(TRUE);
-   }
+	MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals ();
+	if (NULL != specNorms) {
+		specNorms->CheckNormals();
+		if (specNorms->GetNumNormals() == 0)
+			mesh->checkNormals(TRUE);
+	} else {
+		mesh->checkNormals(TRUE);
+	}
 #endif
 
 	Result result = Ok;
+
+	Modifier* geomMorpherMod = GetMorpherModifier(node);
+	bool noSplit = (NULL != geomMorpherMod);
+
 	while (1)
 	{
 		FaceGroups grps;
-		if (!splitMesh(node, mesh, grps, t, vertColors))
+		if (!splitMesh(node, *mesh, grps, t, vertColors, noSplit))
 		{
 			result = Error;
 			break;
 		}
-      bool exportStrips = mTriStrips;
+		bool exportStrips = mTriStrips;
 
-      Matrix44 tm = Matrix44::IDENTITY;
-      if (!mExportExtraNodes) {
-         Matrix33 rot; Vector3 trans;
-         nodeTransform(rot, trans, node, t, local);
-         tm = Matrix44(trans, rot, 1.0f);
-      }
-      tm = TOMATRIX4(Inverse(mtx)) * tm;
+		Matrix44 tm = Matrix44::IDENTITY;
+		if (!mExportExtraNodes) {
+			Matrix33 rot; Vector3 trans;
+			nodeTransform(rot, trans, node, t, local);
+			tm = Matrix44(trans, rot, 1.0f);
+		}
+		tm = TOMATRIX4(Inverse(mtx)) * tm;
 
-      TSTR basename = node->NodeName();
-      TSTR format = (!basename.isNull() && grps.size() > 1) ? "%s:%d" : "%s";
+		TSTR basename = node->NodeName();
+		TSTR format = (!basename.isNull() && grps.size() > 1) ? "%s:%d" : "%s";
 
-      int i=1;
+		int i=1;
 		FaceGroups::iterator grp;
 		for (grp=grps.begin(); grp!=grps.end(); ++grp, ++i)
 		{
-         string name = FormatString(format, basename.data(), i);
-         NiTriBasedGeomRef shape = makeMesh(ninode, getMaterial(node, grp->first), grp->second, exportStrips);
-         if (shape == NULL)
+			string name = FormatString(format, basename.data(), i);
+			NiTriBasedGeomRef shape = makeMesh(ninode, getMaterial(node, grp->first), grp->second, exportStrips);
+			if (shape == NULL)
 			{
 				result = Error;
 				break;
 			}
 
-         if (node->IsHidden())
-            shape->SetVisibility(false);
+			if (node->IsHidden())
+				shape->SetVisibility(false);
 
-         shape->SetName(name);
-         shape->SetLocalTransform(tm);
+			shape->SetName(name);
+			shape->SetLocalTransform(tm);
+
+			if (Exporter::mCollapseTransforms) {
+				shape->ApplyTransforms();
+			}
+
+			makeSkin(shape, node, grp->second, t);
+
+			if (geomMorpherMod) {
+				vector<Vector3> verts = shape->GetData()->GetVertices();
+				exportGeomMorpherControl(geomMorpherMod, verts, shape);
+				shape->GetData()->SetConsistencyFlags(CT_VOLATILE);
+			}
 
-         if (Exporter::mCollapseTransforms) {
-            shape->ApplyTransforms();
-         }
-         
-         makeSkin(shape, node, grp->second, t);
 		}
 
 		break;
@@ -159,8 +170,8 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
 	if (tri != os.obj)
 		tri->DeleteMe();
 
-   if (copymesh)
-      delete copymesh;
+	if (copymesh)
+		delete copymesh;
 
 	return result;
 }
@@ -193,6 +204,7 @@ NiTriBasedGeomRef Exporter::makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp
 	if (grp.vcolors.size() > 0)
 		data->SetVertexColors(grp.vcolors);
 
+	data->SetConsistencyFlags(CT_STATIC);
 	shape->SetData(data);
 
    if (Exporter::mTangentAndBinormalExtraData)
@@ -277,7 +289,7 @@ void Exporter::addFace(FaceGroup &grp, int face, const int vi[3], Mesh *mesh, co
 	grp.faces.push_back(tri);
 }
 
-bool Exporter::splitMesh(INode *node, Mesh *mesh, FaceGroups &grps, TimeValue t, vector<Color4>& vertColors)
+bool Exporter::splitMesh(INode *node, Mesh& mesh, FaceGroups &grps, TimeValue t, vector<Color4>& vertColors, bool noSplit)
 {
 	Mtl* nodeMtl = node->GetMtl();
 	Matrix3 tm = node->GetObjTMAfterWSM(t);
@@ -301,17 +313,68 @@ bool Exporter::splitMesh(INode *node, Mesh *mesh, FaceGroups &grps, TimeValue t,
 	flip.IdentityMatrix();
 	flip.Scale(Point3(1, -1, 1));
 
-	int i, numSubMtls = nodeMtl?nodeMtl->NumSubMtls():0;
-	for (i=0; i<mesh->getNumFaces(); i++) 
+	if (noSplit)
 	{
-		int mtlID = (numSubMtls!=0) ? (mesh->faces[i].getMatID() % numSubMtls) : 0;
-
-		Matrix3 texm;
-		getTextureMatrix(texm, getMaterial(node, mtlID));
-
-		texm *= flip;
-
-		addFace(grps[mtlID], i, vi, mesh, texm, vertColors);
+		int nv = mesh.getNumVerts();
+		int nf = mesh.getNumFaces();
+		// Dont split the mesh at all.  For debugging purposes.
+		FaceGroup& grp = grps[0];
+		grp.vidx.resize(nv, -1);
+		grp.verts.resize(nv);
+		grp.faces.resize(nf);
+		grp.uvs.resize(nv);
+		grp.vnorms.resize(nv);
+
+		for (int face=0; face<nf; ++face) {
+			for (int vi=0; vi<3; ++vi) {
+				int idx = mesh.faces[face].getVert(vi);
+				grp.faces[face][vi] = idx;
+
+				// Calculate normal
+				Point3 norm;
+#if VERSION_3DSMAX <= ((5000<<16)+(15<<8)+0) // Version 5
+				norm = getVertexNormal(&mesh, face, mesh.getRVertPtr(idx));
+#else
+				MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals ();
+				if (NULL != specNorms && specNorms->GetNumNormals() != 0)
+					norm = specNorms->GetNormal(face, vi);
+				else
+					norm = getVertexNormal(&mesh, face, mesh.getRVertPtr(idx));
+#endif
+				if (grp.vidx[idx] == idx){
+					ASSERT(grp.verts[idx] == TOVECTOR3(mesh.getVert(idx)));
+					//ASSERT(vg.norm == norm);
+					Point3 uv = mesh.getTVert(idx);
+					if (mesh.getNumTVerts() > 0)
+					{
+						uv.y = -uv.y;
+						ASSERT(grp.uvs[idx].u == uv.x && grp.uvs[idx].v == uv.y);
+					}
+				} else {
+					grp.vidx[idx] = idx;
+					grp.verts[idx] = TOVECTOR3(mesh.getVert(idx));
+					Point3 uv = (mesh.getNumTVerts() > 0) ? mesh.getTVert(idx) : Point3(0.0f,0.0f,0.0f);
+					grp.uvs[idx].u = uv.x;
+					grp.uvs[idx].v = -uv.y;
+					grp.vnorms[idx] = TOVECTOR3(norm);
+				}
+			}
+		}
+		for (int i=0; i<nv; ++i) {
+			ASSERT(grp.vidx[i] != -1);
+		}
+	}
+	else
+	{
+		int i, numSubMtls = nodeMtl?nodeMtl->NumSubMtls():0;
+		for (i=0; i<mesh.getNumFaces(); i++) 
+		{
+			int mtlID = (numSubMtls!=0) ? (mesh.faces[i].getMatID() % numSubMtls) : 0;
+			Matrix3 texm;
+			getTextureMatrix(texm, getMaterial(node, mtlID));
+			texm *= flip;
+			addFace(grps[mtlID], i, vi, &mesh, texm, vertColors);
+		}
 	}
 
 	return true;
diff --git a/NifImport/BaseImporter.h b/NifImport/BaseImporter.h
index d36b8c4..12c271e 100644
--- a/NifImport/BaseImporter.h
+++ b/NifImport/BaseImporter.h
@@ -72,7 +72,7 @@ public:
       webSite = GetIniValue<TSTR>("System", "Website", "http://www.niftools.org");
       wikiSite = GetIniValue<TSTR>("System", "Wiki", "http://www.niftools.org/wiki/index.php/3ds_Max");
 
-	  nifVersion = Niflib::GetNifVersion(this->path);
+	  nifVersion = Niflib::GetNifVersion(this->name);
 
       // Load ini settings
       iniFileValid = false;
diff --git a/NifImport/ImportAnimation.cpp b/NifImport/ImportAnimation.cpp
index 0459234..edbead6 100644
--- a/NifImport/ImportAnimation.cpp
+++ b/NifImport/ImportAnimation.cpp
@@ -975,8 +975,26 @@ bool AnimationImport::ImportGeoMorph(INode *n, NiGeomMorpherControllerRef ctrl,
 		return false;
 	vector<NiInterpolatorRef> interpolators = ctrl->GetInterpolators();
 	int nmorphs = data->GetMorphCount();
-	if ((interpolators.size() > nmorphs) || nmorphs == 0)
-		return false;
+	if (ni.nifVersion >= VER_10_1_0_106)
+	{
+		if ((interpolators.size() > nmorphs) || nmorphs == 0)
+			return false;
+	}
+	else
+	{
+		for (int i=0; i<nmorphs; i++)
+		{
+			if (NiFloatInterpolatorRef interp = new NiFloatInterpolator())
+			{
+				NiFloatDataRef fdata = new NiFloatData();
+				fdata->SetKeyType( data->GetMorphKeyType(i) );
+				fdata->SetKeys( data->GetMorphKeys(i) );
+				interp->SetFloatValue(FloatNegINF);
+				interp->SetData(fdata);
+				interpolators.push_back(interp);
+			}
+		}
+	}
 	NiGeometryDataRef geoData = parentGeom->GetData();
 	int nBaseVerts = geoData->GetVertexCount();
 	vector<Triangle> tris;
@@ -986,11 +1004,13 @@ bool AnimationImport::ImportGeoMorph(INode *n, NiGeomMorpherControllerRef ctrl,
 		if (triShapeData == NULL)
 			return false;
 		tris = triShapeData->GetTriangles();
+		baseVerts = triShapeData->GetVertices();
 	} else if (geoData->IsDerivedType(NiTriStripsData::TYPE)) {
 		NiTriStripsDataRef triStripData = StaticCast<NiTriStripsData>(geoData);
 		if (triStripData == NULL)
 			return false;
 		tris = triStripData->GetTriangles();
+		baseVerts = triStripData->GetVertices();
 	} else {
 		return false;
 	}
@@ -1002,23 +1022,15 @@ bool AnimationImport::ImportGeoMorph(INode *n, NiGeomMorpherControllerRef ctrl,
 	n->EvalWorldState(0, TRUE);
 
 	// Create meshes for morph
-	for (int i=0; i<nmorphs; ++i)
+	for (int i=1; i<nmorphs; ++i) // Skip first morph as its the baseline
 	{
 		string frameName = (ni.nifVersion >= VER_10_1_0_106) ? data->GetFrameName(i) : FormatString("Frame #%d", i);
 		vector<Vector3> verts = data->GetMorphVerts(i);
 		if (verts.size() != nBaseVerts)
 			continue;
 
-		// All verts after the first index are differentials
-		if (i == 0)
-		{
-			baseVerts = verts;
-		}
-		else
-		{
-			for (int j=0; j<nBaseVerts; ++j)
-				verts[j] += baseVerts[j];
-		}
+		for (int j=0; j<nBaseVerts; ++j)
+			verts[j] += baseVerts[j];
 
 		TSTR name(frameName.c_str());
 		INode *geoNode = CreateGeoMesh(verts, tris, tm, n);
diff --git a/NifPlugins_VC80.vcproj b/NifPlugins_VC80.vcproj
index e351957..192deaf 100644
--- a/NifPlugins_VC80.vcproj
+++ b/NifPlugins_VC80.vcproj
@@ -124,8 +124,8 @@
 		</Configuration>
 		<Configuration
 			Name="Release - Max 6|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -331,8 +331,8 @@
 		</Configuration>
 		<Configuration
 			Name="Debug - Max 6|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -538,8 +538,8 @@
 		</Configuration>
 		<Configuration
 			Name="Release - Max 7|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -745,8 +745,8 @@
 		</Configuration>
 		<Configuration
 			Name="Debug - Max 7|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -952,8 +952,8 @@
 		</Configuration>
 		<Configuration
 			Name="Release - Max 8|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -1159,8 +1159,8 @@
 		</Configuration>
 		<Configuration
 			Name="Debug - Max 8|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -1364,8 +1364,8 @@
 		</Configuration>
 		<Configuration
 			Name="Debug - Max 5|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -1572,8 +1572,8 @@
 		</Configuration>
 		<Configuration
 			Name="Release - Max 5|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -1780,8 +1780,8 @@
 		</Configuration>
 		<Configuration
 			Name="Debug - Max 9|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -1988,8 +1988,8 @@
 		</Configuration>
 		<Configuration
 			Name="Release - Max 9|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -2198,8 +2198,8 @@
 		</Configuration>
 		<Configuration
 			Name="Release - Max 4|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -2406,8 +2406,8 @@
 		</Configuration>
 		<Configuration
 			Name="Debug - Max 4|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -2612,8 +2612,8 @@
 		</Configuration>
 		<Configuration
 			Name="Debug - gmax|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -2820,8 +2820,8 @@
 		</Configuration>
 		<Configuration
 			Name="Release - gmax|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -3028,8 +3028,8 @@
 		</Configuration>
 		<Configuration
 			Name="Debug - Max 4.2|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
@@ -3236,8 +3236,8 @@
 		</Configuration>
 		<Configuration
 			Name="Release - Max 4.2|x64"
-			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) $(PlatformName)\"
-			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) $(PlatformName)\"
+			OutputDirectory="$(SolutionDir)Staging\$(ConfigurationName) - $(PlatformName)\"
+			IntermediateDirectory="$(SolutionDir)Temp\$(ProjectName)\$(ConfigurationName) - $(PlatformName)\"
 			ConfigurationType="2"
 			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
 			UseOfMFC="0"
-- 
GitLab