From da625014860ca865040a0649152d9fa7ac925ec2 Mon Sep 17 00:00:00 2001
From: Tazpn <tazpn@users.sourceforge.net>
Date: Sun, 24 Jun 2007 00:21:24 +0000
Subject: [PATCH]   0.2.14   ----- o Exporter   - Fix a number of issues with
 bhkConvexShape and bhkRigidBody o Importer   - Introduce Morph animation
 support

---
 MaxNifPlugins_Readme.txt      |   7 +
 MaxNifTools.iss               |   6 +-
 NifCommon/AnimKey.cpp         |   8 +
 NifCommon/AnimKey.h           |   3 +
 NifCommon/NifVersion.h        |   6 +-
 NifCommon/niutils.cpp         |  45 ++-
 NifCommon/niutils.h           |   3 +
 NifExport/Coll.cpp            |  28 +-
 NifExport/Exporter.h          |   2 +
 NifExport/MtlTex.cpp          |   2 +-
 NifExport/Util.cpp            |  60 +++-
 NifImport/BaseImporter.h      |   3 +
 NifImport/ImportAnimation.cpp | 500 ++++++++++++++++++++++++++++++++--
 NifImport/ImportCollision.cpp |   2 +-
 NifImport/NIFImport.cpp       |  13 +-
 NifImport/NIFImporter.h       |   1 +
 16 files changed, 637 insertions(+), 52 deletions(-)

diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt
index 78d85b5..3b3a97e 100644
--- a/MaxNifPlugins_Readme.txt
+++ b/MaxNifPlugins_Readme.txt
@@ -33,6 +33,13 @@
 
     Change log
     ----------
+      0.2.14
+      -----
+    o Exporter
+      - Fix a number of issues with bhkConvexShape and bhkRigidBody
+    o Importer
+      - Introduce Morph animation support
+      
       0.2.13
       -----
     o Properties
diff --git a/MaxNifTools.iss b/MaxNifTools.iss
index 6d4b1d0..da1cb30 100644
--- a/MaxNifTools.iss
+++ b/MaxNifTools.iss
@@ -6,7 +6,7 @@ AppName=NIF Utilities for 3ds Max
 AppVerName=NIF Utilities {code:CurVer} for 3ds Max
 AppPublisher=NIF File Format Library and Tools
 AppCopyright=Copyright © 2007, NIF File Format Library and Tools
-OutputBaseFilename=niftools-max-plugins-0.2.13.1
+OutputBaseFilename=niftools-max-plugins-0.2.14.0
 DisableProgramGroupPage=yes
 Compression=lzma
 SolidCompression=yes
@@ -18,7 +18,7 @@ UninstallFilesDir={win}{\}Installer\NifTools
 Uninstallable=yes
 DisableDirPage=yes
 ArchitecturesInstallIn64BitMode=x64
-VersionInfoVersion=0.2.13.1
+VersionInfoVersion=0.2.14.0
 
 SourceDir=.
 ;UninstallDisplayIcon={app}{\}..\Oblivion.exe
@@ -102,7 +102,7 @@ var sVersion: String;
 
 function InitializeSetup(): Boolean;
 begin
-  sVersion := '0.2.13';
+  sVersion := '0.2.14';
   Result := True;
 end;
 
diff --git a/NifCommon/AnimKey.cpp b/NifCommon/AnimKey.cpp
index 04342e9..2844bd8 100644
--- a/NifCommon/AnimKey.cpp
+++ b/NifCommon/AnimKey.cpp
@@ -475,4 +475,12 @@ bool GetTranslationKeys(Control *c, vector<Vector3Key> keys, const vector<float>
    return false;
 }
 
+void ScaleKey(FloatKey& key, float mult) {
+	key.data *= mult;
+}
 
