From 8c3c70496dfbf0576d57a2c37937dd783b14105f Mon Sep 17 00:00:00 2001
From: Tazpn <tazpn@users.sourceforge.net>
Date: Sun, 2 Jul 2006 13:53:41 +0000
Subject: [PATCH] Fix skin rotations once again.  Still having issues with Civ4
 and DAoC not quite lining up.

---
 NifImport/ImportMeshAndSkin.cpp |  16 +-
 NifImport/ImportSkeleton.cpp    | 417 +++++++++++++++++++++-----------
 NifImport/MaxNifTools.ini       |   4 +-
 NifImport/NIFImporter.h         |   5 +-
 4 files changed, 281 insertions(+), 161 deletions(-)

diff --git a/NifImport/ImportMeshAndSkin.cpp b/NifImport/ImportMeshAndSkin.cpp
index 5287ba3..6ae097e 100644
--- a/NifImport/ImportMeshAndSkin.cpp
+++ b/NifImport/ImportMeshAndSkin.cpp
@@ -312,12 +312,9 @@ bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom)
    if (ISkin *skin = (ISkin *) skinMod->GetInterface(I_SKIN)){
       ISkinImportData* iskinImport = (ISkinImportData*) skinMod->GetInterface(I_SKINIMPORTDATA);
 
-      Matrix3 m3(TRUE);
-      if (applyOverallTransformToSkinAndBones) {
-         m3 = TOMATRIX3(data->GetOverallTransform());
-         Matrix3 im3 = Inverse(m3);
-         iskinImport->SetSkinTm(tnode, im3, im3); // ???
-      }
+      Matrix3 m3 = TOMATRIX3(data->GetOverallTransform());
+      Matrix3 im3 = Inverse(m3);
+      iskinImport->SetSkinTm(tnode, m3, m3); // ???
       // Create Bone List
       Tab<INode*> bones;
       for (size_t i=0; i<nifBones.size(); ++i){
@@ -331,9 +328,10 @@ bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom)
             iskinImport->AddBoneEx(boneRef, TRUE);
 
             // Set Bone Transform
-            if (applyOverallTransformToSkinAndBones) {
-               iskinImport->SetBoneTm(boneRef, ib3, ib3);
-            }
+            Matrix3 tm = ib3;
+            if (applyOverallTransformToSkinAndBones)
+               ib3 *= im3;
+            iskinImport->SetBoneTm(boneRef, ib3, ib3);
          }
       }
       ObjectState os = tnode->EvalWorldState(0);
diff --git a/NifImport/ImportSkeleton.cpp b/NifImport/ImportSkeleton.cpp
index 54bc729..b2d0110 100644
--- a/NifImport/ImportSkeleton.cpp
+++ b/NifImport/ImportSkeleton.cpp
@@ -12,6 +12,7 @@ HISTORY:
 **********************************************************************/
 #include "stdafx.h"
 #include "MaxNifImport.h"
+//#include <cs/Biped8Api.h>
 #include <obj/NiTriBasedGeom.h>
 #include <obj/NiTriBasedGeomData.h>
 #include <obj/NiTimeController.h>
@@ -41,6 +42,43 @@ void GoToSkeletonBindPosition(vector<NiNodeRef>& blocks)
    }
 }
 
