From 2fe8542114ca43aaea63b1d73ed03e00903106e6 Mon Sep 17 00:00:00 2001 From: Tazpn <tazpn@users.sourceforge.net> Date: Sat, 1 Jul 2006 15:49:35 +0000 Subject: [PATCH] Miscellaneous fixes for Skin for DAoC and MW and trying not to breaking Civ4 and Ob. --- MaxNifPlugins_Readme.txt | 28 ++++++++---- NifImport/AppSettings.cpp | 4 ++ NifImport/AppSettings.h | 4 ++ NifImport/ImportAnimation.cpp | 56 ++++++++++++++++-------- NifImport/ImportMeshAndSkin.cpp | 23 +++++++--- NifImport/ImportSkeleton.cpp | 76 +++++++++++++++++++++++++++------ NifImport/MaxNifImport.rc | 4 +- NifImport/MaxNifImport.vcproj | 2 +- NifImport/MaxNifTools.ini | 34 +++++++++++++-- NifImport/NIFImport.cpp | 27 ++++++++++-- NifImport/NIFImporter.h | 6 +++ NifImport/niutils.cpp | 14 ++++++ NifImport/niutils.h | 32 ++++++++++---- 13 files changed, 245 insertions(+), 65 deletions(-) diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt index 5df8cb0..be0812c 100644 --- a/MaxNifPlugins_Readme.txt +++ b/MaxNifPlugins_Readme.txt @@ -1,8 +1,7 @@ - MaxPlugins 0.1.2 + MaxPlugins 0.1.3 ================ - This releases introduces an importer and fixes some bugs in the exporter. - + This plugin set currently consists of an exporter, importer and a utility plugin. This is a early release, so expect it to be buggy. @@ -26,12 +25,23 @@ choose "Reset XForm" and click "Reset Selected". This should fix it. + Change log + ---------- - Changes since 0.1 - ----------------- - - - Introduced the importer - + 0.1.3 + ----- + + o Importer + - Fixed alignment issues when importing Morrowind Armor nifs + - Added initial animation support (only for animations internal to nif, no kf file support yet) + - Fixed numerous issues with bone system (biped is still broken) + - Fixed issues with skin and doac nifs + + 0.1.2 + ----- + + - Introduced the importer + - Fixed collision generation, turned out that Oblivion doesn't like NvTriStrip's strips. Thanks to Razorwing for discovering the bug and Tanguy Fautré for his @@ -47,7 +57,7 @@ 3D Studio Max 6, 7 and 8 Importer - 3d Studio Max 8, untested with previous releases + 3d Studio Max 8 Installation ------------ diff --git a/NifImport/AppSettings.cpp b/NifImport/AppSettings.cpp index 6beb806..4c4240a 100644 --- a/NifImport/AppSettings.cpp +++ b/NifImport/AppSettings.cpp @@ -56,6 +56,10 @@ void AppSettings::ReadSettings(string iniFile) Skeleton = GetSetting<string>("Skeleton"); useSkeleton = GetSetting<bool>("UseSkeleton", useSkeleton); goToSkeletonBindPosition = GetSetting<bool>("GoToSkeletonBindPosition", goToSkeletonBindPosition); + disableCreateNubsForBones = GetSetting<bool>("DisableCreateNubsForBones", disableCreateNubsForBones); + applyOverallTransformToSkinAndBones = GetSetting<int>("ApplyOverallTransformToSkinAndBones", -1); + + dummyNodeMatches = TokenizeString(GetSetting<string>("DummyNodeMatches").c_str(), ";"); } string AppSettings::FindImage(const string& fname){ diff --git a/NifImport/AppSettings.h b/NifImport/AppSettings.h index 588cf97..52a7ea4 100644 --- a/NifImport/AppSettings.h +++ b/NifImport/AppSettings.h @@ -24,6 +24,7 @@ public: , parsedImages(false) , useSkeleton(false) , goToSkeletonBindPosition(true) + , disableCreateNubsForBones(false) {} std::string Name; @@ -36,8 +37,11 @@ public: std::string Skeleton; bool useSkeleton; bool goToSkeletonBindPosition; + bool disableCreateNubsForBones; NameValueCollection Environment; NameValueCollection imgTable; + stringlist dummyNodeMatches; + int applyOverallTransformToSkinAndBones; static void Initialize(Interface *gi); void ReadSettings(std::string iniFile); diff --git a/NifImport/ImportAnimation.cpp b/NifImport/ImportAnimation.cpp index 350578c..c239048 100644 --- a/NifImport/ImportAnimation.cpp +++ b/NifImport/ImportAnimation.cpp @@ -35,6 +35,8 @@ enum { }; const float FramesPerSecond = 30.0f; +const float FramesIncrement = 1.0f/30.0f; + const int TicksPerFrame = GetTicksPerFrame(); inline TimeValue TimeToFrame(float t) { @@ -260,6 +262,9 @@ bool NifImporter::ImportAnimation() if (!enableAnimations) return false; + if (nodes.empty()) + return false; + AnimationImport ai(*this); return ai.AddValues(DynamicCast<NiObjectNET>(nodes[0]->GetChildren())); } @@ -267,7 +272,6 @@ bool NifImporter::ImportAnimation() bool KFMImporter::ImportAnimation() { bool ok = false; - const float FramesIncrement = 1.0f/30.0f; int curFrame = 0; // Read Kf files #ifdef USE_UNSUPPORTED_CODE @@ -367,10 +371,6 @@ bool AnimationImport::AddValues(NiObjectNETRef nref) if (NULL == c) return false; - /* vector<KeyTextValue> tkeys = BuildKeyValues(nref); - if (tkeys.empty()) - return false;*/ - float time = 0.0f; list< NiTimeControllerRef > clist = nref->GetControllers(); if (NiTransformControllerRef tc = SelectFirstObjectOfType<NiTransformController>(clist)) { @@ -389,13 +389,33 @@ bool AnimationImport::AddValues(NiObjectNETRef nref) bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time) { + vector<Vector3Key> posKeys = data->GetTranslateKeys(); + vector<QuatKey> quatKeys = data->GetQuatRotateKeys(); + vector<FloatKey> sclKeys = data->GetScaleKeys(); + vector<FloatKey> xKeys = data->GetXRotateKeys(); + vector<FloatKey> yKeys = data->GetYRotateKeys(); + vector<FloatKey> zKeys = data->GetZRotateKeys(); + + // Require more than one key to import (to avoid zero frame positioning used in mw and daoc + if (ni.requireMultipleKeys && + !( posKeys.size() > 1 + || quatKeys.size() > 1 + || sclKeys.size() > 1 + || xKeys.size() > 1 + || yKeys.size() > 1 + || zKeys.size() > 1 + )) + { + return false; + } + // Handle Translation switch (data->GetTranslateType()) { case LINEAR_KEY: if (Control *subCtrl = MakePositionXYZ(c, Class_ID(LININTERP_FLOAT_CLASS_ID,0))) { vector<FloatKey> xkeys, ykeys, zkeys; - SplitKeys(data->GetTranslateKeys(), xkeys, ykeys, zkeys); + SplitKeys(posKeys, xkeys, ykeys, zkeys); SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time); SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time); SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time); @@ -406,7 +426,7 @@ bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time) case XYZ_ROTATION_KEY: if (Control *subCtrl = MakePositionXYZ(c, Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) { vector<FloatKey> xkeys, ykeys, zkeys; - SplitKeys(data->GetTranslateKeys(), xkeys, ykeys, zkeys); + SplitKeys(posKeys, xkeys, ykeys, zkeys); SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time); SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time); SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time); @@ -416,7 +436,7 @@ bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time) case TBC_KEY: if (Control *subCtrl = MakePositionXYZ(c, Class_ID(TCBINTERP_FLOAT_CLASS_ID,0))) { vector<FloatKey> xkeys, ykeys, zkeys; - SplitKeys(data->GetTranslateKeys(), xkeys, ykeys, zkeys); + SplitKeys(posKeys, xkeys, ykeys, zkeys); SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time); SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time); SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time); @@ -429,21 +449,21 @@ bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time) { case LINEAR_KEY: if (Control *subCtrl = MakeRotation(c, Class_ID(LININTERP_ROTATION_CLASS_ID,0), Class_ID(LININTERP_FLOAT_CLASS_ID,0))) { - SetKeys<ILinRotKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time); + SetKeys<ILinRotKey, QuatKey>(subCtrl, quatKeys, time); } break; case QUADRATIC_KEY: if (Control *subCtrl = MakeRotation(c, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) { - SetKeys<IBezQuatKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time); + SetKeys<IBezQuatKey, QuatKey>(subCtrl, quatKeys, time); } break; case XYZ_ROTATION_KEY: if (Control *subCtrl = MakeRotation(c, Class_ID(EULER_CONTROL_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) { - SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetXController(), data->GetXRotateKeys(), time); - SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetYController(), data->GetYRotateKeys(), time); - SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetZController(), data->GetZRotateKeys(), time); + SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetXController(), xKeys, time); + SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetYController(), yKeys, time); + SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetZController(), zKeys, time); } break; @@ -451,11 +471,11 @@ bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time) if (ni.replaceTCBRotationWithBezier) { // TCB simply is not working for me. Better off with Bezier as a workaround if (Control *subCtrl = MakeRotation(c, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) { - SetKeys<IBezQuatKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time); + SetKeys<IBezQuatKey, QuatKey>(subCtrl, quatKeys, time); } } else { if (Control *subCtrl = MakeRotation(c, Class_ID(TCBINTERP_ROTATION_CLASS_ID,0), Class_ID(TCBINTERP_FLOAT_CLASS_ID,0))) { - SetKeys<ITCBRotKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time); + SetKeys<ITCBRotKey, QuatKey>(subCtrl, quatKeys, time); } } break; @@ -465,18 +485,18 @@ bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time) { case LINEAR_KEY: if (Control *subCtrl = MakeScale(c, Class_ID(LININTERP_SCALE_CLASS_ID,0))) { - SetKeys<ILinScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), time); + SetKeys<ILinScaleKey, FloatKey>(subCtrl, sclKeys, time); } break; case QUADRATIC_KEY: case XYZ_ROTATION_KEY: if (Control *subCtrl = MakeScale(c, Class_ID(HYBRIDINTERP_SCALE_CLASS_ID,0))) { - SetKeys<IBezScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), time); + SetKeys<IBezScaleKey, FloatKey>(subCtrl, sclKeys, time); } break; case TBC_KEY: if (Control *subCtrl = MakeScale(c, Class_ID(TCBINTERP_SCALE_CLASS_ID,0))) { - SetKeys<ITCBScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), time); + SetKeys<ITCBScaleKey, FloatKey>(subCtrl, sclKeys, time); } break; } diff --git a/NifImport/ImportMeshAndSkin.cpp b/NifImport/ImportMeshAndSkin.cpp index 28f9bcf..5083e01 100644 --- a/NifImport/ImportMeshAndSkin.cpp +++ b/NifImport/ImportMeshAndSkin.cpp @@ -311,6 +311,15 @@ bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom) if (ISkin *skin = (ISkin *) skinMod->GetInterface(I_SKIN)){ ISkinImportData* iskinImport = (ISkinImportData*) skinMod->GetInterface(I_SKINIMPORTDATA); + Matrix3 m3; + if (applyOverallTransformToSkinAndBones) { + Matrix3 initNodeTM, initObjTM; + initNodeTM.IdentityMatrix(), initObjTM.IdentityMatrix(); + skin->GetSkinInitTM(tnode, initNodeTM, false); + skin->GetSkinInitTM(tnode, initObjTM, true); + m3 = TOMATRIX3(data->GetOverallTransform()); + iskinImport->SetSkinTm(tnode, initNodeTM * m3, initObjTM * m3); + } // Create Bone List Tab<INode*> bones; int i=0; @@ -319,12 +328,16 @@ bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom) if (INode *boneRef = gi->GetINodeByName(name.c_str())) { bones.Append(1, &boneRef); iskinImport->AddBoneEx(boneRef, TRUE); - } - // Set Bone Transform - //Matrix3 tm = boneRef->GetObjectTM(0); - //Matrix3 m = TOMATRIX3(data->GetBoneTransform(i)); - //iskinImport->SetBoneTm(boneRef, tm, m); + // Set Bone Transform + if (applyOverallTransformToSkinAndBones) { + Matrix3 initNodeTM, initBoneTM; + initNodeTM.IdentityMatrix(), initBoneTM.IdentityMatrix(); + skin->GetBoneInitTM(boneRef, initNodeTM, false); + skin->GetBoneInitTM(boneRef, initBoneTM, true); + iskinImport->SetBoneTm(boneRef, initNodeTM * m3, initBoneTM * m3); + } + } } ObjectState os = tnode->EvalWorldState(0); diff --git a/NifImport/ImportSkeleton.cpp b/NifImport/ImportSkeleton.cpp index 9268019..54bc729 100644 --- a/NifImport/ImportSkeleton.cpp +++ b/NifImport/ImportSkeleton.cpp @@ -15,6 +15,9 @@ HISTORY: #include <obj/NiTriBasedGeom.h> #include <obj/NiTriBasedGeomData.h> #include <obj/NiTimeController.h> +#include <obj/NiMultiTargetTransformController.h> +#include <obj/NiStringExtraData.h> +#include <obj/NiBillboardNode.h> #include <float.h> #include <dummy.h> @@ -316,7 +319,6 @@ INode *NifImporter::CreateBone(const string& name, Point3 startPos, Point3 endPo INode *NifImporter::CreateHelper(const string& name, Point3 startPos) { - //POINTHELP_CLASS_ID 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))); @@ -327,6 +329,14 @@ INode *NifImporter::CreateHelper(const string& name, Point3 startPos) 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 NULL; } @@ -350,10 +360,52 @@ float GetObjectLength(NiAVObjectRef obj) 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) +{ + return (ctrlCount.find(name) != ctrlCount.end()); +} + +static bool HasUserPropBuffer(NiNodeRef node) +{ + if (node) { + if (NiStringExtraDataRef data = SelectFirstObjectOfType<NiStringExtraData>(node->GetExtraData())){ + if (strmatch(data->GetName(), "UserPropBuffer")) + return true; + } + } + return false; +} + void NifImporter::ImportBones(NiNodeRef node) { try { + if (uncontrolledDummies) + BuildControllerRefList(node, ctrlCount); + string name = node->GetName(); vector<NiAVObjectRef> children = node->GetChildren(); vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children); @@ -404,22 +456,19 @@ void NifImporter::ImportBones(NiNodeRef node) } else { - list<NiTimeControllerRef> ctrls = node->GetControllers(); - if (parent == NULL || parent->GetParent() == NULL || ctrls.empty()) - { + bool isDummy = ( (uncontrolledDummies && !HasControllerRef(ctrlCount, name)) + || (!dummyNodeMatches.empty() && wildmatch(dummyNodeMatches, name)) + || (convertBillboardsToDummyNodes && node->IsDerivedType(NiBillboardNode::TypeConst())) + ); + if (isDummy && createNubsForBones) bone = CreateHelper(name, p); - if (ctrls.empty()) - bone->Hide(TRUE); - } else if (bone = CreateBone(name, p, pp, zAxis)) { PosRotScaleNode(bone, p, q, scale, prs); - if (createNubsForBones && childNodes.empty()){ - if (INode *helper = CreateHelper(string().assign(name).append(" Nub"), pp)){ - helper->Hide(TRUE); - bone->AttachChild(helper, 1); - } - } + if (isDummy) + bone->Hide(TRUE); + else + bone->Hide(node->GetHidden() ? TRUE : FALSE); } if (bone) { @@ -428,7 +477,6 @@ void NifImporter::ImportBones(NiNodeRef node) if (INode *pn = gi->GetINodeByName(parent->GetName().c_str())) pn->AttachChild(bone, 1); } - bone->Hide(node->GetHidden() ? TRUE : FALSE); } } if (bone) diff --git a/NifImport/MaxNifImport.rc b/NifImport/MaxNifImport.rc index d181dc0..51d8121 100644 --- a/NifImport/MaxNifImport.rc +++ b/NifImport/MaxNifImport.rc @@ -88,7 +88,7 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,3,2 + FILEVERSION 0,1,3,4 PRODUCTVERSION 0,1,3,0 FILEFLAGSMASK 0x37L #ifdef _DEBUG @@ -105,7 +105,7 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "3ds Max Nif Importer" - VALUE "FileVersion", "0, 1, 3, 2" + VALUE "FileVersion", "0, 1, 3, 4" VALUE "InternalName", "MaxNifImport.dli" VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved." VALUE "OriginalFilename", "MaxNifImport.dli" diff --git a/NifImport/MaxNifImport.vcproj b/NifImport/MaxNifImport.vcproj index 8ef3a13..561006e 100644 --- a/NifImport/MaxNifImport.vcproj +++ b/NifImport/MaxNifImport.vcproj @@ -49,7 +49,7 @@ Name="VCCLCompilerTool" AdditionalOptions="/LD " InlineFunctionExpansion="1" - AdditionalIncludeDirectories="C:\3dsmax8\maxsdk\include" + AdditionalIncludeDirectories="" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;USE_NIFLIB_TEMPLATE_HELPERS;_USE_MATH_DEFINES" StringPooling="true" ExceptionHandling="2" diff --git a/NifImport/MaxNifTools.ini b/NifImport/MaxNifTools.ini index c2b63fb..b40bf05 100644 --- a/NifImport/MaxNifTools.ini +++ b/NifImport/MaxNifTools.ini @@ -6,7 +6,7 @@ ShortDescription=Netimmerse/Gamebryo ; KnownApplications - Used to indicate which sections in the ini file point ; to "Applications" which have their own settings in a section below. -KnownApplications=Oblivion;Morrowind;Civ4 +KnownApplications=Oblivion;Morrowind;Civ4;DAoC ; Reparse the Applications (and therefore Texture directory cache) on every import/export Reparse=0 @@ -53,14 +53,25 @@ ForceRotation=0 BrowseForSkeleton=1 ; DefaultName for Skeletons (use if in same directory as imported nif) DefaultSkeletonName=skeleton.nif -; Create Nubs for final bone in chain (not supported yet) -CreateNubsForBones=1 + +; Create Dummy nodes for bones that appear to be helper objects. Default: 0 +CreateNubsForBones=0 +; Dummy nodes wildcard matching. (Hide these when not created as Dummy) Default: Bip??;Bip* NonAccum +DummyNodeMatches=Bip??;* NonAccum +; Make Billboard nodes to Dummy nodes rather than bones. Default: 1 +ConvertBillboardsToDummyNodes=1 +; Add Bones not controlled by a controller as dummy. Default: 1 +UncontrolledDummies=1 [AnimationImport] ; Enable Animation Import. Default: 1 EnableAnimations=1 +; Require Multiple Keys to be present to before importing animation. (Kludge to workaround DOaC issues.) Default: 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 +ApplyOverallTransformToSkinAndBones=0 ; [Applications] ; RootPaths - Semicolon separated list of base directories to use when determining which app the imported file is from @@ -88,6 +99,7 @@ TextureRootPaths=${RootPath};${TextureRootPath} TextureExtensions=.dds; TextureSearchPaths=${RootPath}\Textures;${TextureRootPath}\Textures\Characters;${TextureRootPath}\Textures\Armor GoToSkeletonBindPosition=1 +ApplyOverallTransformToSkinAndBones=0 [Morrowind] InstallPath=[HKLM\SOFTWARE\Bethesda Softworks\Morrowind]=@"Installed Path" @@ -98,6 +110,7 @@ TextureRootPaths=${RootPath}\Textures;${ExtractFolder}\Textures TextureExtensions=.tga; TextureSearchPaths=${RootPath}\Textures GoToSkeletonBindPosition=1 +ApplyOverallTransformToSkinAndBones=1 [Civ4] InstallPath=[HKEY_LOCAL_MACHINE\SOFTWARE\Firaxis Games\Sid Meier's Civilization 4]=@"INSTALLDIR" @@ -107,4 +120,17 @@ RootPaths=${ExtractFolder};${InstallPath}\Assets;${InstallPath}\Mods;%USERPROFIL TextureRootPaths=$(ExtractFolder)\art\shared\ TextureExtensions=.dds;.bmp TextureSearchPaths= -GoToSkeletonBindPosition=1 \ No newline at end of file +GoToSkeletonBindPosition=1 +DummyNodeMatches=MD;Bip;Bip??;* NonAccum;Effect*;Sound*;Dummy* +ApplyOverallTransformToSkinAndBones=0 + +[DAoC] +Isles_InstallPath=[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Dark Age of Camelot - Shrouded Isles_is1]=@"InstallLocation" +RootPath= +ExtractFolder= +RootPaths=${Isles_InstallPath};${ExtractFolder} +TextureRootPaths=$(ExtractFolder) +TextureExtensions=.dds;.bmp;.tga +TextureSearchPaths= +GoToSkeletonBindPosition=1 +ApplyOverallTransformToSkinAndBones=1 diff --git a/NifImport/NIFImport.cpp b/NifImport/NIFImport.cpp index ee9a21b..c9dc00e 100644 --- a/NifImport/NIFImport.cpp +++ b/NifImport/NIFImport.cpp @@ -46,8 +46,8 @@ static void BuildNodes(NiNodeRef object, vector<NiNodeRef>& nodes) { if (!object) return; + nodes.push_back(object); vector<NiNodeRef> links = DynamicCast<NiNode>(object->GetChildren()); - nodes.insert(nodes.end(), links.begin(), links.end()); for (vector<NiNodeRef>::iterator itr = links.begin(), end = links.end(); itr != end; ++itr) BuildNodes(*itr, nodes); } @@ -136,11 +136,26 @@ void NifImporter::LoadIniSettings() maxBoneWidth = GetIniValue<float>(BipedImportSection, "MaxBoneWidth", 3.0f); boneWidthToLengthRatio = GetIniValue<float>(BipedImportSection, "BoneWidthToLengthRatio", 0.25f); createNubsForBones = GetIniValue<bool>(BipedImportSection, "CreateNubsForBones", true); + dummyNodeMatches = TokenizeString(GetIniValue<string>(BipedImportSection, "DummyNodeMatches", "").c_str(), ";"); + convertBillboardsToDummyNodes = GetIniValue<bool>(BipedImportSection, "ConvertBillboardsToDummyNodes", true); + uncontrolledDummies = GetIniValue<bool>(BipedImportSection, "UncontrolledDummies", true); replaceTCBRotationWithBezier = GetIniValue<bool>(AnimImportSection, "ReplaceTCBRotationWithBezier", true); enableAnimations = GetIniValue<bool>(AnimImportSection, "EnableAnimations", true); - - goToSkeletonBindPosition = (appSettings ? appSettings->goToSkeletonBindPosition : false); + requireMultipleKeys = GetIniValue<bool>(AnimImportSection, "RequireMultipleKeys", true); + applyOverallTransformToSkinAndBones = GetIniValue<bool>(AnimImportSection, "ApplyOverallTransformToSkinAndBones", true); + + goToSkeletonBindPosition = false; + // Override specific settings + if (appSettings) { + if (appSettings->disableCreateNubsForBones) + createNubsForBones = false; + goToSkeletonBindPosition = appSettings->goToSkeletonBindPosition; + if (!appSettings->dummyNodeMatches.empty()) + dummyNodeMatches = appSettings->dummyNodeMatches; + if (appSettings->applyOverallTransformToSkinAndBones != -1) + applyOverallTransformToSkinAndBones = appSettings->applyOverallTransformToSkinAndBones ? true : false; + } } void NifImporter::SaveIniSettings() @@ -229,7 +244,11 @@ bool NifImporter::DoImport() } if (isValid()) { - ImportBones(DynamicCast<NiNode>(rootNode->GetChildren())); + if (strmatch(rootNode->GetName(), "Scene Root")) + ImportBones(DynamicCast<NiNode>(rootNode->GetChildren())); + else + ImportBones(rootNode); + ok = ImportMeshes(rootNode); if (importSkeleton && removeUnusedImportedBones){ diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h index 2bec037..e570592 100644 --- a/NifImport/NIFImporter.h +++ b/NifImport/NIFImporter.h @@ -48,13 +48,19 @@ public: float maxBoneWidth; float boneWidthToLengthRatio; bool createNubsForBones; + stringlist dummyNodeMatches; + bool convertBillboardsToDummyNodes; + bool uncontrolledDummies; // Animation related Settings bool replaceTCBRotationWithBezier; bool enableAnimations; + bool requireMultipleKeys; + bool applyOverallTransformToSkinAndBones; vector<Niflib::NiObjectRef> blocks; vector<Niflib::NiNodeRef> nodes; + map<string,int> ctrlCount; // counter for number of controllers referencing a node NifImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts); virtual void Initialize(); diff --git a/NifImport/niutils.cpp b/NifImport/niutils.cpp index 8b94298..5c7cc39 100644 --- a/NifImport/niutils.cpp +++ b/NifImport/niutils.cpp @@ -299,6 +299,20 @@ int wildcmpi(const TCHAR *wild, const TCHAR *string) { return !*wild; } +bool wildmatch(const string& match, const std::string& value) +{ + return (wildcmpi(match.c_str(), value.c_str())) ? true : false; +} + +bool wildmatch(const stringlist& matches, const std::string& value) +{ + for (stringlist::const_iterator itr=matches.begin(), end=matches.end(); itr != end; ++itr){ + if (wildcmpi((*itr).c_str(), value.c_str())) + return true; + } + return false; +} + //! Renames Max Node if it exists void RenameNode(Interface *gi, LPCTSTR SrcName, LPCTSTR DstName) { diff --git a/NifImport/niutils.h b/NifImport/niutils.h index b52b713..1035cc6 100644 --- a/NifImport/niutils.h +++ b/NifImport/niutils.h @@ -41,9 +41,6 @@ INFO: See Implementation for minimalist comments #define _countof(x) (sizeof(x)/sizeof((x)[0])) #endif -extern int wildcmp(const TCHAR *wild, const TCHAR *string); -extern int wildcmpi(const TCHAR *wild, const TCHAR *string); - // Trim whitespace before and after a string inline TCHAR *Trim(TCHAR*&p) { while(_istspace(*p)) *p++ = 0; @@ -114,6 +111,30 @@ struct NumericStringEquivalence } }; +// Common collections that I use +typedef std::map<std::string, std::string, ltstr> NameValueCollection; +typedef std::pair<std::string, std::string> KeyValuePair; +typedef std::list<std::string> stringlist; + +extern int wildcmp(const TCHAR *wild, const TCHAR *string); +extern int wildcmpi(const TCHAR *wild, const TCHAR *string); + +inline bool strmatch(const string& lhs, const std::string& rhs) { + return (0 == _tcsicmp(lhs.c_str(), rhs.c_str())); +} +inline bool strmatch(const TCHAR* lhs, const std::string& rhs) { + return (0 == _tcsicmp(lhs, rhs.c_str())); +} +inline bool strmatch(const string& lhs, const TCHAR* rhs) { + return (0 == _tcsicmp(lhs.c_str(), rhs)); +} +inline bool strmatch(const TCHAR* lhs, const TCHAR* rhs) { + return (0 == _tcsicmp(lhs, rhs)); +} + +bool wildmatch(const string& match, const std::string& value); +bool wildmatch(const stringlist& matches, const std::string& value); + // Generic IniFile reading routine template<typename T> inline T GetIniValue(LPCTSTR Section, LPCTSTR Setting, T Default, LPCTSTR iniFileName){ @@ -178,11 +199,6 @@ inline void SetIniValue<TSTR>(LPCTSTR Section, LPCTSTR Setting, TSTR value, LPCT WritePrivateProfileString(Section, Setting, value.data(), iniFileName); } -// Common collections that I use -typedef std::map<std::string, std::string, ltstr> NameValueCollection; -typedef std::pair<std::string, std::string> KeyValuePair; -typedef std::list<std::string> stringlist; - extern TSTR FormatText(const TCHAR* format,...); extern std::string FormatString(const TCHAR* format,...); -- GitLab