+void ScaleKeys(vector<FloatKey>&keys, float mult) {
+	for (int i=0, n = keys.size(); i<n; ++i) {
+		ScaleKey(keys[i], mult);
+	}
+}
diff --git a/NifCommon/AnimKey.h b/NifCommon/AnimKey.h
index 593717f..99baf89 100644
--- a/NifCommon/AnimKey.h
+++ b/NifCommon/AnimKey.h
@@ -206,3 +206,6 @@ extern void JoinKeys(vector<Vector3Key>&keys, vector<FloatKey>&xkeys, vector<Flo
 typedef Key<string> KeyTextValue;
 
 bool GetTranslationKeys(Control *c, vector<Vector3Key> keys, const vector<float>& times, float timeOffset=0.0f);
+
+extern void ScaleKeys(vector<FloatKey>&keys, float mult);
+extern void ScaleKey(FloatKey& key, float mult);
diff --git a/NifCommon/NifVersion.h b/NifCommon/NifVersion.h
index 2060d4c..2f97e84 100644
--- a/NifCommon/NifVersion.h
+++ b/NifCommon/NifVersion.h
@@ -18,10 +18,10 @@ HISTORY:
 */
 #define VERSION_MAJOR_INT  0
 #define VERSION_MINOR_INT  2
-#define VERSION_BUILD_INT  13
-#define VERSION_PATCH_INT  1
+#define VERSION_BUILD_INT  14
+#define VERSION_PATCH_INT  0
 
-#define VERSION_STRING "0, 2, 13, 1"
+#define VERSION_STRING "0, 2, 14, 0"
 
 //#define DEF_VERSION_STRING(a,b,c,d) #a ", " #b ", " #c ", " #d
 //#define VERSION_STRING DEF_VERSION_STRING(a,b,c,d)
diff --git a/NifCommon/niutils.cpp b/NifCommon/niutils.cpp
index ff3669f..ade1713 100644
--- a/NifCommon/niutils.cpp
+++ b/NifCommon/niutils.cpp
@@ -1114,7 +1114,6 @@ void GetIniFileName(char *iniName)
 	}
 }
 
-
 Modifier *GetbhkCollisionModifier(INode* node)
 {
 	extern Class_ID BHKRIGIDBODYMODIFIER_CLASS_ID;
@@ -1184,4 +1183,46 @@ Matrix3 GetLocalTM(INode *node)
 	{
 		return node->GetNodeTM(0);
 	}
-}
\ No newline at end of file
+}
+
+Modifier *GetMorpherModifier(INode* node)
+{
+	const Class_ID MORPHERMODIFIER_CLASS_ID(0x17bb6854, 0xa5cba2a3);
+
+	Object* pObj = node->GetObjectRef();
+	if (!pObj) return NULL;
+	while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
+	{
+		IDerivedObject* pDerObj = (IDerivedObject *)(pObj);
+		int Idx = 0;
+		while (Idx < pDerObj->NumModifiers())
+		{
+			// Get the modifier. 
+			Modifier* mod = pDerObj->GetModifier(Idx);
+			if (mod->ClassID() == MORPHERMODIFIER_CLASS_ID)
+			{
+				return mod;
+			}
+			Idx++;
+		}
+		pObj = pDerObj->GetObjRef();
+	}
+	return NULL;
+}
+
+Modifier *CreateMorpherModifier(INode* node)
+{
+	const Class_ID MORPHERMODIFIER_CLASS_ID(0x17bb6854, 0xa5cba2a3);
+
+	Modifier *mod = GetMorpherModifier(node);
+	if (mod == NULL)
+	{
+		IDerivedObject *dobj = CreateDerivedObject(node->GetObjectRef());
+		mod = (Modifier*) CreateInstance(OSM_CLASS_ID, MORPHERMODIFIER_CLASS_ID);
+		dobj->SetAFlag(A_LOCK_TARGET);
+		dobj->AddModifier(mod);
+		dobj->ClearAFlag(A_LOCK_TARGET);
+		node->SetObjectRef(dobj);
+	}
+	return mod;
+}
diff --git a/NifCommon/niutils.h b/NifCommon/niutils.h
index 93e53bc..feef397 100644
--- a/NifCommon/niutils.h
+++ b/NifCommon/niutils.h
@@ -430,4 +430,7 @@ void GetIniFileName(char *iniName);
 
 Matrix3 GetLocalTM(INode *node);
 
+extern Modifier *GetMorpherModifier(INode* node);
+extern Modifier *CreateMorpherModifier(INode* node);
+
 #endif // _NIUTILS_H_
\ No newline at end of file
diff --git a/NifExport/Coll.cpp b/NifExport/Coll.cpp
index b6810ef..22b4a49 100755
--- a/NifExport/Coll.cpp
+++ b/NifExport/Coll.cpp
@@ -184,9 +184,11 @@ Exporter::Result Exporter::exportCollision(NiNodeRef &parent, INode *node)
 			Vector3 trans; Matrix33 rm; float scale;
 			rm4.Decompose(trans, rm, scale);
 