+float GetObjectLength(NiAVObjectRef obj)
+{
+   float clen = obj->GetLocalTranslation().Magnitude();
+   if (clen < (FLT_EPSILON*10)) {
+      if (NiTriBasedGeomRef geom = DynamicCast<NiTriBasedGeom>(obj)) {
+         if (NiTriBasedGeomDataRef data = geom->GetData()) {
+            clen = data->GetRadius() * 2.0f;
+         }
+      }
+   }
+   return clen;
+}
+
+static void BuildControllerRefList(NiNodeRef node, map<string,int>& ctrlCount)
+{
+   list<NiTimeControllerRef> ctrls = node->GetControllers();
+   for (list<NiTimeControllerRef>::iterator itr = ctrls.begin(), end = ctrls.end(); itr != end; ++itr) {
+      list<NiNodeRef> nlist = DynamicCast<NiNode>((*itr)->GetRefs());
+
+      // Append extra targets.  Goes away if GetRefs eventually returns the extra targets
+      if (NiMultiTargetTransformControllerRef multiCtrl = DynamicCast<NiMultiTargetTransformController>(*itr)) {
+         vector<NiNodeRef> extra = multiCtrl->GetExtraTargets();
+         nlist.insert(nlist.end(), extra.begin(), extra.end());
+      }
+
+      for (list<NiNodeRef>::iterator nitr = nlist.begin(); nitr != nlist.end(); ++nitr){
+         string name = (*nitr)->GetName();
+         map<string,int>::iterator citr = ctrlCount.find(name);
+         if (citr != ctrlCount.end())
+            ++(*citr).second;
+         else
+            ctrlCount[name] = 1;
+      }
+   }
+}
+
+
 bool NifImporter::HasSkeleton()
 {
    if (!skeletonCheck.empty()){
@@ -54,9 +92,12 @@ bool NifImporter::HasSkeleton()
 bool NifImporter::IsBiped()
 {
    if (hasSkeleton){
-      list<NiExtraDataRef> extraData = nodes[0]->GetExtraData();
-      if (!extraData.empty()) {
-         return ( DynamicCast<BSBound>(extraData).size() != 0 );
+      NiNodeRef rootNode = root;
+      if (rootNode){
+         list<NiExtraDataRef> extraData = rootNode->GetExtraData();
+         if (!extraData.empty()) {
+            return ( SelectFirstObjectOfType<BSBound>(extraData) != NULL );
+         }
       }
    }
    return false;
@@ -115,25 +156,33 @@ void NifImporter::ImportBipeds(vector<NiNodeRef>& nodes)
             numfingers, nfinglinks, numtoes, ntoelinks, bipedAnkleAttach, prop1exists, 
             prop2exists, prop3exists, forearmTwistLinks, upperarmTwistLinks, thighTwistLinks,
             calfTwistLinks, horseTwistLinks);
-         master->SetRootName(const_cast<TCHAR*>(bipname.c_str()));
          if (master)
          {
+            master->SetRootName(const_cast<TCHAR*>(bipname.c_str()));
+            
+            //if (INode *rootNode = gi->GetINodeByName(bipname.c_str())) {
+            //   if (Control *c = rootNode->GetTMController()) {
+            //      if (IBipMaster8 *master8 = GetBipMaster8Interface(c)) {
+            //         bool ok = master8->SetEulerActive(KEY_LARM, EULERTYPE_XYZ);
+            //         ok = ok;
+            //      }
+            //   }
+            //}
             master->BeginModes(BMODE_FIGURE, FALSE);
             master->SetTrianglePelvis(FALSE);
             master->SetDisplaySettings(BDISP_BONES);
             LPCTSTR bipname = master->GetRootName();
 
-            // Rename twists, if necessary
+            // Rename twists, if necessary for Oblivion
             RenameNode(gi, FormatText("%s L ForeTwist", bipname), FormatText("%s L ForearmTwist", bipname));
             RenameNode(gi, FormatText("%s R ForeTwist", bipname), FormatText("%s R ForearmTwist", bipname));
             RenameNode(gi, FormatText("%s R LUpArmTwist", bipname), FormatText("%s L UpperArmTwist", bipname));
             RenameNode(gi, FormatText("%s R LUpArmTwist", bipname), FormatText("%s R UpperArmTwist", bipname));
 
-            NiNodeRef nifBip = FindNodeByName(nodes, bipname);
-            //AlignBiped(master, nifBip);
-            ScaleBiped(master, nifBip, true);
-            PositionBiped(master, nifBip, true);
-            RotateBiped(master, nifBip, true);
+            if (NiNodeRef nifBip = FindNodeByName(nodes, bipname))
+            {
+               AlignBiped(master, nifBip);
+            }
          }
       }
    }
@@ -148,146 +197,257 @@ void NifImporter::ImportBipeds(vector<NiNodeRef>& nodes)
       master->EndModes(BMODE_FIGURE, TRUE);
 }
 
-void NifImporter::AlignBiped(IBipMaster* master, NiNodeRef block)
+static float CalcLength(NiNodeRef node, vector<NiAVObjectRef>& children)
 {
-   string name = block->GetName();
-   Matrix44 wt = block->GetWorldTransform();
-   Matrix44 lt = block->GetLocalTransform();
-
-   Vector3 pos; Matrix33 rot; float scale;
-   wt.Decompose(pos, rot, scale);
-
-   INode *node = gi->GetINodeByName(name.c_str());
-   if (node != NULL) 
-   {
-      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
-      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
-
-      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
-      Matrix3 im = Inverse(m);
-
-      Point3 p; Quat q; Point3 s;
-      DecomposeMatrix(im, p, q, s);
-      master->SetBipedRot(q, 0, node, TRUE);
+   bool hasChildren = !children.empty();
+   float len = 0.0f;
+   if (hasChildren) {
+      for (vector<NiAVObjectRef>::iterator itr=children.begin(), end = children.end(); itr != end; ++itr) {
+         len += GetObjectLength(*itr);
+      }
+      len /= float(children.size());
    }
    else
    {
-
-   }
-   vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
-   for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
-      AlignBiped(master, *itr);
+      len = node->GetLocalTranslation().Magnitude();
    }
+   return len;
 }
 
