Newer
Older
/**********************************************************************
*<
FILE: ImportSkeleton.cpp
DESCRIPTION: Skeleton import routines
CREATED BY: tazpn (Theo)
*> Copyright (c) 2006, All Rights Reserved.
**********************************************************************/
#include "stdafx.h"
#include "MaxNifImport.h"
#include <cs/BipedApi.h>
#include <obj/NiTriBasedGeom.h>
#include <obj/NiTriBasedGeomData.h>
#include <obj/NiTimeController.h>
Tazpn
committed
#include <obj/NiMultiTargetTransformController.h>
#include <obj/NiStringExtraData.h>
#include <obj/NiBillboardNode.h>
#include <float.h>
#include <dummy.h>
using namespace Niflib;
struct NiNodeNameEquivalence : public NumericStringEquivalence
{
bool operator()(const NiNodeRef& n1, const NiNodeRef& n2) const {
return NumericStringEquivalence::operator()(n1->GetName(), n2->GetName());
}
};
void GoToSkeletonBindPosition(vector<NiNodeRef>& blocks)
{
//Send all skeleton roots to bind position
for (unsigned int i = 0; i < blocks.size(); ++i) {
NiNodeRef node = blocks[i];
if (node != nullptr && node->IsSkeletonRoot()) {
node->GoToSkeletonBindPosition();
}
}
}
Tazpn
committed
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;
Tazpn
committed
}
static void BuildControllerRefList(NiNodeRef node, map<tstring, int>& ctrlCount)
Tazpn
committed
{
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 = DynamicCast<NiNode>(multiCtrl->GetExtraTargets());
nlist.insert(nlist.end(), extra.begin(), extra.end());
}
for (list<NiNodeRef>::iterator nitr = nlist.begin(); nitr != nlist.end(); ++nitr) {
tstring name = A2TString((*nitr)->GetName());
map<tstring, int>::iterator citr = ctrlCount.find(name);
if (citr != ctrlCount.end())
++(*citr).second;
else
ctrlCount[name] = 1;
}
}
Tazpn
committed
}
bool NifImporter::HasSkeleton()
{
if (!skeletonCheck.empty()) {
vector<NiNodeRef> bipedRoots = SelectNodesByName(nodes, skeletonCheck.c_str());
return !bipedRoots.empty();
}
return false;
}
bool NifImporter::IsBiped()
{
if (HasSkeleton()) {
NiNodeRef rootNode = root;
if (rootNode) {
list<NiExtraDataRef> extraData = rootNode->GetExtraData();
if (!extraData.empty()) {
if (BSXFlagsRef flags = SelectFirstObjectOfType<BSXFlags>(extraData)) {
return (flags->GetData() & 0x4);
}
}
}
}
return false;
}
void NifImporter::ImportBipeds(vector<NiNodeRef>& nodes)
{
#ifdef USE_BIPED
IBipMaster* master = nullptr;
try
{
vector<NiNodeRef> bipedRoots = SelectNodesByName(nodes, "Bip??");
std::stable_sort(bipedRoots.begin(), bipedRoots.end(), NiNodeNameEquivalence());
for (vector<NiNodeRef>::iterator bipedItr = bipedRoots.begin(); bipedItr != bipedRoots.end(); ++bipedItr)
{
tstring bipname = A2TString((*bipedItr)->GetName());
tstring match = bipname + TEXT("*");
vector<NiNodeRef> bipedNodes = SelectNodesByName(nodes, match.c_str());
float height = this->bipedHeight;
#if USE_CUSTOM_BSBOUND
list<NiExtraDataRef> extraData = nodes[0]->GetExtraData();
if (!extraData.empty()) {
BSBoundRef bound = SelectFirst<BSBound>(extraData);
if (bound) {
array<float, 6> floats = bound->GetUnknownFloats();
height = floats[2] * 2.0f;
}
}
#endif
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
float angle = TORAD(bipedAngle);
Point3 wpos(0.0f, 0.0f, 0.0f);
BOOL arms = (CountNodesByName(bipedNodes, FormatText(TEXT("%s L UpperArm"), bipname.c_str())) > 0) ? TRUE : FALSE;
BOOL triPelvis = bipedTrianglePelvis ? TRUE : FALSE;
int nnecklinks = CountNodesByName(bipedNodes, FormatText(TEXT("%s Neck*"), bipname.c_str()));
int nspinelinks = CountNodesByName(bipedNodes, FormatText(TEXT("%s Spine*"), bipname.c_str()));
int nleglinks = 3 + CountNodesByName(bipedNodes, FormatText(TEXT("%s L HorseLink"), bipname.c_str()));
int ntaillinks = CountNodesByName(bipedNodes, FormatText(TEXT("%s Tail*"), bipname.c_str()));
int npony1links = CountNodesByName(bipedNodes, FormatText(TEXT("%s Ponytail1*"), bipname.c_str()));
int npony2links = CountNodesByName(bipedNodes, FormatText(TEXT("%s Ponytail2*"), bipname.c_str()));
int numfingers = CountNodesByName(bipedNodes, FormatText(TEXT("%s L Finger?"), bipname.c_str()));
int nfinglinks = CountNodesByName(bipedNodes, FormatText(TEXT("%s L Finger0*"), bipname.c_str()));
int numtoes = CountNodesByName(bipedNodes, FormatText(TEXT("%s L Toe?"), bipname.c_str()));
int ntoelinks = CountNodesByName(bipedNodes, FormatText(TEXT("%s L Toe0*"), bipname.c_str()));
BOOL prop1exists = CountNodesByName(bipedNodes, FormatText(TEXT("%s Prop1"), bipname.c_str())) ? TRUE : FALSE;
BOOL prop2exists = CountNodesByName(bipedNodes, FormatText(TEXT("%s Prop2"), bipname.c_str())) ? TRUE : FALSE;
BOOL prop3exists = CountNodesByName(bipedNodes, FormatText(TEXT("%s Prop3"), bipname.c_str())) ? TRUE : FALSE;
int forearmTwistLinks = CountNodesByName(bipedNodes, FormatText(TEXT("%s L Fore*Twist*"), bipname.c_str()));
int upperarmTwistLinks = CountNodesByName(bipedNodes, FormatText(TEXT("%s L Up*Twist*"), bipname.c_str()));
int thighTwistLinks = CountNodesByName(bipedNodes, FormatText(TEXT("%s L Thigh*Twist*"), bipname.c_str()));
int calfTwistLinks = CountNodesByName(bipedNodes, FormatText(TEXT("%s L Calf*Twist*"), bipname.c_str()));
int horseTwistLinks = CountNodesByName(bipedNodes, FormatText(TEXT("%s L Horse*Twist*"), bipname.c_str()));
NiNodeRef root = nodes[0];
IBipMaster* master = nullptr;
if (Max8CreateNewBiped) {
master = Max8CreateNewBiped(height, angle, wpos, arms, triPelvis,
nnecklinks, nspinelinks, nleglinks, ntaillinks, npony1links, npony2links,
numfingers, nfinglinks, numtoes, ntoelinks, bipedAnkleAttach, prop1exists,
prop2exists, prop3exists, forearmTwistLinks, upperarmTwistLinks, thighTwistLinks,
calfTwistLinks, horseTwistLinks);
}
else if (Max7CreateNewBiped) {
master = Max7CreateNewBiped(height, angle, wpos, arms, triPelvis,
nnecklinks, nspinelinks, nleglinks, ntaillinks, npony1links, npony2links,
numfingers, nfinglinks, numtoes, ntoelinks, bipedAnkleAttach, prop1exists,
prop2exists, prop3exists, forearmTwistLinks);
}
if (master)
{
master->SetRootName(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 for Oblivion
RenameNode(gi, FormatText(TEXT("%s L ForeTwist"), bipname), FormatText(TEXT("%s L ForearmTwist"), bipname));
RenameNode(gi, FormatText(TEXT("%s R ForeTwist"), bipname), FormatText(TEXT("%s R ForearmTwist"), bipname));
RenameNode(gi, FormatText(TEXT("%s R LUpArmTwist"), bipname), FormatText(TEXT("%s L UpperArmTwist"), bipname));
RenameNode(gi, FormatText(TEXT("%s R LUpArmTwist"), bipname), FormatText(TEXT("%s R UpperArmTwist"), bipname));
if (NiNodeRef nifBip = FindNodeByName(nodes, bipname))
{
AlignBiped(master, nifBip);
}
}
}
}
catch (exception & e)
{
e = e;
}
catch (...)
{
}
if (master)
master->EndModes(BMODE_FIGURE, TRUE);
#endif
}
Tazpn
committed
static float CalcLength(NiNodeRef node, vector<NiAVObjectRef>& children)
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
{
len = node->GetLocalTranslation().Magnitude();
}
return len;
}
Tazpn
committed
static float CalcLength(INode *bone)
{
int n = bone->NumberOfChildren();
float len = 0.0f;
if (n > 0)
{
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;
}
Tazpn
committed
static bool HasBipedPosDOF(LPCTSTR name)
// 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(TEXT("Bip?? ? Clavicle"), name)
|| wildcmpi(TEXT("Bip?? ? Toe?"), name)
|| wildcmpi(TEXT("Bip?? ? Finger?"), name)
|| wildcmpi(TEXT("Bip?? *Twist*"), name)
);
Tazpn
committed
}
Tazpn
committed
static float CalcScale(INode *bone, NiNodeRef node, vector<NiNodeRef>& children)
{
int n = bone->NumberOfChildren();
if (n > 0)
{
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 cm = child->GetObjectTM(0);
len1 += Length(m.GetTrans() - cm.GetTrans());
if (NiNodeRef child2 = FindNodeByName(children, child->GetName())) {
Matrix3 cm2 = TOMATRIX3(child2->GetWorldTransform());
len2 += Length(m2.GetTrans() - cm2.GetTrans());
}
}
return (len2 != 0.0f && len1 != 0.0f) ? (len2 / len1) : 1.0f;
}
return 1.0f;
Tazpn
committed
}
#ifdef USE_BIPED
Tazpn
committed
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);
Tazpn
committed
}
Tazpn
committed
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));
Tazpn
committed
}
Tazpn
committed
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.GetRow(0)[0] = rcos + u*u*(1 - rcos);
m.GetRow(1)[0] = w * rsin + v*u*(1 - rcos);
m.GetRow(2)[0] = -v * rsin + w*u*(1 - rcos);
m.GetRow(0)[1] = -w * rsin + u*v*(1 - rcos);
m.GetRow(1)[1] = rcos + v*v*(1 - rcos);
m.GetRow(2)[1] = u * rsin + w*v*(1 - rcos);
m.GetRow(0)[2] = v * rsin + u*w*(1 - rcos);
m.GetRow(1)[2] = -u * rsin + v*w*(1 - rcos);
m.GetRow(2)[2] = rcos + w*w*(1 - rcos);
return m;
Tazpn
committed
}
Tazpn
committed
static AngAxis CalcTransform(INode *bone, NiNodeRef node, vector<NiNodeRef>& children)
{
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
Matrix3 mr(TRUE);
int n = bone->NumberOfChildren();
if (n > 0)
{
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, 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;
#endif
Tazpn
committed
void NifImporter::AlignBiped(IBipMaster* master, NiNodeRef node)
{
#ifdef USE_BIPED
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
NiNodeRef parent = node->GetParent();
tstring pname = A2TString(parent->GetName());
tstring name = A2TString(node->GetName());
vector<NiAVObjectRef> children = node->GetChildren();
vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children);
TSTR s1 = FormatText(TEXT("Processing %s:"), name.c_str());
TSTR s2 = FormatText(TEXT("Processing %s:"), name.c_str());
INode *bone = GetNode(node);
if (bone != nullptr)
{
if (uncontrolledDummies)
BuildControllerRefList(node, ctrlCount);
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(TEXT(" ( %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(pname, pnode->GetName())) {
if (pnode = FindNode(parent)) {
bone->Detach(0);
pnode->AttachChild(bone);
}
}
// 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(TEXT(" (%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(TEXT("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(TEXT(" ( %s)"), PrintMatrix3(bone->GetNodeTM(0)).data());
}
else
{
ImportBones(node, false);
}
for (TCHAR *p = DataForWrite(s1); *p != 0; ++p) if (_istspace(*p)) *p = TEXT(' ');
for (TCHAR *p = DataForWrite(s2); *p != 0; ++p) if (_istspace(*p)) *p = TEXT(' ');
OutputDebugString(s1 + TEXT("\n"));
OutputDebugString(s2 + TEXT("\n"));
for (vector<NiNodeRef>::iterator itr = childNodes.begin(), end = childNodes.end(); itr != end; ++itr) {
AlignBiped(master, *itr);
}
#endif
}
INode *NifImporter::CreateBone(const tstring& name, Point3 startPos, Point3 endPos, Point3 zAxis)
if (FPInterface * fpBones = GetCOREInterface(Interface_ID(0x438aff72, 0xef9675ac)))
{
FunctionID createBoneID = fpBones->FindFn(TEXT("createBone"));
FPValue result;
FPParams params(3, TYPE_POINT3, &startPos, TYPE_POINT3, &endPos, TYPE_POINT3, &zAxis);
FPStatus status = fpBones->Invoke(createBoneID, result, ¶ms);
if (status == FPS_OK && result.type == TYPE_INODE)
{
if (INode *n = result.n)
{
SetNodeName(n, name.c_str());
float len = Length(endPos - startPos);
float width = max(minBoneWidth, min(maxBoneWidth, len * boneWidthToLengthRatio));
if (Object* o = n->GetObjectRef())
{
setMAXScriptValue(o->GetReference(0), TEXT("width"), 0, width);
setMAXScriptValue(o->GetReference(0), TEXT("height"), 0, width);
}
Tazpn
committed
#if VERSION_3DSMAX > ((6000<<16)+(15<<8)+0) // Version 6
Tazpn
committed
#else
Tazpn
committed
#endif
}
return result.n;
}
fpBones->ReleaseInterface();
}
return nullptr;
}
INode *NifImporter::CreateHelper(const tstring& name, Point3 startPos)
if (DummyObject *ob = (DummyObject *)gi->CreateInstance(HELPER_CLASS_ID, Class_ID(DUMMY_CLASS_ID, 0))) {
const float DUMSZ = 1.0f;
ob->SetBox(Box3(Point3(-DUMSZ, -DUMSZ, -DUMSZ), Point3(DUMSZ, DUMSZ, DUMSZ)));
if (INode *n = CreateImportNode(name.c_str(), ob, nullptr)) {
n->SetWireColor(RGB(192, 192, 192));
Quat q; q.Identity();
PosRotScaleNode(n, startPos, q, 1.0f, prsPos);
#if VERSION_3DSMAX > ((6000<<16)+(15<<8)+0) // Version 6
n->BoneAsLine(dummyBonesAsLines ? 1 : 0);
n->ShowBone(2);
#endif
return n;
}
}
//if (Object *ob = (Object *)gi->CreateInstance(HELPER_CLASS_ID,Class_ID(BONE_CLASS_ID,0))) {
// if (INode *n = gi->CreateObjectNode(ob)) {
// n->SetName(const_cast<TCHAR*>(name.c_str()));
// Quat q; q.Identity();
// PosRotScaleNode(n, startPos, q, 1.0f, prsPos);
// return n;
// }
//}
return nullptr;
}
INode *NifImporter::CreateCamera(const tstring& name)
Tazpn
committed
{
if (GenCamera *ob = (GenCamera *)gi->CreateInstance(CAMERA_CLASS_ID, Class_ID(SIMPLE_CAM_CLASS_ID, 0))) {
ob->Enable(1);
ob->NewCamera(0);
ob->SetFOV(0, TORAD(75.0f));
if (INode *n = gi->CreateObjectNode(ob)) {
//if (INode *n = CreateImportNode(name.c_str(), ob, nullptr)) {
n->Hide(TRUE);
n->BoneAsLine(1);
return n;
}
}
return nullptr;
Tazpn
committed
}
void NifImporter::ImportBones(vector<NiNodeRef>& bones)
{
for (vector<NiNodeRef>::iterator itr = bones.begin(), end = bones.end(); itr != end; ++itr) {
ImportBones(*itr);
}
}
Tazpn
committed
static bool HasControllerRef(map<tstring, int>& ctrlCount, const tstring& name)
Tazpn
committed
{
return (ctrlCount.find(name) != ctrlCount.end());
Tazpn
committed
}
static bool HasUserPropBuffer(NiNodeRef node)
{
if (node) {
if (NiStringExtraDataRef data = SelectFirstObjectOfType<NiStringExtraData>(node->GetExtraData())) {
if (strmatch(data->GetName(), "UserPropBuffer"))
return true;
}
}
return false;
Tazpn
committed
}
extern Point3 TOEULER(const Matrix3 &m);
inline Point3 TORAD(const Point3& p) {
return Point3(TORAD(p[0]), TORAD(p[1]), TORAD(p[2]));
Tazpn
committed
void NifImporter::ImportBones(NiNodeRef node, bool recurse)
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
try
{
if (uncontrolledDummies)
BuildControllerRefList(node, ctrlCount);
bool setnoname = false;
tstring name = A2TString(node->GetName());
if (name.empty())
{
name = FormatText(TEXT("noname:%d"), ++unnamedCounter);
setnoname = true;
}
vector<NiAVObjectRef> children = node->GetChildren();
vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children);
NiAVObject::CollisionType cType = node->GetCollisionMode();
if (children.empty() && name == TEXT("Bounding Box"))
return;
// Do all node manipulations here
NiNodeRef parent = node->GetParent();
tstring parentname = A2TString((parent ? parent->GetName() : ""));
Matrix44 m4 = node->GetWorldTransform();
// Check for Prn strings and change parent if necessary
if (supportPrnStrings) {
list<NiStringExtraDataRef> strings = DynamicCast<NiStringExtraData>(node->GetExtraData());
for (list<NiStringExtraDataRef>::iterator itr = strings.begin(); itr != strings.end(); ++itr) {
if (strmatch((*itr)->GetName(), "Prn")) {
parentname = A2TString((*itr)->GetData());
if (INode *pn = GetNode(parentname)) {
// Apparently Heads tend to need to be rotated 90 degrees on import for
if (!rotate90Degrees.empty() && wildmatch(rotate90Degrees, parentname)) {
m4 *= TOMATRIX4(RotateYMatrix(TORAD(90)));
}
m4 *= TOMATRIX4(pn->GetObjTMAfterWSM(0, nullptr));
}
}
}
}
float len = node->GetLocalTranslation().Magnitude();
// Remove NonAccum nodes and merge into primary bone
if (mergeNonAccum && wildmatch(TEXT("* NonAccum"), name) && parent)
{
tstring realname = name.substr(0, name.length() - 9);
if (strmatch(realname, parentname))
{
Matrix44 tm = parent->GetLocalTransform() * node->GetLocalTransform();
name = realname;
len += tm.GetTranslation().Magnitude();
parent = parent->GetParent();
}
}
PosRotScale prs = prsDefault;
Vector3 pos; Matrix33 rot; float scale;
m4.Decompose(pos, rot, scale);
Matrix3 im = TOMATRIX3(m4);
Point3 p = im.GetTrans();
Quat q(im);
//q.Normalize();
Vector3 ppos;
Point3 zAxis(0, 0, 1);
bool hasChildren = !children.empty();
if (hasChildren) {
float len = 0.0f;
for (vector<NiAVObjectRef>::iterator itr = children.begin(), end = children.end(); itr != end; ++itr) {
len += GetObjectLength(*itr);
}
len /= float(children.size());
ppos = pos + Vector3(len, 0.0f, 0.0f); // just really need magnitude as rotation will take care of positioning
}
else if (parent)
{
ppos = pos + Vector3(len / 3.0f, 0.0f, 0.0f);
}
Point3 pp(ppos.x, ppos.y, ppos.z);
Point3 qp = TORAD(TOEULER(im));
INode *bone = nullptr;
if (!doNotReuseExistingBones) // Games like BC3 reuse the same bone names
{
bone = FindNode(node);
if (bone == nullptr)
bone = GetNode(name);
}
if (bone)
{
// Is there a better way of TEXT("Affect Pivot Only") behaviors?
INode *pinode = bone->GetParentNode();
if (pinode)
bone->Detach(0, 1);
PosRotScaleNode(bone, p, q, scale, prs);
if (pinode)
pinode->AttachChild(bone, 1);
}
else
{
bool isDummy = importBonesAsDummy
|| ((uncontrolledDummies && !HasControllerRef(ctrlCount, name))
|| (!dummyNodeMatches.empty() && wildmatch(dummyNodeMatches, name))
|| (convertBillboardsToDummyNodes && node->IsDerivedType(NiBillboardNode::TYPE))
);
if (wildmatch(TEXT("Camera*"), name)) {
if (enableCameras) {
if (bone = CreateCamera(name)) {
PosRotScaleNode(bone, p, q, scale, prs);
bone->Hide(node->GetVisibility() ? FALSE : TRUE);
}
}
}
else if (isDummy && createNubsForBones) {
bone = CreateHelper(name, p);
PosRotScaleNode(bone, p, q, scale, prs);
//bone->Hide(node->GetVisibility() ? FALSE : TRUE);
}
else if (bone = CreateBone(name, p, pp, zAxis)) {
PosRotScaleNode(bone, p, q, scale, prs);
bone->Hide(node->GetVisibility() ? FALSE : TRUE);
}
if (bone)
{
if (!parentname.empty())
{
if (mergeNonAccum && wildmatch(TEXT("* NonAccum"), parentname)) {
parentname = parentname.substr(0, parentname.length() - 9);
}
if (INode *pn = GetNode(parentname))
pn->AttachChild(bone, 1);
}
if (setnoname)
bone->SetUserPropBool(NP_NONAME, TRUE);
else
RegisterNode(node, bone);
}
}
// Import UPB
if (bone) ImportUPB(bone, node);
// Import Havok Collision Data surrounding node,
// unfortunately this causes double import of collision so I'm disabling it for now.
if (enableCollision && node->GetParent()) {
ImportCollision(node);
}
if (bone && recurse)
{
ImportBones(childNodes);
}
}
catch (exception & e)
{
e = e;
}
catch (...)
{
}
}
bool NifImporter::ImportUPB(INode *node, Niflib::NiNodeRef block)
{
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
USES_CONVERSION;
TCHAR tbuf[128];
if (!importUPB)
return false;
bool ok = false;
if (node && block)
{
list<NiStringExtraDataRef> strings = DynamicCast<NiStringExtraData>(block->GetExtraData());
for (list<NiStringExtraDataRef>::iterator itr = strings.begin(); itr != strings.end(); ++itr) {
if (strmatch((*itr)->GetName(), "UserPropBuffer") || strmatch((*itr)->GetName(), "UPB")) {
char buffer[1048];
istringstream istr((*itr)->GetData(), ios_base::out);
while (!istr.eof()) {
char *line = buffer;
buffer[0] = 0;
istr.getline(buffer, _countof(buffer) - 1);
if (LPSTR equals = strchr(line, '=')) {
*equals++ = 0;
Trim(line), Trim(equals);
if (line[0] && equals[0]) {
TSTR tline = A2THelper(tbuf, line, _countof(tbuf));
TSTR tequals = A2THelper(tbuf, equals, _countof(tbuf));
node->SetUserPropString(tline, tequals);
ok |= true;
}
}
else {
Trim(line);
int len = strlen(line);
// Handle bethesda special values?
if (len > 0 && line[len - 1] == TEXT('#')) {
TSTR buf, value;
node->GetUserPropBuffer(buf);
value.append(A2THelper(tbuf, line, _countof(tbuf)));
value.append(TEXT("\r\n")).append(buf);
if (wildmatch(TEXT("BSBoneLOD#*"), value)) {
TCHAR *data = DataForWrite(value);
data[0] = TEXT('N');
data[1] = TEXT('i'); // Use NIBoneLOD to be compatible with Civ4 code
}
node->SetUserPropBuffer(value);
}
}
}
}
}
}
return ok;