-			QuaternionXYZW q = TOQUATXYZW(rm.AsQuaternion());
-			body->SetRotation(q);
-			body->SetTranslation(trans / Exporter::bhkScaleFactor);
+			body->SetTranslation(TOVECTOR3(Point3(0.0f, 0.0f, 0.0f)));
+			body->SetRotation(TOQUATXYZW(Quat()));
+			//QuaternionXYZW q = TOQUATXYZW(rm.AsQuaternion());
+			//body->SetRotation(q);
+			//body->SetTranslation(trans / Exporter::bhkScaleFactor);
 
 			bhkShapeRef shape = makeCollisionShape(node, tm, body);
 			if (shape)
@@ -367,27 +369,31 @@ bhkConvexVerticesShapeRef Exporter::makeConvexShape(Mesh& mesh, Matrix3& tm)
 	float radius = 0.10f;
 	//CalcAxisAlignedSphere(mesh, center, radius);
 	shape->SetRadius(radius);
-	vector<Vector3> verts, norms;
-	vector<float> dist;
+	vector<Vector3> verts;
+	vector<Float4> norms;
 	int nvert = mesh.getNumVerts();
 	int nface = mesh.getNumFaces();
 
 	verts.resize(nvert);
 	norms.resize(nface);
-	dist.resize(nface);
 	for (int i=0; i<nvert; ++i)
 	{
-		Point3 vert = mesh.getVert(i) / Exporter::bhkScaleFactor;
+		Point3 vert = mesh.getVert(i) / Exporter::bhkScaleFactor * tm;
 		verts[i] = TOVECTOR3(vert);
 	}
 	for (int i=0; i<nface; ++i)
 	{
-		norms[i] = TOVECTOR3(mesh.getFaceNormal(i));
-		dist[i] = -mesh.FaceCenter(i).Length();
+		Float4 &value = norms[i];
+		Point3 &pt = mesh.getFaceNormal(i);
+		value[0] = pt.x;
+		value[1] = pt.y;
+		value[2] = pt.z;
+		value[3] = -mesh.FaceCenter(i).Length();
 	}
+	sortVector3(verts);
+	sortFloat4(norms);
 	shape->SetVertices(verts);
-	shape->SetNormals(norms);
-	shape->SetDistToCenter(dist);
+	shape->SetNormalsAndDist(norms);
 	return shape;
 }
 
diff --git a/NifExport/Exporter.h b/NifExport/Exporter.h
index 7b103da..b729c6d 100755
--- a/NifExport/Exporter.h
+++ b/NifExport/Exporter.h
@@ -282,6 +282,8 @@ public:
 	int countNodes(INode *node);
 	bool isSkeletonRoot(INode *node);
 	void ApplyAllSkinOffsets( NiAVObjectRef & root );
+	void sortVector3(vector<Vector3>& vector);
+	void sortFloat4(vector<Float4>& vector);
 
 	/* Progress Bar stuff */
 	enum ProgressSection
diff --git a/NifExport/MtlTex.cpp b/NifExport/MtlTex.cpp
index c6d66b8..a9ceaae 100755
--- a/NifExport/MtlTex.cpp
+++ b/NifExport/MtlTex.cpp
@@ -18,7 +18,7 @@ void Exporter::makeTexture(NiAVObjectRef &parent, Mtl *mtl)
 	if (!bmTex)
       return;
 
-	 if (Exporter::mNifVersionInt <= VER_4_0_0_0)
+	 if (Exporter::mNifVersionInt >= VER_4_0_0_0)
 	 {
 		 NiTexturingPropertyRef texProp = CreateNiObject<NiTexturingProperty>();
 		 texProp->SetApplyMode(APPLY_MODULATE);
diff --git a/NifExport/Util.cpp b/NifExport/Util.cpp
index 7b831e5..0a1c6c0 100755
--- a/NifExport/Util.cpp
+++ b/NifExport/Util.cpp
@@ -551,4 +551,62 @@ void Exporter::ApplyAllSkinOffsets( NiAVObjectRef & root ) {
          ApplyAllSkinOffsets( children[i] );
       }
    }