-void NifImporter::PositionBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
+Matrix3 GetLocalTM(INode *node)
 {
-   string name = block->GetName();
-
-   Matrix44 wt = block->GetWorldTransform();
-   Matrix44 lt = block->GetLocalTransform();
-
-   Vector3 pos; Matrix33 rot; float scale;
-   wt.Decompose(pos, rot, scale);
-
-   INode *node = gi->GetINodeByName(name.c_str());
-   if (node != NULL) 
+   if (INode *parent = node->GetParentNode())
    {
-      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
-      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
-
-      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
-      Matrix3 im = Inverse(m);
-      Point3 p; Quat q; Point3 s;
-      DecomposeMatrix(im, p, q, s);
-      master->SetBipedRot(q, 0, node, TRUE);
+      Matrix3 parentTM, nodeTM;
+      nodeTM = node->GetNodeTM(0);
+      parent = node->GetParentNode();
+      parentTM = parent->GetNodeTM(0);
+      return nodeTM*Inverse(parentTM);
    }
    else
    {
-
+      return node->GetNodeTM(0);
    }
-   if (Recurse)
+}
+
+static float CalcLength(INode *bone)
+{
+   int n = bone->NumberOfChildren();
+   float len = 0.0f;
+   if (n > 0) 
    {
-      vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
-      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
-         PositionBiped(master, *itr, Recurse);
+      Matrix3 m = bone->GetNodeTM(0);
+      Point3 p = m.GetTrans();
+      for (int i = 0; i<n; i++)
+      {
+         INode *child = bone->GetChildNode(i);
+         Matrix3 cm = child->GetObjectTM(0);
+         Point3 cp = cm.GetTrans();
+
+         float clen = Length(p-cp);
+         len += clen;
       }
+      len /= float(n);
    }
+   else
+   {
+      len = Length(GetLocalTM(bone).GetTrans());
+   }
+   return len;
 }
 
-void NifImporter::RotateBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
+static bool HasBipedPosDOF(LPCTSTR name)
 {
-   string name = block->GetName();
-
-   Matrix44 wt = block->GetWorldTransform();
-   Matrix44 lt = block->GetLocalTransform();
-
-   Vector3 pos; Matrix33 rot; float scale;
-   wt.Decompose(pos, rot, scale);
+   // Check for specific nodes to ignore.  
+   //   These are nodes which have a full translation DOF and 
+   //   there for do not effect the scale value we are toying with
+   return (  wildcmpi("Bip?? ? Clavicle", name) 
+          || wildcmpi("Bip?? ? Toe?", name) 
+          || wildcmpi("Bip?? ? Finger?", name) 
+          || wildcmpi("Bip?? *Twist*", name) 
+          );
+}
 