-} 
\ No newline at end of file
+} 
+
+struct SortVectorEquivalence
+{
+	inline bool operator()(const Vector3& lhs, const Vector3& rhs) const
+	{
+		float diff = lhs.x - rhs.x;
+		if (diff < 0)
+			return true;
+		if (diff == 0)
+		{
+			diff = lhs.y - rhs.y;
+			if (diff < 0)
+				return true;
+			if (diff == 0)
+			{
+				diff = lhs.z - rhs.z;
+				if (diff < 0)
+					return true;
+			}
+		}
+		return false;
+	}
+	inline bool operator()(const Float4& lhs, const Float4& rhs) const
+	{
+		float diff = lhs[0] - rhs[0];
+		if (diff < 0)
+			return true;
+		if (diff == 0)
+		{
+			diff = lhs[1] - rhs[1];
+			if (diff < 0)
+				return true;
+			if (diff == 0)
+			{
+				diff = lhs[2] - rhs[2];
+				if (diff < 0)
+					return true;
+				if (diff == 0)
+				{
+					diff = lhs[3] - rhs[3];
+					if (diff < 0)
+						return true;
+				}
+			}
+		}
+		return false;
+	}
+};
+
+void Exporter::sortVector3(vector<Vector3>& vector)
+{
+	std::stable_sort(vector.begin(), vector.end(), SortVectorEquivalence());
+}
+
+void Exporter::sortFloat4(vector<Float4>& vector)
+{
+	std::stable_sort(vector.begin(), vector.end(), SortVectorEquivalence());
+}
\ No newline at end of file
diff --git a/NifImport/BaseImporter.h b/NifImport/BaseImporter.h
index e4c7ded..d36b8c4 100644
--- a/NifImport/BaseImporter.h
+++ b/NifImport/BaseImporter.h
@@ -32,6 +32,7 @@ public:
    AppSettings *appSettings;
    TSTR webSite;
    TSTR wikiSite;
+   int nifVersion;
 
    Niflib::NiObjectRef root;
 
@@ -71,6 +72,8 @@ 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);
+
       // Load ini settings
       iniFileValid = false;
       LoadIniSettings();
diff --git a/NifImport/ImportAnimation.cpp b/NifImport/ImportAnimation.cpp
index 54677a0..737961b 100644
--- a/NifImport/ImportAnimation.cpp
+++ b/NifImport/ImportAnimation.cpp
@@ -14,6 +14,7 @@ HISTORY:
 #if VERSION_3DSMAX >= ((7000<<16)+(15<<8)+0) // Version 7
 #  include <IFrameTagManager.h>
 #endif
+#include <maxscrpt/strings.h>
 #include <notetrck.h>
 #include "MaxNifImport.h"
 #include "NIFImporter.h"
@@ -31,6 +32,12 @@ HISTORY:
 #include <obj/NiKeyframeData.h>
 #include <obj/NiStringPalette.h>
 #include <obj/NiBSplineCompTransformInterpolator.h>
+#include <obj/NiGeomMorpherController.h>
+#include <obj/NiMorphData.h>
+#include <obj/NiBSplineCompFloatInterpolator.h>
+#include <obj/NiFloatInterpolator.h>
+#include <obj/NiFloatData.h>
+#include "niutils.h"
 using namespace Niflib;
 
 const Class_ID IPOS_CONTROL_CLASS_ID = Class_ID(0x118f7e02,0xffee238a);
@@ -81,15 +88,27 @@ struct AnimationImport
    bool AddValues(Control *c, NiKeyframeDataRef data, float time);
    bool AddBiped(Control *c, NiKeyframeDataRef data, float time);
 
+   bool AddValues(NiInterpolatorRef interp, IParamBlock* pblock, float time);
+
    Control* MakePosition(Control *tmCont, Class_ID clsid);
    Control* MakePositionXYZ(Control *tmCont, Class_ID clsid);
    Control* MakeRotation(Control *tmCont, Class_ID rotClsid, Class_ID rollClsid);
    Control* MakeScale(Control *tmCont, Class_ID clsid);
+   Control* MakeFloat(IParamBlock* pblock, int idx, Class_ID clsid);
 
+   
    Control* GetTMController(const string& name);
+   Control* GetTMController(NiObjectNETRef node);
    Matrix3 GetTM(const string& name, TimeValue t = 0);
 
    bool GetTransformData(ControllerLink& lnk, string name, NiKeyframeDataRef& outData, Point3& p, Quat& q, float& s);
+
+   bool ImportGeoMorph(INode *n, NiGeomMorpherControllerRef ctrl, float time);
+   INode* CreateGeoMesh(const vector<Vector3>& verts, const vector<Triangle>& tris, Matrix3& tm, INode *parent);
+
+   void MorpherBuildFromNode(Modifier* mod, int index, INode *target);
+   void MorpherSetName(Modifier* mod, int index, TSTR& name);
+   void MorpherRebuild(Modifier* mod, int index);
 };
 
 bool NifImporter::ImportAnimation()
@@ -485,26 +504,69 @@ bool KFMImporter::ImportAnimation()
             name = name.substr(0, name.length() - 9);
          }
 
-         Control *c = ai.GetTMController(name);
-         if (NULL == c)
-            continue;
-
-         INode *n = gi->GetINodeByName(name.c_str());
-
-         if ((*lnk).priority_ != 0.0f) {
-            npSetProp(n, NP_ANM_PRI, (*lnk).priority_);
-         }
-
-         NiKeyframeDataRef data;
-         Point3 p; Quat q; float s;
-         if (ai.GetTransformData(*lnk, name, data, p, q, s)) {
-            PosRotScaleNode(n, p, q, s, prsDefault, 0);
-            if (ai.AddValues(c, data, time)) {
-               minTime = min(minTime, start);
-               maxTime = max(maxTime, stop);
-               ok = true;
-            }
-         }
+		 string type = (*lnk).controllerType;
+		 if (type.empty()) {
+			 NiStringPaletteRef strings = lnk->stringPalette;
+			 type = strings->GetSubStr((*lnk).controllerTypeOffset);
+		 }
+		 if (type.empty())
+			 continue;
+
+		 if (strmatch(type, "NiTransformController"))
+		 {
+			 Control *c = ai.GetTMController(name);
+			 if (NULL == c)
+				 continue;
+
+			 INode *n = gi->GetINodeByName(name.c_str());
+
+			 if ((*lnk).priority_ != 0.0f) {
+				 npSetProp(n, NP_ANM_PRI, (*lnk).priority_);
+			 }
+
+			 NiKeyframeDataRef data;
+			 Point3 p; Quat q; float s;
+			 if (ai.GetTransformData(*lnk, name, data, p, q, s)) {
+				 PosRotScaleNode(n, p, q, s, prsDefault, 0);
+				 if (ai.AddValues(c, data, time)) {
+					 minTime = min(minTime, start);
+					 maxTime = max(maxTime, stop);
+					 ok = true;
+				 }
+			 }
+		 }
+		 else if (strmatch(type, "NiGeomMorpherController"))
+		 {
+			 string var2 = (*lnk).variable2;
+			 if (var2.empty()) {
+				 if (NiStringPaletteRef strings = lnk->stringPalette)
+					 var2 = strings->GetSubStr((*lnk).variableOffset2);
+			 }
+			 if (!var2.empty())
+			 {
+				 if (INode *n = gi->GetINodeByName(name.c_str()))
+				 {
+					 if (Modifier* mod = GetMorpherModifier(n))
+					 {
+						 int idx = -1;
+						 for (int i=1; i<=100; ++i) {
+							 if (strmatch(var2, mod->SubAnimName(i))) {
+								 idx = i;
+								 break;
+							 }
+						 }
+						 if (idx != -1)
+						 {
+							 if (ai.AddValues(lnk->interpolator, (IParamBlock*)mod->GetReference(idx), time)) {
+								 minTime = min(minTime, start);
+								 maxTime = max(maxTime, stop);
+								 ok = true;
+							 }
+						 }
+					 }
+				 }
+			 }
+		 }
       }
       if (maxTime > minTime && maxTime > 0.0f)
          time += (maxTime-minTime) + FramesIncrement;
@@ -578,7 +640,7 @@ bool AnimationImport::GetTransformData(ControllerLink& lnk, string name, NiKeyfr
             s = FloatNegINF;
             return true;
          }
-      }
+	  }
    }
    return false;
 }
@@ -597,6 +659,18 @@ Control *AnimationImport::GetTMController(const string& name)
    return c;
 }
 