-   INode *node = gi->GetINodeByName(name.c_str());
-   if (node != NULL) 
+static float CalcScale(INode *bone, NiNodeRef node, vector<NiNodeRef>& children)
+{
+   int n = bone->NumberOfChildren();
+   if (n > 0) 
    {
-      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
-      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
+      float len1 = 0.0f;
+      float len2 = 0.0f;
+      Matrix3 m = bone->GetNodeTM(0);
+      Matrix3 m2 = TOMATRIX3(node->GetWorldTransform());
+      for (int i = 0; i<n; i++)
+      {
+         INode *child = bone->GetChildNode(i);
+         LPCTSTR name = child->GetName();
+         if (HasBipedPosDOF(name))
+            continue;
 
-      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
-      Matrix3 im = Inverse(m);
+         Matrix3 cm = child->GetObjectTM(0);
+         len1 += Length(m.GetTrans()-cm.GetTrans());
 
-      Point3 p; Quat q; Point3 s;
-      DecomposeMatrix(im, p, q, s);
-      master->SetBipedRot(q, 0, node, TRUE);
+         if (NiNodeRef child2 = FindNodeByName(children, string(child->GetName()))){
+            Matrix3 cm2 = TOMATRIX3(child2->GetWorldTransform());
+            len2 += Length(m2.GetTrans()-cm2.GetTrans());
+         }
+      }
+      return (len2 != 0.0f && len1 != 0.0f) ? (len2/len1) : 1.0f;
    }
-   else
-   {
+   return 1.0f;
+}
 
-   }
-   if (Recurse)
+void PosRotScaleBiped(IBipMaster* master, INode *n, Point3 p, Quat& q, float s, PosRotScale prs, TimeValue t = 0)
+{
+   if (prs & prsScale)
+      master->SetBipedScale(TRUE, ScaleValue(Point3(s,s,s)), t, n);
+   if (prs & prsRot)
+      master->SetBipedRot(q, t, n, FALSE);
+   if (prs & prsPos)
+      master->SetBipedPos(p, t, n, FALSE);
+}
+AngAxis CalcAngAxis(Point3 vs, Point3 vf)
+{
+   Point3 cross = CrossProd(vs, vf);
+   // Test for colinear
+   if (cross.x < 0.01 && cross.y < 0.01 && cross.z < 0.01)
+      return AngAxis(Matrix3(TRUE));
+   float dot = DotProd(vs, vf);
+   return AngAxis( cross, acos( dot ) );
+}
+Matrix3 GenerateRotMatrix(AngAxis a)
+{
+   Matrix3 m(TRUE);
+   float u = a.axis.x;
+   float v = a.axis.y;
+   float w = a.axis.z;
+   float rcos = cos(a.angle);
+   float rsin = sin(a.angle);
+   m[0][0] =      rcos + u*u*(1-rcos);
+   m[1][0] =  w * rsin + v*u*(1-rcos);
+   m[2][0] = -v * rsin + w*u*(1-rcos);
+   m[0][1] = -w * rsin + u*v*(1-rcos);
+   m[1][1] =      rcos + v*v*(1-rcos);
+   m[2][1] =  u * rsin + w*v*(1-rcos);
+   m[0][2] =  v * rsin + u*w*(1-rcos);
+   m[1][2] = -u * rsin + v*w*(1-rcos);
+   m[2][2] =      rcos + w*w*(1-rcos);
+   return m;
+}
+static AngAxis CalcTransform(INode *bone, NiNodeRef node, vector<NiNodeRef>& children)
+{
+   Matrix3 mr(TRUE);
+   int n = bone->NumberOfChildren();
+   if (n > 0) 
    {
-      vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
-      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
-         RotateBiped(master, *itr, Recurse);
+      int c = 0;
+      Point3 vs(0.0f, 0.0f, 0.0f), vf(0.0f, 0.0f, 0.0f);
+      Matrix3 m = bone->GetNodeTM(0);
+      Matrix3 m2 = TOMATRIX3(node->GetWorldTransform());
+      for (int i = 0; i<n; i++)
+      {
+         INode *child = bone->GetChildNode(i);
+         LPCTSTR name = child->GetName();
+         if (HasBipedPosDOF(name))
+            continue;
+
+         Matrix3 cm = child->GetObjectTM(0);
+         vs += (m.GetTrans()-cm.GetTrans());
+
+         if (NiNodeRef child2 = FindNodeByName(children, string(child->GetName()))){
+            Matrix3 cm2 = TOMATRIX3(child2->GetWorldTransform());
+            vf += (m2.GetTrans()-cm2.GetTrans());
+         }
+         ++c;
       }
+      vs = FNormalize(vs); 
+      vf = FNormalize(vf);
+      Point3 cross = CrossProd(vs, vf);
+      if (fabs(cross.x) < 0.01 && fabs(cross.y) < 0.01 && fabs(cross.z) < 0.01)
+         return AngAxis(Point3(0.0f, 0.0f, 0.0f), 0.0f);
+      float dot = DotProd(vs, vf);
+      return AngAxis( cross, acos( dot ) );
    }
+   return mr;
 }
 