+Control *AnimationImport::GetTMController(NiObjectNETRef obj)
+{
+	if (obj->IsDerivedType(NiNode::TYPE))
+	{
+		NiNodeRef node = StaticCast<NiNode>(obj);
+		if (INode *n = ni.GetNode(node)) {
+			return n->GetTMController();
+		}
+	}
+	return GetTMController(obj->GetName().c_str());
+}
+
 Matrix3 AnimationImport::GetTM(const string& name, TimeValue t)
 {
    INode *n = ni.gi->GetINodeByName(name.c_str());
@@ -628,28 +702,33 @@ bool AnimationImport::AddValues(vector<NiObjectNETRef>& nodes)
 
 bool AnimationImport::AddValues(NiObjectNETRef nref)
 {
-   Control *c = GetTMController(nref->GetName().c_str());
-   if (NULL == c)
-      return false;
-
    if (NiTextKeyExtraDataRef keydata = SelectFirstObjectOfType<NiTextKeyExtraData>(nref->GetExtraData())) {
       ni.AddNoteTracks(0.0f, string(), nref->GetName(), keydata, false);
    }
 
+   bool ok = false;
    float time = 0.0f;
    list< NiTimeControllerRef > clist = nref->GetControllers();
    if (NiTransformControllerRef tc = SelectFirstObjectOfType<NiTransformController>(clist)) {
       if (NiTransformInterpolatorRef interp = tc->GetInterpolator()) {
          if (NiTransformDataRef data = interp->GetData()) {
-            return AddValues(c, data, time);
+			 if (Control *c = GetTMController(nref))
+	            ok |= AddValues(c, data, time);
          }
       }
    } else if (NiKeyframeControllerRef kf = SelectFirstObjectOfType<NiKeyframeController>(clist)) {
       if (NiKeyframeDataRef kfData = kf->GetData()) {
-         return AddValues(c, kfData, time);
+		  if (Control *c = GetTMController(nref))
+	         ok |= AddValues(c, kfData, time);
       }
    }
-   return false;
+   if (NiGeomMorpherControllerRef gmc = SelectFirstObjectOfType<NiGeomMorpherController>(clist)) {
+	   if (INode *n = ni.GetNode(nref)) {
+		   ok |= ImportGeoMorph(n, gmc, time);
+	   }
+   }
+
+   return ok;
 }
 
 bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time)
@@ -818,6 +897,23 @@ Control* AnimationImport::MakeScale(Control *tmCont, Class_ID clsid)
    }
    return NULL;
 }