-void NifImporter::ScaleBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
-{
-   string name = block->GetName();
 
-   Matrix44 wt = block->GetWorldTransform();
-   Matrix44 lt = block->GetLocalTransform();
 
-   Vector3 pos; Matrix33 rot; float scale;
-   wt.Decompose(pos, rot, scale);
-
-   INode *node = gi->GetINodeByName(name.c_str());
-   if (node != NULL) 
+void NifImporter::AlignBiped(IBipMaster* master, NiNodeRef node)
+{
+   NiNodeRef parent = node->GetParent();
+   string name = node->GetName();
+   vector<NiAVObjectRef> children = node->GetChildren();
+   vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children);
+
+   TSTR s1 = FormatText("Processing %s:", name.c_str());
+   TSTR s2 = FormatText("Processing %s:", name.c_str());
+   INode *bone = gi->GetINodeByName(name.c_str());
+   if (bone != NULL) 
    {
-      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
-      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
+      if (uncontrolledDummies)
+         BuildControllerRefList(node, ctrlCount);
 
-      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
-      Matrix3 im = Inverse(m);
+      Matrix44 m4 = node->GetWorldTransform();
+      Vector3 pos; Matrix33 rot; float scale;
+      m4.Decompose(pos, rot, scale);
+      Matrix3 m = TOMATRIX3(m4);
+      Point3 p = m.GetTrans();
+      Quat q(m);
+
+      s1 += FormatText(" ( %s)", PrintMatrix3(m).data());
+      if (strmatch(name, master->GetRootName()))
+      {
+         // Align COM
+         //PosRotScaleNode(bone, p, q, 1.0f, prsPos);
+         PosRotScaleBiped(master, bone, p, q, 1.0f, prsPos);
+      }
+      else if (INode *pnode = bone->GetParentNode())
+      {
+         // Reparent if necessary
+         if (!strmatch(parent->GetName(), pnode->GetName())) {
+            if (pnode = gi->GetINodeByName(parent->GetName().c_str())) {
+               bone->Detach(0);
+               pnode->AttachChild(bone);
+            }
+         }
 
-      Point3 p; Quat q; Point3 s;
-      DecomposeMatrix(im, p, q, s);
-      master->SetBipedRot(q, 0, node, TRUE);
+         // Hack to scale the object until it fits
+         for (int i=0; i<10; ++i) {
+            float s = CalcScale(bone, node, childNodes);
+            if (fabs(s-1.0f) < (FLT_EPSILON*100.0f))
+               break;
+            s1 += FormatText(" (%g)", s);
+            master->SetBipedScale(TRUE, ScaleValue(Point3(s,s,s)), 0, bone);
+         }
+         PosRotScale prs = prsDefault;
+         PosRotScaleBiped(master, bone, p, q, scale, prs);
+
+         // Rotation with Clavicle is useless in Figure Mode using the standard interface
+         //   I was tring unsuccessfully to correct for it
+         //if (wildcmpi("Bip?? ? Clavicle", name.c_str())) {
+         //   AngAxis a1 = CalcTransform(bone, node, childNodes);
+         //   Matrix3 tm1 = GenerateRotMatrix(a1);
+         //   Quat nq = TransformQuat(tm1, q);
+         //   PosRotScaleNode(bone, p, nq, scale, prsRot);
+         //}
+      }
+      s2 += FormatText(" ( %s)", PrintMatrix3(bone->GetNodeTM(0)).data());
    }
    else
    {
-
+      ImportBones(node, false);
    }
-   if (Recurse)
-   {
-      vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
-      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
-         ScaleBiped(master, *itr, Recurse);
-      }
+   for (char *p = s1; *p != 0; ++p) if (isspace(*p)) *p = ' ';
+   for (char *p = s2; *p != 0; ++p) if (isspace(*p)) *p = ' ';
+   OutputDebugString(s1 + "\n");
+   OutputDebugString(s2 + "\n");
+
+   for (vector<NiNodeRef>::iterator itr = childNodes.begin(), end = childNodes.end(); itr != end; ++itr){
+      AlignBiped(master, *itr);
    }
 }
 
-
 INode *NifImporter::CreateBone(const string& name, Point3 startPos, Point3 endPos, Point3 zAxis)
 {
    if (FPInterface * fpBones = GetCOREInterface(Interface_ID(0x438aff72, 0xef9675ac)))
@@ -308,7 +468,7 @@ INode *NifImporter::CreateBone(const string& name, Point3 startPos, Point3 endPo
                setMAXScriptValue(o->GetReference(0), "width", 0, width);
                setMAXScriptValue(o->GetReference(0), "height", 0, width);
             }
-            n->ShowBone(1);
+            n->ShowBone(2);
          }
          return result.n;
       }