+Control* AnimationImport::MakeFloat(IParamBlock* pblock, int idx, Class_ID clsid)
+{
+	Interface *ip = ni.gi;
+	if (Control *c = pblock->GetController(idx)) {
+		if (c->ClassID()!=clsid) {
+			if (Control *tmpCtrl = (Control*)ip->CreateInstance(CTRL_SCALE_CLASS_ID, clsid)){
+				pblock->SetController(idx, tmpCtrl);
+				if (pblock->GetController(idx) != tmpCtrl)
+					tmpCtrl->DeleteThis();
+				else
+					c = tmpCtrl;
+			}
+		}
+		return c;
+	}
+	return NULL;
+}
 
 Control* AnimationImport::MakePosition(Control *tmCont, Class_ID clsid)
 {
@@ -870,4 +966,350 @@ bool AnimationImport::AddBiped(Control *c, NiKeyframeDataRef data, float time)
 {
    return false;
 }
-#endif
\ No newline at end of file
+#endif
+
+bool AnimationImport::ImportGeoMorph(INode *n, NiGeomMorpherControllerRef ctrl, float time)
+{
+	NiObjectNETRef target = ctrl->GetTarget();
+	if (!target->IsDerivedType(NiTriBasedGeom::TYPE))
+		return false;
+	NiTriBasedGeomRef parentGeom = StaticCast<NiTriBasedGeom>(target);
+	NiMorphDataRef data = ctrl->GetData();
+	if (data == NULL)
+		return false;
+	vector<NiInterpolatorRef> interpolators = ctrl->GetInterpolators();
+	int nmorphs = data->GetMorphCount();
+	if ((interpolators.size() > nmorphs) || nmorphs == 0)
+		return false;
+	NiGeometryDataRef geoData = parentGeom->GetData();
+	int nBaseVerts = geoData->GetVertexCount();
+	vector<Triangle> tris;
+	vector<Vector3> baseVerts;
+	if (geoData->IsDerivedType(NiTriShapeData::TYPE)) {
+		NiTriShapeDataRef triShapeData = StaticCast<NiTriShapeData>(geoData);
+		if (triShapeData == NULL)
+			return false;
+		tris = triShapeData->GetTriangles();
+	} else if (geoData->IsDerivedType(NiTriStripsData::TYPE)) {
+		NiTriStripsDataRef triStripData = StaticCast<NiTriStripsData>(geoData);
+		if (triStripData == NULL)
+			return false;
+		tris = triStripData->GetTriangles();
+	} else {
+		return false;
+	}
+	Matrix3 tm = TOMATRIX3(parentGeom->GetLocalTransform());
+	tm.Invert();
+	//tm.NoRot(); tm.NoTrans(); // Leave scale.  Parent to owner mesh
+
+	Modifier *mod = CreateMorpherModifier(n);
+	n->EvalWorldState(0, TRUE);
+
+	// Create meshes for morph
+	for (int i=0; i<nmorphs; ++i)
+	{
+		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];
+		}
+
+		TSTR name(frameName.c_str());
+		INode *geoNode = CreateGeoMesh(verts, tris, tm, n);
+		geoNode->SetName(FormatText("Morph: %s", name));
+
+		MorpherBuildFromNode(mod, i+1, geoNode);
+		MorpherSetName(mod, i+1, name);
+		n->EvalWorldState(0, TRUE);
+
+		AddValues(interpolators[i], (IParamBlock*)mod->GetReference(i+1), time);
+	}
+	return false;
+}
+
+bool AnimationImport::AddValues(NiInterpolatorRef interp, IParamBlock* pblock, float time)
+{
+	bool retval = false;
+	// Handle Translation
+	//
+	KeyType keyType = UNKNOWN_KEY;
+	vector<FloatKey> keys;
+	if (interp->IsDerivedType(NiFloatInterpolator::TYPE)) {
+		if (NiFloatDataRef data = StaticCast<NiFloatInterpolator>(interp)->GetData())
+		{
+			keyType = data->GetKeyType();
+			keys = data->GetKeys();
+		}
+	} else if (interp->IsDerivedType(NiBSplineCompFloatInterpolator::TYPE)) {
+		NiBSplineCompFloatInterpolatorRef bsfi = StaticCast<NiBSplineCompFloatInterpolator>(interp);
+		int npoints = bsfi->GetNumControlPt();
+		if (npoints > 3) {
+			keyType = QUADRATIC_KEY;
+			keys = bsfi->SampleKeys(npoints, 3);
+		}			
+	}
+	if (keyType != UNKNOWN_KEY)
+	{
+		ScaleKeys(keys, 100.0f);
+		switch (keyType)
+		{
+		case LINEAR_KEY:
+			if (Control *subCtrl = MakeFloat(pblock, 0, Class_ID(LININTERP_FLOAT_CLASS_ID,0))) {
+				MergeKeys<ILinFloatKey, FloatKey>(subCtrl, keys, time);
+				retval |= true;
+			}
+			break;
+		case QUADRATIC_KEY:
+		case XYZ_ROTATION_KEY:
+			if (Control *subCtrl = MakeFloat(pblock, 0, Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) {
+				MergeKeys<IBezFloatKey, FloatKey>(subCtrl, keys, time);
+				retval |= true;
+			}
+			break;
+		case TBC_KEY:
+			if (Control *subCtrl = MakeFloat(pblock, 0, Class_ID(TCBINTERP_FLOAT_CLASS_ID,0))) {
+				MergeKeys<ITCBFloatKey, FloatKey>(subCtrl, keys, time);
+				retval |= true;
+			}
+			break;
+		}
+	}
+	return retval;
+}
+
+INode *AnimationImport::CreateGeoMesh(
+	const vector<Vector3>& verts, 
+	const vector<Triangle>& tris,
+	Matrix3& tm,
+	INode *parent
+	)
+{
+	INode *returnNode = NULL;
+	if ( ImpNode *node = ni.i->CreateNode() )
+	{
+		TriObject *triObject = CreateNewTriObject();
+		node->Reference(triObject);
+
+		Mesh& mesh = triObject->GetMesh();
+		INode *tnode = node->GetINode();
+
+		// Vertex info
+		{
+			int nVertices = verts.size();
+			mesh.setNumVerts(nVertices);
+			for (int i=0; i < nVertices; ++i){
+				const Vector3& v = verts[i];
+				mesh.verts[i].Set(v.x, v.y, v.z);
+			}
+		}
+
+		// Triangles and texture vertices
+		ni.SetTriangles(mesh, tris);
+
+		//MNMesh mn(mesh);
+		//mn.OutToTri(mesh);
+		mesh.checkNormals(TRUE);
+
+		// Wireframe Red color
+		StdMat2 *collMat = NewDefaultStdMat();
+		collMat->SetDiffuse(Color(0.0f, 1.0f, 0.0f), 0);
+		collMat->SetWire(TRUE);
+		collMat->SetFaceted(TRUE);
+		ni.gi->GetMaterialLibrary().Add(collMat);
+		tnode->SetMtl(collMat);
+		tnode->SetRenderable(FALSE);
+		tnode->SetPrimaryVisibility(FALSE);
+		tnode->SetSecondaryVisibility(FALSE);
+		tnode->SetWireColor( RGB(0,255,0) );
+
+		returnNode = node->GetINode();
+		
+		//PosRotScaleNode(returnNode, tm, prsDefault, 0);
+		if (parent != NULL)
+			parent->AttachChild(tnode, 1);
+	}
+	return returnNode;
+}
+
+// CallMaxscript
+// Send the string to maxscript 
+//
+void AnimationImport::MorpherBuildFromNode(Modifier* mod, int index, INode *target)
+{
+	// 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, target, 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_BuildFromNode"));
+		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.
+		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);
+			args[2] = vl.target = MAXNode::intern(target);
+
+			// Call the function and save the result.
+			vl.result = static_cast<Primitive*>(vl.fn)->apply(args, 3);
+		}
+	} 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();
+}
+
+void AnimationImport::MorpherSetName(Modifier* mod, int index, TSTR& name)
+{
+	// 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, value, result);
+	save_current_frames();
+	trace_back_active = FALSE;
+	String* value = new String(name);
+
+	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_SetName"));
+		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);
+			args[2] = vl.value = value;
+
+			// Call the function and save the result.
+			vl.result = static_cast<Primitive*>(vl.fn)->apply(args, 3);
+		}
+	} catch (...) {
+		value->collect();
+		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();
+}
+
+void AnimationImport::MorpherRebuild(Modifier* mod, int index)
+{
+	// 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_Rebuild"));
+		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);
+		}
+	} 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();
+}
diff --git a/NifImport/ImportCollision.cpp b/NifImport/ImportCollision.cpp
index d36cc4d..6400a53 100644
--- a/NifImport/ImportCollision.cpp
+++ b/NifImport/ImportCollision.cpp
@@ -108,7 +108,7 @@ bool NifImporter::ImportCollision(NiNodeRef node)
 				   Matrix3 tm(true);
 				   if (!ci.ImportShape(body, rbody, shape, node, tm))
 				   {
-					   body->Delete(0, 1);
+					   gi->DeleteNode(body, FALSE);
 				   }
 			   }
 		   }
diff --git a/NifImport/NIFImport.cpp b/NifImport/NIFImport.cpp
index 53cb702..c668025 100644
--- a/NifImport/NIFImport.cpp
+++ b/NifImport/NIFImport.cpp
@@ -280,6 +280,17 @@ INode *NifImporter::GetNode(Niflib::NiNodeRef node)
 	return FindNode(node);
 }
 
+INode *NifImporter::GetNode(Niflib::NiObjectNETRef obj)
+{
+	if (obj->IsDerivedType(NiNode::TYPE)) {
+		NiNodeRef node = StaticCast<NiNode>(obj);
+		if (INode *n = GetNode(node)) {
+			return n;
+		}
+	}
+	return gi->GetINodeByName(obj->GetName().c_str());
+}
+
 bool NifImporter::DoImport()
 {
    bool ok = true;
@@ -353,7 +364,7 @@ bool NifImporter::DoImport()
                importedNodes.begin(), importedNodes.end(), results.begin());
             for (vector<string>::iterator itr = results.begin(); itr != end; ++itr){
                if (INode *node = gi->GetINodeByName((*itr).c_str())){
-                  node->Delete(0, TRUE);
+				   gi->DeleteNode(node, FALSE);
                }
             }
          }
diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h
index d16023b..0d36d9f 100644
--- a/NifImport/NIFImporter.h
+++ b/NifImport/NIFImporter.h
@@ -144,6 +144,7 @@ public:
    INode *FindNode(Niflib::NiObjectNETRef node);
 
    INode *GetNode(Niflib::NiNodeRef node);
+   INode *GetNode(Niflib::NiObjectNETRef obj);
    string GetSkeleton(AppSettings *appSettings);
 
    bool ShowDialog();
-- 
GitLab