@@ -347,41 +507,6 @@ void NifImporter::ImportBones(vector<NiNodeRef>& bones)
    }
 }
 
-float GetObjectLength(NiAVObjectRef obj)
-{
-   float clen = obj->GetLocalTranslation().Magnitude();
-   if (clen < (FLT_EPSILON*10)) {
-      if (NiTriBasedGeomRef geom = DynamicCast<NiTriBasedGeom>(obj)) {
-         if (NiTriBasedGeomDataRef data = geom->GetData()) {
-            clen = data->GetRadius() * 2.0f;
-         }
-      }
-   }
-   return clen;
-}
-
-static void BuildControllerRefList(NiNodeRef node, map<string,int>& ctrlCount)
-{
-   list<NiTimeControllerRef> ctrls = node->GetControllers();
-   for (list<NiTimeControllerRef>::iterator itr = ctrls.begin(), end = ctrls.end(); itr != end; ++itr) {
-      list<NiNodeRef> nlist = DynamicCast<NiNode>((*itr)->GetRefs());
-
-      // Append extra targets.  Goes away if GetRefs eventually returns the extra targets
-      if (NiMultiTargetTransformControllerRef multiCtrl = DynamicCast<NiMultiTargetTransformController>(*itr)) {
-         vector<NiNodeRef> extra = multiCtrl->GetExtraTargets();
-         nlist.insert(nlist.end(), extra.begin(), extra.end());
-      }
-
-      for (list<NiNodeRef>::iterator nitr = nlist.begin(); nitr != nlist.end(); ++nitr){
-         string name = (*nitr)->GetName();
-         map<string,int>::iterator citr = ctrlCount.find(name);
-         if (citr != ctrlCount.end())
-            ++(*citr).second;
-         else
-            ctrlCount[name] = 1;
-      }
-   }
-}
 
 static bool HasControllerRef(map<string,int>& ctrlCount, const string& name)
 {
@@ -399,7 +524,7 @@ static bool HasUserPropBuffer(NiNodeRef node)
    return false;
 }
 
-void NifImporter::ImportBones(NiNodeRef node)
+void NifImporter::ImportBones(NiNodeRef node, bool recurse)
 {
    try 
    {
@@ -479,7 +604,7 @@ void NifImporter::ImportBones(NiNodeRef node)
             }
          }
       }
-      if (bone)
+      if (bone && recurse)
       {
          ImportBones(childNodes);
       }
diff --git a/NifImport/MaxNifTools.ini b/NifImport/MaxNifTools.ini
index b2fbbe2..0259437 100644
--- a/NifImport/MaxNifTools.ini
+++ b/NifImport/MaxNifTools.ini
@@ -72,7 +72,7 @@ EnableAnimations=1
 RequireMultipleKeys=1
 ; Replace TCB Rotation with Bezier (workaround for unexpected behavior with TCB rotations)
 ReplaceTCBRotationWithBezier=1
-; Apply the overall transform to skin and bones. Default: 0
+; Apply the overall transform to skin and bones. Default: 1
 ApplyOverallTransformToSkinAndBones=1
 
 ; [Applications]
@@ -132,4 +132,4 @@ TextureRootPaths=$(ExtractFolder)
 TextureExtensions=.dds;.bmp;.tga
 TextureSearchPaths=
 GoToSkeletonBindPosition=1
-
+ApplyOverallTransformToSkinAndBones=0
\ No newline at end of file
diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h
index c268b44..015a62e 100644
--- a/NifImport/NIFImporter.h
+++ b/NifImport/NIFImporter.h
@@ -75,12 +75,9 @@ public:
    bool HasSkeleton();
    bool IsBiped();
    void ImportBones(vector<Niflib::NiNodeRef>& bones);
-   void ImportBones(Niflib::NiNodeRef blocks);
+   void ImportBones(Niflib::NiNodeRef blocks, bool recurse = true);
    void ImportBipeds(vector<Niflib::NiNodeRef>& blocks);
    void AlignBiped(IBipMaster* master, Niflib::NiNodeRef block);
-   void PositionBiped(IBipMaster* master, Niflib::NiNodeRef block, bool Recurse = false);
-   void RotateBiped(IBipMaster* master, Niflib::NiNodeRef block, bool Recurse = false);
-   void ScaleBiped(IBipMaster* master, Niflib::NiNodeRef block, bool Recurse = false);
    bool ImportMeshes(Niflib::NiNodeRef block);
    string FindImage(const string& name);
 
-- 
GitLab