diff --git a/NifImport/BaseImporter.h b/NifImport/BaseImporter.h new file mode 100644 index 0000000000000000000000000000000000000000..cec3fd50a9e6b098a266b6200344848bba3cb13b --- /dev/null +++ b/NifImport/BaseImporter.h @@ -0,0 +1,87 @@ +/********************************************************************** +*< +FILE: BaseImporter.h + +DESCRIPTION: Base Importer class + +CREATED BY: tazpn (Theo) + +HISTORY: + +*> Copyright (c) 2006, All Rights Reserved. +**********************************************************************/ + +#ifndef __BASEIMPORTER_H__ +#define __BASEIMPORTER_H__ + +extern IBipMaster * (_cdecl * Max8CreateNewBiped)(float,float,class Point3 const &,int,int,int,int,int,int,int,int,int,int,int,int,float,int,int,int,int,int,int,int,int); + +// Importer Base +class BaseImporter +{ +public: + string name; + string path; + ImpInterface *i; + Interface *gi; + BOOL suppressPrompts; + bool iniFileValid; + string iniFileName; + AppSettings *appSettings; + + Niflib::NiObjectRef root; + + BaseImporter(){} + + void BaseInit(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts) + { + name = Name; + i=I; + gi=GI; + suppressPrompts = SuppressPrompts; + + char buffer[MAX_PATH] = {0}, *p = NULL; + GetFullPathName(Name, _countof(buffer), buffer, &p); + if (p) *p = 0; + path = buffer; + iniFileValid = false; + + LPCTSTR Max8CreateNewBipedName = TEXT("?CreateNewBiped@@YAPAVIBipMaster@@MMABVPoint3@@HHHHHHHHHHHHMHHHHHHHH@Z"); + HMODULE hBiped = GetModuleHandle("biped.dlc"); + if (NULL != hBiped && 0 == Max8CreateNewBiped) + *(FARPROC*)&Max8CreateNewBiped = GetProcAddress(hBiped, Max8CreateNewBipedName); + + // Load ini settings + iniFileValid = false; + LoadIniSettings(); + + ReadBlocks(); + + Initialize(); + } + + virtual void LoadIniSettings() = 0; + virtual void ReadBlocks() = 0; + virtual void Initialize() = 0; + + virtual bool isValid() const { return (NULL != root); } + + // Generic IniFile reading routine + template<typename T> + T GetIniValue(LPCTSTR Section, LPCTSTR Setting, T Default){ + if (!iniFileValid) + return Default; + return ::GetIniValue<T>(Section, Setting, Default, iniFileName.c_str()); + } + // Generic IniFile reading routine + template<typename T> + void SetIniValue(LPCTSTR Section, LPCTSTR Setting, T value){ + if (!iniFileValid) + return; + ::SetIniValue<T>(Section, Setting, value, iniFileName.c_str()); + } +}; + +extern void GoToSkeletonBindPosition(std::vector<Niflib::NiNodeRef>& blocks); + +#endif diff --git a/NifImport/ImportAnimation.cpp b/NifImport/ImportAnimation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..350578c287ba202fead4dc9387bb25615773b078 --- /dev/null +++ b/NifImport/ImportAnimation.cpp @@ -0,0 +1,562 @@ +/********************************************************************** +*< +FILE: ImportAnimation.cpp + +DESCRIPTION: Animation Import Routines + +CREATED BY: tazpn (Theo) + +HISTORY: + +*> Copyright (c) 2006, All Rights Reserved. +**********************************************************************/ +#include "stdafx.h" +#include "MaxNifImport.h" +#include "NIFImporter.h" +#include "KFMImporter.h" +#include <obj/NiInterpolator.h> +#include <obj/NiTransformInterpolator.h> +#include <obj/NiTransformData.h> +#include <obj/NiTimeController.h> +#include <obj/NiTransformController.h> +#include <obj/NiTextKeyExtraData.h> +#include <obj/NiKeyframeController.h> +#include <obj/NiKeyframeData.h> +#include <obj/NiStringPalette.h> + +using namespace Niflib; + +const Class_ID IPOS_CONTROL_CLASS_ID = Class_ID(0x118f7e02,0xffee238a); +enum { + IPOS_X_REF = 0, + IPOS_Y_REF = 1, + IPOS_Z_REF = 2, + IPOS_W_REF = 3, +}; + +const float FramesPerSecond = 30.0f; +const int TicksPerFrame = GetTicksPerFrame(); + +inline TimeValue TimeToFrame(float t) { + return TimeValue(t * FramesPerSecond * TicksPerFrame); +} + +typedef Key<float> FloatKey; +typedef Key<Quaternion> QuatKey; +typedef Key<Vector3> Vector3Key; + + +template<typename T, typename U> +inline T MapKey(U& key, float time); + + +template <typename T, typename U> +inline T& InitLinKey(T& rKey, U& key, float time) +{ + rKey.time = TimeToFrame(time + key.time); + rKey.flags = 0; + return rKey; +} +template<> +inline ILinFloatKey MapKey<ILinFloatKey,FloatKey>(FloatKey& key, float time) +{ + ILinFloatKey rKey; + rKey.val = key.data; + return InitLinKey(rKey, key, time); +} + +template<> +inline ILinRotKey MapKey<ILinRotKey, QuatKey>(QuatKey& key, float time) +{ + ILinRotKey rKey; + rKey.val = TOQUAT(key.data); + return InitLinKey(rKey, key, time); +} + +template<> +inline ILinScaleKey MapKey<ILinScaleKey, FloatKey>(FloatKey& key, float time) +{ + ILinScaleKey rKey; + rKey.val.s.Set(key.data, key.data, key.data); + rKey.val.q.Identity(); + return InitLinKey(rKey, key, time); +} + +template<> +inline ILinPoint3Key MapKey<ILinPoint3Key, Vector3Key>(Vector3Key& key, float time) +{ + ILinPoint3Key rKey; + rKey.val = TOPOINT3(key.data); + return InitLinKey(rKey, key, time); +} + + +template <typename T, typename U> +inline T& InitBezKey(T& rKey, U& key, float time) +{ + rKey.time = TimeToFrame(time + key.time); + rKey.flags = 0; + SetInTanType(rKey.flags,BEZKEY_FLAT); + SetOutTanType(rKey.flags,BEZKEY_FLAT); + return rKey; +} +template<> +inline IBezFloatKey MapKey<IBezFloatKey, FloatKey>(FloatKey& key, float time) +{ + IBezFloatKey rKey; + rKey.val = key.data; + rKey.intan = key.forward_tangent; + rKey.outtan = key.backward_tangent; + return InitBezKey(rKey, key, time); +} + +template<> +inline IBezPoint3Key MapKey<IBezPoint3Key, Vector3Key>(Vector3Key& key, float time) +{ + IBezPoint3Key rKey; + rKey.val = TOPOINT3(key.data); + rKey.intan = TOPOINT3(key.forward_tangent); + rKey.outtan = TOPOINT3(key.backward_tangent); + return InitBezKey(rKey, key, time); +} + + +template<> +inline IBezQuatKey MapKey<IBezQuatKey, QuatKey>(QuatKey& key, float time) +{ + IBezQuatKey rKey; + rKey.val = TOQUAT(key.data); + return InitBezKey(rKey, key, time); +} + +template<> +inline IBezScaleKey MapKey<IBezScaleKey, FloatKey>(FloatKey& key, float time) +{ + IBezScaleKey rKey; + rKey.val.s.Set(key.data, key.data, key.data); + rKey.val.q.Identity(); + return InitBezKey(rKey, key, time); +} + +template <typename T, typename U> +inline T& InitTCBKey(T& rKey, U& key, float time) +{ + rKey.time = TimeToFrame(time + key.time); + rKey.tens = key.tension; + rKey.cont = key.continuity; + rKey.bias = key.bias; + rKey.easeIn = 0.0f; + rKey.easeOut = 0.0f; + return rKey; +} + +template<> +inline ITCBFloatKey MapKey<ITCBFloatKey, FloatKey>(FloatKey& key, float time) +{ + ITCBFloatKey rKey; + rKey.val = key.data; + return InitTCBKey(rKey, key, time); +} + +template<> +inline ITCBRotKey MapKey<ITCBRotKey, QuatKey>(QuatKey& key, float time) +{ + ITCBRotKey rKey; + rKey.val = TOQUAT(key.data); + InitTCBKey(rKey, key, time); + rKey.flags = TCBKEY_QUATVALID; + return rKey; +} + +template<> +inline ITCBPoint3Key MapKey<ITCBPoint3Key, Vector3Key>(Vector3Key& key, float time) +{ + ITCBPoint3Key rKey; + rKey.val = TOPOINT3(key.data); + return InitTCBKey(rKey, key, time); +} + +template<> +inline ITCBScaleKey MapKey<ITCBScaleKey, FloatKey>(FloatKey& key, float time) +{ + ITCBScaleKey rKey; + rKey.val.s.Set(key.data, key.data, key.data); + rKey.val.q.Identity(); + return InitTCBKey(rKey, key, time); +} + +template<typename T, typename U> +inline void SetKeys(Control *subCtrl, vector<U>& keys, float time) +{ + if (subCtrl && !keys.empty()){ + if (IKeyControl *ikeys = GetKeyControlInterface(subCtrl)){ + ikeys->SetNumKeys(keys.size()); + for (int i=0,n=keys.size(); i<n; ++i) { + ikeys->SetKey(i, &MapKey<T>(keys[i], time)); + } + } + } +} + +enum V3T +{ + V3T_X, V3T_Y, V3T_Z +}; +inline float GetValue(Vector3& value, V3T type) +{ + switch (type) { + case V3T_X: return value.x; + case V3T_Y: return value.y; + case V3T_Z: return value.z; + } + return 0.0f; +} + +FloatKey& CopyKey( FloatKey& dst, Vector3Key& src, V3T type) +{ + dst.time = src.time; + dst.bias = src.bias; + dst.continuity = src.continuity; + dst.tension = src.tension; + dst.backward_tangent = GetValue(src.backward_tangent, type); + dst.forward_tangent = GetValue(src.forward_tangent, type); + dst.data = GetValue(src.data, type); + return dst; +} + +void SplitKeys(vector<Vector3Key>&keys, vector<FloatKey>&xkeys, vector<FloatKey>&ykeys, vector<FloatKey>&zkeys) +{ + int n = keys.size(); + xkeys.resize(n), ykeys.resize(n), zkeys.resize(n); + for (int i=0,n=keys.size(); i<n; ++i) { + Vector3Key& key = keys[i]; + CopyKey(xkeys[i], key, V3T_X), CopyKey(ykeys[i], key, V3T_Y), CopyKey(zkeys[i], key, V3T_Z); + } +} + +typedef Key<string> KeyTextValue; + +struct AnimationImport +{ + AnimationImport(NifImporter& parent) : ni(parent) {} + + NifImporter ∋ + + vector<KeyTextValue> BuildKeyValues(NiObjectNETRef nref); + bool AddValues(NiObjectNETRef nref); + bool AddValues(vector<NiObjectNETRef>& nodes); + + bool AddValues(Control *c, NiKeyframeDataRef data, float time); + + 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* GetTMController(const string& name); +}; + +bool NifImporter::ImportAnimation() +{ + if (!enableAnimations) + return false; + + AnimationImport ai(*this); + return ai.AddValues(DynamicCast<NiObjectNET>(nodes[0]->GetChildren())); +} + +bool KFMImporter::ImportAnimation() +{ + bool ok = false; + const float FramesIncrement = 1.0f/30.0f; + int curFrame = 0; + // Read Kf files +#ifdef USE_UNSUPPORTED_CODE + AnimationImport ai(*this); + + float time = 0.0f; + for(vector<NiControllerSequenceRef>::iterator itr = kf.begin(); itr != kf.end(); ++itr){ + + NiControllerSequenceRef cntr = (*itr); + vector<ControllerLink> links = cntr->GetControllerData(); + for (vector<ControllerLink>::iterator lnk=links.begin(); lnk != links.end(); ++lnk){ + + string name = (*lnk).targetName; + if (name.empty()) { + NiStringPaletteRef strings = lnk->stringPalette; + name = strings->GetSubStr((*lnk).nodeNameOffset); + } + if (name.empty()) + continue; + + Control *c = ai.GetTMController(name); + if (NULL == c) + continue; + + float start = cntr->GetStartTime(); + float stop = cntr->GetStopTime(); + if ((*lnk).interpolator){ + if (NiTransformInterpolatorRef interp = (*lnk).interpolator) { + if (NiTransformDataRef data = interp->GetData()){ + ok |= ai.AddValues(c, data, time); + time += (stop-start) + FramesIncrement; // round to nearest 10f ? + } + } + } else if ((*lnk).controller) { + if (NiTransformControllerRef tc = DynamicCast<NiTransformController>((*lnk).controller)) { + if (NiTransformInterpolatorRef interp = tc->GetInterpolator()) { + if (NiTransformDataRef data = interp->GetData()){ + ok |= ai.AddValues(c, data, time); + time += (stop-start) + FramesIncrement; // round to nearest 10f ? + } + } + } else if (NiKeyframeControllerRef kfc = DynamicCast<NiKeyframeController>((*lnk).controller)) { + if (NiKeyframeDataRef kfData = kfc->GetData()) { + ok |= ai.AddValues(c, kfData, time); + time += (stop-start) + FramesIncrement; // round to nearest 10f ? + } + } + } + } + } +#endif + return ok; +} + +Control *AnimationImport::GetTMController(const string& name) +{ + INode *n = ni.gi->GetINodeByName(name.c_str()); + if (NULL == n) + return NULL; + + Control *c = n->GetTMController(); + if (NULL == c) + return NULL; + + // ignore bipeds for now. + if ( (c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) + ||(c->ClassID() == BIPBODY_CONTROL_CLASS_ID) + ||(c->ClassID() == FOOTPRINT_CLASS_ID)) + return NULL; + + return c; +} + +vector<KeyTextValue> AnimationImport::BuildKeyValues(NiObjectNETRef nref) +{ + if (NiTextKeyExtraDataRef keydata = SelectFirstObjectOfType<NiTextKeyExtraData>(nref->GetExtraData())) + return keydata->GetKeys(); + return vector<KeyTextValue>(); +} + +bool AnimationImport::AddValues(vector<NiObjectNETRef>& nodes) +{ + bool ok = false; + for (vector<NiObjectNETRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr) + { + ok |= AddValues(*itr); + NiNodeRef ninode = (*itr); + if (ninode) + ok |= AddValues(DynamicCast<NiObjectNET>(ninode->GetChildren())); + } + return ok; +} + +bool AnimationImport::AddValues(NiObjectNETRef nref) +{ + Control *c = GetTMController(nref->GetName().c_str()); + 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)) { + if (NiTransformInterpolatorRef interp = tc->GetInterpolator()) { + if (NiTransformDataRef data = interp->GetData()) { + return AddValues(c, data, time); + } + } + } else if (NiKeyframeControllerRef kf = SelectFirstObjectOfType<NiKeyframeController>(clist)) { + if (NiKeyframeDataRef kfData = kf->GetData()) { + return AddValues(c, kfData, time); + } + } + return false; +} + +bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time) +{ + // 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); + SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time); + SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time); + SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time); + } + break; + + case QUADRATIC_KEY: + 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); + SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time); + SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time); + SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time); + } + break; + + 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); + SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time); + SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time); + SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time); + } + break; + } + + // Handle Rotation + switch (data->GetRotateType()) + { + 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); + } + 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); + } + 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); + } + break; + + case TBC_KEY: + 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); + } + } 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); + } + } + break; + } + // Handle Scale + switch (data->GetScaleType()) + { + case LINEAR_KEY: + if (Control *subCtrl = MakeScale(c, Class_ID(LININTERP_SCALE_CLASS_ID,0))) { + SetKeys<ILinScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), 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); + } + break; + case TBC_KEY: + if (Control *subCtrl = MakeScale(c, Class_ID(TCBINTERP_SCALE_CLASS_ID,0))) { + SetKeys<ITCBScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), time); + } + break; + } + return true; +} + +Control* AnimationImport::MakeRotation(Control *tmCont, Class_ID rotClsid, Class_ID rollClsid) +{ + Interface *ip = ni.gi; + if (Control *c = tmCont->GetRotationController()) { + if (c->ClassID()!=rotClsid) { + if (Control *tmpCtrl = (Control*)ip->CreateInstance(CTRL_ROTATION_CLASS_ID, rotClsid)) { + if (!tmCont->SetRotationController(tmpCtrl)) + tmpCtrl->DeleteThis(); + else + c = tmpCtrl; + } + } + if (Control *r = tmCont->GetRollController()) { + if (r->ClassID()!=rollClsid) { + if (Control *r = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID,rollClsid)) + if (!tmCont->SetRollController(r)) + r->DeleteThis(); + } + } + return c; + } + return NULL; +} + +Control* AnimationImport::MakeScale(Control *tmCont, Class_ID clsid) +{ + Interface *ip = ni.gi; + if (Control *c = tmCont->GetScaleController()) { + if (c->ClassID()!=clsid) { + if (Control *tmpCtrl = (Control*)ip->CreateInstance(CTRL_SCALE_CLASS_ID, clsid)){ + if (!tmCont->SetScaleController(tmpCtrl)) + tmpCtrl->DeleteThis(); + else + c = tmpCtrl; + } + } + return c; + } + return NULL; +} + +Control* AnimationImport::MakePositionXYZ(Control *tmCont, Class_ID clsid) +{ + Interface *ip = ni.gi; + if (Control *c = tmCont->GetPositionController()){ + // First make the controller and XYZ Independent position controller, then fix individuals + if (c->ClassID()!= IPOS_CONTROL_CLASS_ID) { + if (Control *tmp = (Control*)ip->CreateInstance(CTRL_POSITION_CLASS_ID, IPOS_CONTROL_CLASS_ID)) { + if (!tmCont->SetPositionController(tmp)) { + tmp->DeleteThis(); + } else { + c = tmp; + } + } + } + if (Control *x = c->GetXController()){ + if (x->ClassID()!= clsid) { + if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid)) + c->SetReference(IPOS_X_REF, tmp); + } + } + if (Control *y = c->GetYController()){ + if (y->ClassID()!= clsid) { + if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid)) + c->SetReference(IPOS_Y_REF, tmp); + } + } + if (Control *z = c->GetZController()){ + if (z->ClassID()!= clsid) { + if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid)) + c->SetReference(IPOS_Z_REF, tmp); + } + } + return c; + } + return NULL; +} diff --git a/NifImport/ImportMeshAndSkin.cpp b/NifImport/ImportMeshAndSkin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..28f9bcf6159f46c68a631e90cec94db154df2893 --- /dev/null +++ b/NifImport/ImportMeshAndSkin.cpp @@ -0,0 +1,364 @@ +/********************************************************************** +*< +FILE: ImportMeshAndSkin.cpp + +DESCRIPTION: Mesh and Skin Import routines + +CREATED BY: tazpn (Theo) + +HISTORY: + +*> Copyright (c) 2006, All Rights Reserved. +**********************************************************************/ +#include "stdafx.h" +#include "MaxNifImport.h" + +using namespace Niflib; + +// Locate a TriObject in an Object if it exists +TriObject* GetTriObject(Object *o) +{ + if (o && o->CanConvertToType(triObjectClassID)) + return (TriObject *)o->ConvertToType(0, triObjectClassID); + while (o->SuperClassID() == GEN_DERIVOB_CLASS_ID && o) + { + IDerivedObject* dobj = (IDerivedObject *)(o); + o = dobj->GetObjRef(); + if (o && o->CanConvertToType(triObjectClassID)) + return (TriObject *)o->ConvertToType(0, triObjectClassID); + } + return NULL; +} + +// Get or Create the Skin Modifier +Modifier *GetSkin(INode *node) +{ + 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() == SKIN_CLASSID) + { + // is this the correct Physique Modifier based on index? + return mod; + } + Idx++; + } + pObj = pDerObj->GetObjRef(); + } + + IDerivedObject *dobj = CreateDerivedObject(node->GetObjectRef()); + + //create a skin modifier and add it + Modifier *skinMod = (Modifier*) CreateInstance(OSM_CLASS_ID, SKIN_CLASSID); + dobj->SetAFlag(A_LOCK_TARGET); + dobj->AddModifier(skinMod); + dobj->ClearAFlag(A_LOCK_TARGET); + node->SetObjectRef(dobj); + return skinMod; +} + +bool NifImporter::ImportTransform(ImpNode *node, NiAVObjectRef avObject) +{ + node->SetTransform(0,TOMATRIX3(avObject->GetWorldTransform())); + return true; +} + + +bool NifImporter::ImportMeshes(NiNodeRef node) +{ + bool ok = true; + try + { + vector<NiTriShapeRef> trinodes = DynamicCast<NiTriShape>(node->GetChildren()); + for (vector<NiTriShapeRef>::iterator itr = trinodes.begin(), end = trinodes.end(); itr != end; ++itr){ + ok |= ImportMesh(*itr); + } + vector<NiTriStripsRef> tristrips = DynamicCast<NiTriStrips>(node->GetChildren()); + for (vector<NiTriStripsRef>::iterator itr = tristrips.begin(), end = tristrips.end(); itr != end; ++itr){ + ok |= ImportMesh(*itr); + } + vector<NiNodeRef> nodes = DynamicCast<NiNode>(node->GetChildren()); + for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){ + ok |= ImportMeshes(*itr); + } + } + catch( exception & e ) + { + e=e; + ok = false; + } + catch( ... ) + { + ok = false; + } + return ok; +} + +bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, bool hasTexture) +{ + Mesh& mesh = o->GetMesh(); + + node->SetTransform(0,TOMATRIX3(triGeom->GetLocalTransform())); + + // Vertex info + { + int nVertices = triGeomData->GetVertexCount(); + vector<Vector3> vertices = triGeomData->GetVertices(); + mesh.setNumVerts(nVertices); + for (int i=0; i < nVertices; ++i){ + Vector3 &v = vertices[i]; + mesh.verts[i].Set(v.x, v.y, v.z); + } + } + // uv texture info + { + int nUVSet = triGeomData->GetUVSetCount(); + int n = 0; + for (int j=0; j<nUVSet; j++){ + vector<TexCoord> texCoords = triGeomData->GetUVSet(j); + n = texCoords.size(); + mesh.setNumTVerts(n, FALSE); + for (int i=0; i<n; ++i) { + TexCoord& texCoord = texCoords[i]; + mesh.tVerts[i].Set(texCoord.u, (flipUVTextures) ? -texCoord.v : texCoord.v, 0); + } + } + } + if (removeDegenerateFaces) + mesh.RemoveDegenerateFaces(); + if (removeIllegalFaces) + mesh.RemoveIllegalFaces(); + if (enableAutoSmooth) + mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); + + // Normals + { + mesh.checkNormals(TRUE); + vector<Vector3> n = triGeomData->GetNormals(); + for (int i=0; i<n.size(); i++){ + Vector3 v = n[i]; + mesh.setNormal(i, Point3(v.x, v.y, v.z)); + } + } + // vertex coloring + { + bool hasAlpha = false; + vector<Color4> cv = triGeomData->GetColors(); + mesh.setNumVertCol(cv.size()); + for (int i=0; i<cv.size(); i++){ + Color4& c = cv[i]; + mesh.vertCol[i].Set(c.r, c.g, c.b); + hasAlpha |= (c.a != 0.0); + } + } + return true; +} + +void NifImporter::SetTrangles(Mesh& mesh, vector<Triangle>& v, bool hasTexture) +{ + int n = v.size(); + mesh.setNumFaces(n); + //if (hasTexture) + mesh.setNumTVFaces(n); + for (int i=0; i<n; ++i) { + Triangle& t = v[i]; + Face& f = mesh.faces[i]; + f.setVerts(t.v1, t.v2, t.v3); + f.Show(); + f.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS); + //if (hasTexture) { + TVFace& tf = mesh.tvFace[i]; + tf.setTVerts(t.v1, t.v2, t.v3); + //} + } +} + +bool NifImporter::ImportMesh(NiTriShapeRef triShape) +{ + bool ok = true; + + ImpNode *node = i->CreateNode(); + if(!node) return false; + TriObject *triObject = CreateNewTriObject(); + node->Reference(triObject); + string name = triShape->GetName(); + node->SetName(name.c_str()); + + INode *inode = node->GetINode(); + + // Texture + bool hasTexture = ImportMaterialAndTextures(node, triShape); + Mesh& mesh = triObject->GetMesh(); + NiTriShapeDataRef triShapeData = DynamicCast<NiTriShapeData>(triShape->GetData()); + if (triShapeData == NULL) + return false; + + ok |= ImportMesh(node, triObject, triShape, triShapeData, hasTexture); + + // Triangles and texture vertices + if (ok) + { + vector<Triangle> v = triShapeData->GetTriangles(); + SetTrangles(mesh, v, hasTexture); + } + if (enableSkinSupport) + ImportSkin(node, triShape); + + i->AddNodeToScene(node); + + // attach child + if (INode *parent = GetNode(triShape->GetParent())) + parent->AttachChild(inode, 1); + + inode->Hide(triShape->GetHidden() ? TRUE : FALSE); + + if (enableAutoSmooth){ + if (TriObject *tri = GetTriObject(inode->GetObjectRef())){ + tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); + } + } + + return ok; +} + +bool NifImporter::ImportMesh(NiTriStripsRef triStrips) +{ + bool ok = true; + + ImpNode *node = i->CreateNode(); + if(!node) return false; + INode *inode = node->GetINode(); + TriObject *triObject = CreateNewTriObject(); + node->Reference(triObject); + string name = triStrips->GetName(); + node->SetName(name.c_str()); + + // Texture + bool hasTexture = ImportMaterialAndTextures(node, triStrips); + + Mesh& mesh = triObject->GetMesh(); + NiTriStripsDataRef triStripsData = DynamicCast<NiTriStripsData>(triStrips->GetData()); + if (triStripsData == NULL) + return false; + + ok |= ImportMesh(node, triObject, triStrips, triStripsData, hasTexture); + + // Triangles and texture vertices + if (ok) + { + vector<Triangle> v = triStripsData->GetTriangles(); + SetTrangles(mesh, v, hasTexture); + } + if (enableSkinSupport) + ImportSkin(node, triStrips); + + i->AddNodeToScene(node); + + // attach child + if (INode *parent = GetNode(triStrips->GetParent())) { + parent->AttachChild(inode, 0); + } + + inode->Hide(triStrips->GetHidden() ? TRUE : FALSE); + + // apply autosmooth after object creation for it to take hold + if (enableAutoSmooth){ + if (TriObject *tri = GetTriObject(inode->GetObjectRef())){ + tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); + } + } + + return ok; +} + +struct VertexHolder +{ + VertexHolder() : vertIndex(0), count(0) {} + + int vertIndex; + int count; + Tab<INode*> boneNodeList; + Tab<float> weights; +}; + +bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom) +{ + bool ok = true; + NiSkinInstanceRef nifSkin = triGeom->GetSkinInstance(); + if (!nifSkin) + return false; + + INode *tnode = node->GetINode(); + + NiSkinDataRef data = nifSkin->GetSkinData(); + NiSkinPartitionRef part = nifSkin->GetSkinPartition(); + + vector<NiNodeRef> nifBones = nifSkin->GetBones(); + + //create a skin modifier and add it + Modifier *skinMod = GetSkin(tnode); + TriObject *triObject = GetTriObject(tnode->GetObjectRef()); + Mesh& m = triObject->GetMesh(); + + //get the skin interface + if (ISkin *skin = (ISkin *) skinMod->GetInterface(I_SKIN)){ + ISkinImportData* iskinImport = (ISkinImportData*) skinMod->GetInterface(I_SKINIMPORTDATA); + + // Create Bone List + Tab<INode*> bones; + int i=0; + for (vector<NiNodeRef>::iterator itr = nifBones.begin(), end = nifBones.end(); itr != end; ++itr, ++i){ + string name = (*itr)->GetName(); + 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); + } + ObjectState os = tnode->EvalWorldState(0); + + // Need to trigger ModifyObject in BonesDefMod prior to adding vertices or nothing is added + GetCOREInterface()->ForceCompleteRedraw(); + + // Need to get a list of bones and weights for each vertex. + vector<VertexHolder> vertexHolders; + vertexHolders.resize(m.numVerts); + for (int i=0, n=data->GetBoneCount();i<n; ++i){ + if (INode *boneRef = bones[i]){ + vector<SkinWeight> weights = data->GetBoneWeights(i); + for (vector<SkinWeight>::iterator itr=weights.begin(), end=weights.end(); itr != end; ++itr){ + VertexHolder& h = vertexHolders[itr->index]; + h.vertIndex = itr->index; + ++h.count; + h.weights.Append(1, &itr->weight); + h.boneNodeList.Append(1, &boneRef); + } + } + } + + // Assign the weights + for (vector<VertexHolder>::iterator itr=vertexHolders.begin(), end=vertexHolders.end(); itr != end; ++itr){ + VertexHolder& h = (*itr); + if (h.count){ + float sum = 0.0f; + for (int i=0; i<h.count; ++i) + sum += h.weights[i]; + ASSERT(fabs(sum-1.0) < 0.001); + BOOL add = iskinImport->AddWeights(tnode, h.vertIndex, h.boneNodeList, h.weights); + add = add; + } + } + } + return ok; +} diff --git a/NifImport/ImportMtlAndTex.cpp b/NifImport/ImportMtlAndTex.cpp new file mode 100644 index 0000000000000000000000000000000000000000..038520d9884564974814a1f47f7e0a197bf08fda --- /dev/null +++ b/NifImport/ImportMtlAndTex.cpp @@ -0,0 +1,142 @@ +/********************************************************************** +*< +FILE: ImportMtlAndTex.cpp + +DESCRIPTION: Material and Texture Import routines + +CREATED BY: tazpn (Theo) + +HISTORY: + +*> Copyright (c) 2006, All Rights Reserved. +**********************************************************************/ +#include "stdafx.h" +#include <shaders.h> +#include "MaxNifImport.h" +#include "obj/NiWireframeProperty.h" +#include "obj/NiAlphaProperty.h" +#include "obj/NiStencilProperty.h" +#include "objectParams.h" +using namespace Niflib; + +Texmap* NifImporter::CreateTexture(TexDesc& desc) +{ + BitmapManager *bmpMgr = TheManager; + if (NiSourceTextureRef texSrc = desc.source){ + string filename = texSrc->GetExternalFileName(); + if (bmpMgr->CanImport(filename.c_str())){ + BitmapTex *bmpTex = NewDefaultBitmapTex(); + bmpTex->SetName(texSrc->GetName().c_str()); + bmpTex->SetMapName(const_cast<TCHAR*>(FindImage(filename).c_str())); + bmpTex->SetAlphaAsMono(TRUE); + bmpTex->SetAlphaSource(ALPHA_NONE); + return bmpTex; + } + } + return NULL; +} + +bool NifImporter::ImportMaterialAndTextures(ImpNode *node, NiAVObjectRef avObject) +{ + // Texture + vector<NiPropertyRef> props = avObject->GetProperties(); + NiTexturingPropertyRef texRef = SelectFirstObjectOfType<NiTexturingProperty>(props); + NiMaterialPropertyRef matRef = SelectFirstObjectOfType<NiMaterialProperty>(props); + NiWireframePropertyRef wireRef = SelectFirstObjectOfType<NiWireframeProperty>(props); + NiAlphaPropertyRef alphaRef = SelectFirstObjectOfType<NiAlphaProperty>(props); + NiStencilPropertyRef stencilRef = SelectFirstObjectOfType<NiStencilProperty>(props); + + bool hasTexture = (texRef && matRef); + if (matRef != NULL){ + StdMat2 *m = NewDefaultStdMat(); + m->SetName(matRef->GetName().c_str()); + if (showTextures) + m->SetMtlFlag(MTL_DISPLAY_ENABLE_FLAGS, TRUE); + m->SetAmbient(TOCOLOR(matRef->GetAmbientColor()),0); + m->SetDiffuse(TOCOLOR(matRef->GetDiffuseColor()),0); + m->SetSpecular(TOCOLOR(matRef->GetSpecularColor()),0); + Color c = TOCOLOR(matRef->GetEmissiveColor()); + if (c.r != 0 || c.b != 0 || c.g != 0) { + m->SetSelfIllumColorOn(TRUE); + m->SetSelfIllumColor(c,0); + } + m->SetShinStr(0.0,0); + m->SetShininess(matRef->GetGlossiness()/100.0,0); + m->SetOpacity(matRef->GetTransparency(),0); + + bool hasShaderAttributes = (wireRef != NULL) || (stencilRef != NULL); + if (m->SupportsShaders() && hasShaderAttributes) { + if (Shader *s = m->GetShader()) { + if (wireRef != NULL && (wireRef->GetFlags() & 1)) { + BOOL value = TRUE; + setMAXScriptValue(s->GetReference(0), "wire", 0, value); + } + if (stencilRef != NULL) { + + } + } + } + + + if (NULL != texRef) + { + // Handle Base/Detail ??? + if (texRef->HasTexture(DECAL_0_MAP)){ + if (Texmap* tex = CreateTexture(texRef->GetTexture(DECAL_0_MAP))) + m->SetSubTexmap(ID_DI, tex); + if (texRef->HasTexture(BASE_MAP)){ + m->LockAmbDiffTex(FALSE); + if (Texmap* tex = CreateTexture(texRef->GetTexture(BASE_MAP))) + m->SetSubTexmap(ID_AM, tex); + } + } else if (texRef->HasTexture(BASE_MAP)) { + if (Texmap* tex = CreateTexture(texRef->GetTexture(BASE_MAP))) + m->SetSubTexmap(ID_DI, tex); + } + // Handle Bump map + if (texRef->HasTexture(BUMP_MAP)) { + if (Texmap* tex = CreateTexture(texRef->GetTexture(BUMP_MAP))) + m->SetSubTexmap(ID_BU, tex); + } + // Shiny map + if (texRef->HasTexture(GLOSS_MAP)) { + if (Texmap* tex = CreateTexture(texRef->GetTexture(GLOSS_MAP))) + m->SetSubTexmap(ID_SS, tex); + } + + // Self illumination + if (texRef->HasTexture(GLOW_MAP)) { + if (Texmap* tex = CreateTexture(texRef->GetTexture(GLOW_MAP))) + m->SetSubTexmap(ID_SI, tex); + } + } + gi->GetMaterialLibrary().Add(m); + node->GetINode()->SetMtl(m); + } + return hasTexture; +} + +string NifImporter::FindImage(const string& name) +{ + TCHAR buffer[MAX_PATH]; + + // Simply check for fully qualified path + if (PathIsRoot(name.c_str())) { + if (-1 != _taccess(name.c_str(), 0)) + return string(buffer); + } + if (!path.empty()) { + PathCombine(buffer, path.c_str(), name.c_str()); // try as-is + if (-1 != _taccess(buffer, 0)) + return string(buffer); + + // try only filename in nif directory + PathCombine(buffer, path.c_str(), PathFindFileName(name.c_str())); + if (-1 != _taccess(buffer, 0)) + return string(buffer); + } + if (appSettings != NULL) { + return appSettings->FindImage(name); + } + return name; +} \ No newline at end of file diff --git a/NifImport/ImportSkeleton.cpp b/NifImport/ImportSkeleton.cpp new file mode 100644 index 0000000000000000000000000000000000000000..926801979509242d99ba103afb0bad77d92c6bb1 --- /dev/null +++ b/NifImport/ImportSkeleton.cpp @@ -0,0 +1,446 @@ +/********************************************************************** +*< +FILE: ImportSkeleton.cpp + +DESCRIPTION: Skeleton import routines + +CREATED BY: tazpn (Theo) + +HISTORY: + +*> Copyright (c) 2006, All Rights Reserved. +**********************************************************************/ +#include "stdafx.h" +#include "MaxNifImport.h" +#include <obj/NiTriBasedGeom.h> +#include <obj/NiTriBasedGeomData.h> +#include <obj/NiTimeController.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 (uint i = 0; i < blocks.size(); ++i) { + NiNodeRef node = blocks[i]; + if ( node != NULL && node->IsSkeletonRoot() ) { + node->GoToSkeletonBindPosition(); + } + } +} + +bool NifImporter::HasSkeleton() +{ + if (!skeletonCheck.empty()){ + vector<NiNodeRef> bipedRoots = SelectNodesByName(nodes, skeletonCheck.c_str()); + return !bipedRoots.empty(); + } + return false; +} + + +bool NifImporter::IsBiped() +{ + if (hasSkeleton){ + list<NiExtraDataRef> extraData = nodes[0]->GetExtraData(); + if (!extraData.empty()) { + return ( DynamicCast<BSBound>(extraData).size() != 0 ); + } + } + return false; +} + +void NifImporter::ImportBipeds(vector<NiNodeRef>& nodes) +{ + IBipMaster* master = NULL; + 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) + { + string bipname = (*bipedItr)->GetName(); + string match = bipname + "*"; + 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 + float angle = TORAD(bipedAngle); + Point3 wpos(0.0f,0.0f,0.0f); + BOOL arms = (CountNodesByName(bipedNodes, FormatText("%s L UpperArm", bipname.c_str())) > 0) ? TRUE : FALSE; + BOOL triPelvis = bipedTrianglePelvis ? TRUE : FALSE; + int nnecklinks=CountNodesByName(bipedNodes, FormatText("%s Neck*", bipname.c_str())); + int nspinelinks=CountNodesByName(bipedNodes, FormatText("%s Spine*", bipname.c_str())); + int nleglinks = 3 + CountNodesByName(bipedNodes, FormatText("%s L HorseLink", bipname.c_str())); + int ntaillinks = CountNodesByName(bipedNodes, FormatText("%s Tail*", bipname.c_str())); + int npony1links = CountNodesByName(bipedNodes, FormatText("%s Ponytail1*", bipname.c_str())); + int npony2links = CountNodesByName(bipedNodes, FormatText("%s Ponytail2*", bipname.c_str())); + int numfingers = CountNodesByName(bipedNodes, FormatText("%s L Finger?", bipname.c_str())); + int nfinglinks = CountNodesByName(bipedNodes, FormatText("%s L Finger0*", bipname.c_str())); + int numtoes = CountNodesByName(bipedNodes, FormatText("%s L Toe?", bipname.c_str())); + int ntoelinks = CountNodesByName(bipedNodes, FormatText("%s L Toe0*", bipname.c_str())); + BOOL prop1exists = CountNodesByName(bipedNodes, FormatText("%s Prop1", bipname.c_str())) ? TRUE : FALSE; + BOOL prop2exists = CountNodesByName(bipedNodes, FormatText("%s Prop2", bipname.c_str())) ? TRUE : FALSE; + BOOL prop3exists = CountNodesByName(bipedNodes, FormatText("%s Prop3", bipname.c_str())) ? TRUE : FALSE; + int forearmTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Fore*Twist*", bipname.c_str())); + int upperarmTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Up*Twist*", bipname.c_str())); + int thighTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Thigh*Twist*", bipname.c_str())); + int calfTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Calf*Twist*", bipname.c_str())); + int horseTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Horse*Twist*", bipname.c_str())); + + NiNodeRef root = nodes[0]; + IBipMaster* 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); + master->SetRootName(const_cast<TCHAR*>(bipname.c_str())); + if (master) + { + master->BeginModes(BMODE_FIGURE, FALSE); + master->SetTrianglePelvis(FALSE); + master->SetDisplaySettings(BDISP_BONES); + LPCTSTR bipname = master->GetRootName(); + + // Rename twists, if necessary + 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); + } + } + } + catch( exception & e ) + { + e=e; + } + catch( ... ) + { + } + if (master) + master->EndModes(BMODE_FIGURE, TRUE); +} + +void NifImporter::AlignBiped(IBipMaster* master, NiNodeRef block) +{ + 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); + } + else + { + + } + vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren()); + for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){ + AlignBiped(master, *itr); + } +} + +void NifImporter::PositionBiped(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) + { + 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); + } + else + { + + } + if (Recurse) + { + vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren()); + for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){ + PositionBiped(master, *itr, Recurse); + } + } +} + +void NifImporter::RotateBiped(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) + { + 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); + } + else + { + + } + if (Recurse) + { + vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren()); + for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){ + RotateBiped(master, *itr, Recurse); + } + } +} + +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) + { + 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); + } + else + { + + } + 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); + } + } +} + + +INode *NifImporter::CreateBone(const string& 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) + { + n->SetName(const_cast<TCHAR*>(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), "width", 0, width); + setMAXScriptValue(o->GetReference(0), "height", 0, width); + } + n->ShowBone(1); + } + return result.n; + } + fpBones->ReleaseInterface(); + } + return NULL; +} + +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))); + 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; +} + +void NifImporter::ImportBones(vector<NiNodeRef>& bones) +{ + for (vector<NiNodeRef>::iterator itr = bones.begin(), end = bones.end(); itr != end; ++itr){ + ImportBones(*itr); + } +} + +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; +} + +void NifImporter::ImportBones(NiNodeRef node) +{ + try + { + string name = node->GetName(); + vector<NiAVObjectRef> children = node->GetChildren(); + vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children); + + NiAVObject::CollisionType cType = node->GetCollision(); + if (cType == NiAVObject::ctBoundingBox && children.empty() && name=="Bounding Box") + return; + + NiNodeRef parent = node->GetParent(); + + PosRotScale prs = prsDefault; + Matrix44 m4 = node->GetWorldTransform(); + 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,1,0); + 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) + { + float len = node->GetLocalTranslation().Magnitude(); + ppos = pos + Vector3(len/3.0f, 0.0f, 0.0f); + } + Point3 pp(ppos.x, ppos.y, ppos.z); + + INode *bone = gi->GetINodeByName(name.c_str()); + if (bone) + { + // Is there a better way of "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 + { + list<NiTimeControllerRef> ctrls = node->GetControllers(); + if (parent == NULL || parent->GetParent() == NULL || ctrls.empty()) + { + 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 (bone) + { + if (parent) + { + if (INode *pn = gi->GetINodeByName(parent->GetName().c_str())) + pn->AttachChild(bone, 1); + } + bone->Hide(node->GetHidden() ? TRUE : FALSE); + } + } + if (bone) + { + ImportBones(childNodes); + } + } + catch( exception & e ) + { + e=e; + } + catch( ... ) + { + } +} \ No newline at end of file diff --git a/NifImport/KFMImporter.cpp b/NifImport/KFMImporter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dcdc4ef98c0e6c9cdf2990dc619812826a55bbb0 --- /dev/null +++ b/NifImport/KFMImporter.cpp @@ -0,0 +1,63 @@ +#include "StdAfx.h" +#include "KFMImporter.h" +#include <kfm.h> +#include "gen/ControllerLink.h" +using namespace Niflib; + +KFMImporter::KFMImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts) + : BaseClass() +{ + BaseInit(Name, I, GI, SuppressPrompts); +} + +KFMImporter::~KFMImporter(void) +{ +} + +void KFMImporter::ReadBlocks() +{ + try + { + Kfm kfm; + int ver = kfm.Read(name); + if (ver != VER_UNSUPPORTED) + { + TCHAR buffer[MAX_PATH]; + GetFullPathName(name.c_str(), MAX_PATH, buffer, NULL); + PathRemoveFileSpec(buffer); +#ifdef USE_UNSUPPORTED_CODE + //root = kfm.MergeActions(string(buffer)); + BuildNodes(); + + string last_file; + vector<NiControllerSequenceRef> ctrllist; + + for ( vector<KfmAction>::iterator it = kfm.actions.begin(); it != kfm.actions.end(); it++ ) { + string action_filename = path + '\\' + it->action_filename; + if (-1 != _taccess(action_filename.c_str(), 0)){ + if (action_filename != last_file) { + ctrllist = DynamicCast<NiControllerSequence>(ReadNifList(action_filename)); + } + if ((*it).unk_int2 && (*it).unk_int2 <= ctrllist.size()) { + if (NiControllerSequenceRef ctrlSeq = ctrllist[(*it).unk_int2-1]) + kf.push_back(ctrlSeq); + } + } + } +#endif + } + } + catch (std::exception&) + { + } + catch (...) + { + } +} + +bool KFMImporter::DoImport() +{ + // might check if blocks exist and if not go ahead and import nif. + return ImportAnimation(); + //return BaseClass::DoImport(); +} diff --git a/NifImport/KFMImporter.h b/NifImport/KFMImporter.h new file mode 100644 index 0000000000000000000000000000000000000000..241f706fd1a90a3a08c0c87717414a165235fcdf --- /dev/null +++ b/NifImport/KFMImporter.h @@ -0,0 +1,41 @@ +/********************************************************************** +*< +FILE: KFMImporter.h + +DESCRIPTION: KFM Importer helper class + +CREATED BY: tazpn (Theo) + +HISTORY: + +*> Copyright (c) 2006, All Rights Reserved. +**********************************************************************/ +#ifndef _KFMIMPORTER_H_ +#define _KFMIMPORTER_H_ + +#pragma once +#include "NIFImporter.h" +#include "niutils.h" +#include "obj/NiControllerSequence.h" + +class KFMImporter : public NifImporter +{ + typedef NifImporter BaseClass; +public: + KFMImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts); + ~KFMImporter(void); + + void ReadBlocks(); + virtual bool DoImport(); + + virtual bool isValid() const { return (NULL != root) || !kf.empty(); } + + // Implemented in ImportAnimation.cpp + virtual bool ImportAnimation(); + + + std::vector<Niflib::NiControllerSequenceRef> kf; +}; + + +#endif \ No newline at end of file diff --git a/NifImport/MaxNifImport.cpp b/NifImport/MaxNifImport.cpp index d862c802121665c7e8d5218c5692216621020831..aa456df63a2da1cf64eb602b2c09feabc80ce4e3 100644 --- a/NifImport/MaxNifImport.cpp +++ b/NifImport/MaxNifImport.cpp @@ -11,200 +11,13 @@ *> Copyright (c) 2006, All Rights Reserved. **********************************************************************/ #include "stdafx.h" -#include <math.h> -#include <io.h> -#include <sstream> #include "MaxNifImport.h" -#include <cs/Biped8Api.h> -#include <scenetraversal.h> -#include <plugapi.h> -#include <triobj.h> -#include <bitmap.h> -#include <modstack.h> -#include <iskin.h> -#include <strclass.h> -#include "objectParams.h" -#undef ALPHA_NONE - -#include <math.h> -#include <string.h> - -#include "niflib.h" -#include "obj\NiObject.h" -#include "obj\NiNode.h" -#include "obj\NiTriShape.h" -#include "obj\NiTriShapeData.h" -#include "obj\NiTriStrips.h" -#include "obj\NiTriStripsData.h" -#include "obj\NiMaterialProperty.h" -#include "obj\NiTexturingProperty.h" -#include "obj\NiSourceTexture.h" -#include "obj\NiExtraData.h" -#include "obj\BSBound.h" -#include "obj\NiSkinData.h" -#include "obj\NiSkinInstance.h" -#include "obj\NiSkinPartition.h" - -#include "niutils.h" -#include "AppSettings.h" - -#ifndef ASSERT -#ifdef _DEBUG -#include <crtdbg.h> -#define ASSERT _ASSERTE -#else -#define ASSERT(exp) -#endif -#endif - +#include "KFMImporter.h" using namespace Niflib; #define MaxNifImport_CLASS_ID Class_ID(0x794ac1c1, 0x8b4c64c7) -// Define the standard section names used in the ini file -LPCTSTR NifImportSection = TEXT("MaxNifImport"); -LPCTSTR SystemSection = TEXT("System"); -LPCTSTR BipedImportSection = TEXT("BipedImport"); - -struct NiNodeNameEquivalence : public NumericStringEquivalence -{ - bool operator()(const NiNodeRef& n1, const NiNodeRef& n2) const { - return NumericStringEquivalence::operator()(n1->GetName(), n2->GetName()); - } -}; - -// NIF Importer -class NifImporter -{ -public: - string name; - string path; - ImpInterface *i; - Interface *gi; - BOOL suppressPrompts; - bool iniFileValid; - string iniFileName; - AppSettings *appSettings; - - // Ini settings - bool showTextures; // show textures in viewport - bool removeIllegalFaces; - bool removeDegenerateFaces; - bool enableAutoSmooth; - float autoSmoothAngle; - bool flipUVTextures; - bool enableSkinSupport; - bool goToSkeletonBindPosition; - - // Biped/Bones related settings - string skeleton; - float bipedHeight; - string skeletonCheck; - float bipedAngle; - float bipedAnkleAttach; - bool bipedTrianglePelvis; - bool importSkeleton; - bool useBiped; - bool hasSkeleton; - bool isBiped; - bool removeUnusedImportedBones; - bool forceRotation; - bool browseForSkeleton; - string defaultSkeletonName; - - float minBoneWidth; - float maxBoneWidth; - float boneWidthToLengthRatio; - - vector<NiObjectRef> blocks; - vector<NiNodeRef> nodes; - - NifImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts) - : name(Name), i(I), gi(GI), suppressPrompts(SuppressPrompts) - { - char buffer[MAX_PATH] = {0}, *p = NULL; - GetFullPathName(Name, _countof(buffer), buffer, &p); - if (p) *p = 0; - path = buffer; - - // Load ini settings - iniFileValid = false; - LoadIniSettings(); - - // load file - blocks = ReadNifList( name ); - nodes = DynamicCast<NiNode>(blocks); - - // Apply post processing checks after reading blocks - if (isValid()){ - if (goToSkeletonBindPosition && !nodes.empty()) - GoToSkeletonBindPosition(nodes); - - hasSkeleton = HasSkeleton(); - isBiped = IsBiped(); - skeleton = (appSettings != NULL) ? appSettings->Skeleton : ""; - importSkeleton &= hasSkeleton; - - // Guess that the skeleton is the same one in the current directory - if (importSkeleton && !defaultSkeletonName.empty()) { - TCHAR buffer[MAX_PATH]; - GetFullPathName(name.c_str(), _countof(buffer), buffer, NULL); - PathRemoveFileSpec(buffer); - PathAddBackslash(buffer); - PathAppend(buffer, defaultSkeletonName.c_str()); - if (-1 != _taccess(buffer, 0)) - skeleton = buffer; - } - } - } - bool isValid() const { return (0 != blocks.size()); } - - // Ini File related routines - void LoadIniSettings(); - void SaveIniSettings(); - - // Generic IniFile reading routine - template<typename T> - T GetIniValue(LPCTSTR Section, LPCTSTR Setting, T Default){ - if (!iniFileValid) - return Default; - return ::GetIniValue<T>(Section, Setting, Default, iniFileName.c_str()); - } - // Generic IniFile reading routine - template<typename T> - void SetIniValue(LPCTSTR Section, LPCTSTR Setting, T value){ - if (!iniFileValid) - return; - ::SetIniValue<T>(Section, Setting, value, iniFileName.c_str()); - } - - bool HasSkeleton(); - bool IsBiped(); - void ImportBones(vector<NiNodeRef>& bones); - void ImportBones(NiNodeRef blocks); - void ImportBipeds(vector<NiNodeRef>& blocks); - void AlignBiped(IBipMaster* master, NiNodeRef block); - void PositionBiped(IBipMaster* master, NiNodeRef block, bool Recurse = false); - void RotateBiped(IBipMaster* master, NiNodeRef block, bool Recurse = false); - void ScaleBiped(IBipMaster* master, NiNodeRef block, bool Recurse = false); - bool ImportMeshes(NiNodeRef block); - string FindImage(const string& name); - - void SetTrangles(Mesh& mesh, vector<Triangle>& v, bool hasTexture); - bool ImportMesh(NiTriShapeRef triShape); - bool ImportMesh(NiTriStripsRef triStrips); - bool ImportMaterialAndTextures(ImpNode *node, NiAVObjectRef avObject); - bool ImportTransform(ImpNode *node, NiAVObjectRef avObject); - bool ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, bool hasTexture); - - bool ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom); - Texmap* CreateTexture(TexDesc& desc); - - INode *CreateBone(const string& name, Point3 startPos, Point3 endPos, Point3 zAxis); -}; - - - +string shortDescription; class MaxNifImport : public SceneImport { public: @@ -228,7 +41,6 @@ class MaxNifImport : public SceneImport { ~MaxNifImport(); string iniFileName; - string shortDescription; }; @@ -307,7 +119,7 @@ MaxNifImport::MaxNifImport() PathAppend(iniName, "MaxNifTools.ini"); } iniFileName = iniName; - shortDescription = GetIniValue<string>(SystemSection, "ShortDescription", "Netimmerse/Gamebryo", iniFileName.c_str()); + shortDescription = GetIniValue<string>("System", "ShortDescription", "Netimmerse/Gamebryo", iniFileName.c_str()); } MaxNifImport::~MaxNifImport() @@ -317,14 +129,21 @@ MaxNifImport::~MaxNifImport() int MaxNifImport::ExtCount() { - //TODO: Returns the number of file name extensions supported by the plug-in. - return 1; +#ifdef USE_UNSUPPORTED_CODE + return 2; +#else + return 1; +#endif } const TCHAR *MaxNifImport::Ext(int n) { - //TODO: Return the 'i-th' file name extension (i.e. "3DS"). - return _T("NIF"); + switch (n) + { + case 0: return _T("NIF"); + case 1: return _T("KFM"); + } + return _T(""); } const TCHAR *MaxNifImport::LongDesc() @@ -401,96 +220,24 @@ int MaxNifImport::DoImport(const TCHAR *filename,ImpInterface *i, Interface *gi, std::string current_file = filename; - NifImporter importer(filename, i, gi, suppressPrompts); - if (!importer.isValid()) - return FALSE; - - //if(!suppressPrompts) - // DialogBoxParam(hInstance, - // MAKEINTRESOURCE(IDD_PANEL), - // GetActiveWindow(), - // MaxNifImportOptionsDlgProc, (LPARAM)&importer); + LPCTSTR ext = PathFindExtension(filename); - // - if (importer.isBiped) + if (_tcsicmp(ext, ".KFM") == 0) { - if (importer.useBiped){ - importer.ImportBipeds(importer.nodes); - } else { - importer.ImportBones(DynamicCast<NiNode>(importer.nodes[0]->GetChildren())); - } + KFMImporter importer(filename, i, gi, suppressPrompts); + if (!importer.isValid()) + return FALSE; + + ok = importer.DoImport(); + } else { - vector<string> importedBones; - if (importer.importSkeleton) - { - if (importer.browseForSkeleton) - { - TCHAR filter[64], *pfilter=filter; - pfilter = _tcscpy(filter, this->ShortDesc()); - pfilter = _tcscat(pfilter, " (*.NIF)"); - pfilter += strlen(pfilter); - *pfilter++ = '\0'; - _tcscpy(pfilter, "*.NIF"); - pfilter += strlen(pfilter); - *pfilter++ = '\0'; - *pfilter++ = '\0'; - - TCHAR filename[MAX_PATH]; - GetFullPathName(importer.skeleton.c_str(), _countof(filename), filename, NULL); - - OPENFILENAME ofn; - memset(&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = gi->GetMAXHWnd(); - ofn.lpstrFilter = filter; - ofn.lpstrFile = filename; - ofn.nMaxFile = _countof(filename); - ofn.lpstrTitle = TEXT("Browse for Skeleton NIF..."); - ofn.lpstrDefExt = TEXT("NIF"); - ofn.Flags = OFN_HIDEREADONLY|OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST; - importer.importSkeleton = GetOpenFileName(&ofn) ? true : false; - if (importer.importSkeleton) { - importer.skeleton = filename; - } - } - if (importer.importSkeleton && !importer.skeleton.empty()) { - NifImporter skelImport(importer.skeleton.c_str(), i, gi, suppressPrompts); - if (skelImport.isValid()) - { - if (skelImport.useBiped){ - skelImport.ImportBipeds(skelImport.nodes); - } else { - skelImport.ImportBones(DynamicCast<NiNode>(skelImport.nodes[0]->GetChildren())); - if (importer.removeUnusedImportedBones){ - importedBones = GetNamesOfNodes(skelImport.nodes); - } - } - } - } - } - if (importer.isValid()) { - - importer.ImportBones(DynamicCast<NiNode>(importer.nodes[0]->GetChildren())); - ok = importer.ImportMeshes(importer.nodes[0]); + NifImporter importer(filename, i, gi, suppressPrompts); + if (!importer.isValid()) + return FALSE; - if (importer.importSkeleton && importer.removeUnusedImportedBones){ - vector<string> importedNodes = GetNamesOfNodes(importer.nodes); - sort(importedBones.begin(), importedBones.end()); - sort(importedNodes.begin(), importedNodes.end()); - vector<string> results; - results.resize(importedBones.size()); - vector<string>::iterator end = set_difference ( - importedBones.begin(), importedBones.end(), - 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); - } - } - } - } + ok = importer.DoImport(); } } catch( exception & e ) @@ -510,808 +257,3 @@ int MaxNifImport::DoImport(const TCHAR *filename,ImpInterface *i, Interface *gi, return ok ? TRUE : FALSE; } -void NifImporter::LoadIniSettings() -{ - TCHAR iniName[MAX_PATH]; - LPCTSTR pluginDir = gi->GetDir(APP_PLUGCFG_DIR); - PathCombine(iniName, pluginDir, "MaxNifTools.ini"); - this->iniFileName = iniName; - iniFileValid = (-1 != _access(iniName, 0)); - - // Locate which application to use. If Auto, find first app where this file appears in the root path list - appSettings = NULL; - string curapp = GetIniValue<string>(NifImportSection, "CurrentApp", "AUTO"); - if (0 == _tcsicmp(curapp.c_str(), "AUTO")) { - // Scan Root paths - for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){ - if ((*itr).IsFileInRootPaths(this->name)) { - appSettings = &(*itr); - break; - } - } - } else { - appSettings = FindAppSetting(curapp); - } - if (appSettings == NULL && !TheAppSettings.empty()){ - appSettings = &TheAppSettings.front(); - } - - useBiped = GetIniValue<bool>(NifImportSection, "UseBiped", false); - skeletonCheck = GetIniValue<string>(NifImportSection, "SkeletonCheck", "Bip*"); - showTextures = GetIniValue<bool>(NifImportSection, "ShowTextures", true); - - removeIllegalFaces = GetIniValue<bool>(NifImportSection, "RemoveIllegalFaces", true); - removeDegenerateFaces = GetIniValue<bool>(NifImportSection, "RemoveDegenerateFaces", true); - enableAutoSmooth = GetIniValue<bool>(NifImportSection, "EnableAutoSmooth", true); - autoSmoothAngle = GetIniValue<float>(NifImportSection, "AutoSmoothAngle", 30.0f); - flipUVTextures = GetIniValue<bool>(NifImportSection, "FlipUVTextures", true); - enableSkinSupport = GetIniValue<bool>(NifImportSection, "EnableSkinSupport", true); - - bipedHeight = GetIniValue<float>(BipedImportSection, "BipedHeight", 131.90f); - bipedAngle = GetIniValue<float>(BipedImportSection, "BipedAngle", 90.0f); - bipedAnkleAttach = GetIniValue<float>(BipedImportSection, "BipedAnkleAttach", 0.2f); - bipedTrianglePelvis = GetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", false); - removeUnusedImportedBones = GetIniValue<bool>(BipedImportSection, "RemoveUnusedImportedBones", false); - forceRotation = GetIniValue<bool>(BipedImportSection, "ForceRotation", true); - browseForSkeleton = GetIniValue<bool>(BipedImportSection, "BrowseForSkeleton", true); - defaultSkeletonName = GetIniValue<string>(BipedImportSection, "DefaultSkeletonName", "Skeleton.Nif"); - - minBoneWidth = GetIniValue<float>(BipedImportSection, "MinBoneWidth", 0.5f); - maxBoneWidth = GetIniValue<float>(BipedImportSection, "MaxBoneWidth", 3.0f); - boneWidthToLengthRatio = GetIniValue<float>(BipedImportSection, "BoneWidthToLengthRatio", 0.25f); - - goToSkeletonBindPosition = (appSettings ? appSettings->goToSkeletonBindPosition : false); -} - -void NifImporter::SaveIniSettings() -{ - SetIniValue<bool>(NifImportSection, "UseBiped", useBiped); - SetIniValue<string>(NifImportSection, "Skeleton", skeleton); - SetIniValue<string>(NifImportSection, "SkeletonCheck", skeletonCheck); - - SetIniValue<float>(BipedImportSection, "BipedHeight", bipedHeight); - SetIniValue<float>(BipedImportSection, "BipedAngle", bipedAngle); - SetIniValue<float>(BipedImportSection, "BipedAnkleAttach", bipedAnkleAttach); - SetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", bipedTrianglePelvis); -} - -bool NifImporter::HasSkeleton() -{ - if (!skeletonCheck.empty()){ - vector<NiNodeRef> bipedRoots = SelectNodesByName(nodes, skeletonCheck.c_str()); - return !bipedRoots.empty(); - } - return false; -} - -bool NifImporter::IsBiped() -{ - if (hasSkeleton){ - list<NiExtraDataRef> extraData = nodes[0]->GetExtraData(); - if (!extraData.empty()) { - return ( DynamicCast<BSBound>(extraData).size() != 0 ); - } - } - return false; -} - -void NifImporter::ImportBipeds(vector<NiNodeRef>& nodes) -{ - IBipMaster* master = NULL; - 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) - { - string bipname = (*bipedItr)->GetName(); - string match = bipname + "*"; - 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 - float angle = TORAD(bipedAngle); - Point3 wpos(0.0f,0.0f,0.0f); - BOOL arms = (CountNodesByName(bipedNodes, FormatText("%s L UpperArm", bipname.c_str())) > 0) ? TRUE : FALSE; - BOOL triPelvis = bipedTrianglePelvis ? TRUE : FALSE; - int nnecklinks=CountNodesByName(bipedNodes, FormatText("%s Neck*", bipname.c_str())); - int nspinelinks=CountNodesByName(bipedNodes, FormatText("%s Spine*", bipname.c_str())); - int nleglinks = 3 + CountNodesByName(bipedNodes, FormatText("%s L HorseLink", bipname.c_str())); - int ntaillinks = CountNodesByName(bipedNodes, FormatText("%s Tail*", bipname.c_str())); - int npony1links = CountNodesByName(bipedNodes, FormatText("%s Ponytail1*", bipname.c_str())); - int npony2links = CountNodesByName(bipedNodes, FormatText("%s Ponytail2*", bipname.c_str())); - int numfingers = CountNodesByName(bipedNodes, FormatText("%s L Finger?", bipname.c_str())); - int nfinglinks = CountNodesByName(bipedNodes, FormatText("%s L Finger0*", bipname.c_str())); - int numtoes = CountNodesByName(bipedNodes, FormatText("%s L Toe?", bipname.c_str())); - int ntoelinks = CountNodesByName(bipedNodes, FormatText("%s L Toe0*", bipname.c_str())); - BOOL prop1exists = CountNodesByName(bipedNodes, FormatText("%s Prop1", bipname.c_str())) ? TRUE : FALSE; - BOOL prop2exists = CountNodesByName(bipedNodes, FormatText("%s Prop2", bipname.c_str())) ? TRUE : FALSE; - BOOL prop3exists = CountNodesByName(bipedNodes, FormatText("%s Prop3", bipname.c_str())) ? TRUE : FALSE; - int forearmTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Fore*Twist*", bipname.c_str())); - int upperarmTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Up*Twist*", bipname.c_str())); - int thighTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Thigh*Twist*", bipname.c_str())); - int calfTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Calf*Twist*", bipname.c_str())); - int horseTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Horse*Twist*", bipname.c_str())); - - NiNodeRef root = nodes[0]; - IBipMaster* master = CreateNewBiped(height, angle, wpos, arms, triPelvis, nnecklinks, nspinelinks, - nleglinks, ntaillinks, npony1links, npony2links, numfingers, nfinglinks, numtoes, - ntoelinks, bipedAnkleAttach, prop1exists, prop2exists, prop3exists, - forearmTwistLinks, upperarmTwistLinks, thighTwistLinks, - calfTwistLinks, horseTwistLinks); - master->SetRootName(const_cast<TCHAR*>(bipname.c_str())); - if (master) - { - master->BeginModes(BMODE_FIGURE, FALSE); - master->SetTrianglePelvis(FALSE); - master->SetDisplaySettings(BDISP_BONES); - LPCTSTR bipname = master->GetRootName(); - - // Rename twists, if necessary - 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); - } - } - } - catch( exception & e ) - { - e=e; - } - catch( ... ) - { - } - if (master) - master->EndModes(BMODE_FIGURE, TRUE); -} - -void NifImporter::AlignBiped(IBipMaster* master, NiNodeRef block) -{ - 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); - } - else - { - - } - vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren()); - for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){ - AlignBiped(master, *itr); - } -} - -void NifImporter::PositionBiped(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) - { - 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); - } - else - { - - } - if (Recurse) - { - vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren()); - for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){ - PositionBiped(master, *itr, Recurse); - } - } -} - -void NifImporter::RotateBiped(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) - { - 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); - } - else - { - - } - if (Recurse) - { - vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren()); - for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){ - RotateBiped(master, *itr, Recurse); - } - } -} - -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) - { - 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); - } - else - { - - } - 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); - } - } -} - - -INode *NifImporter::CreateBone(const string& 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) - { - n->SetName(const_cast<TCHAR*>(name.c_str())); - float len = fabs(Length(startPos)-Length(endPos)); - float width = max(minBoneWidth, min(maxBoneWidth, len * boneWidthToLengthRatio)); - if (Object* o = n->GetObjectRef()) - { - setMAXScriptValue(o->GetReference(0), "width", 0, width); - setMAXScriptValue(o->GetReference(0), "height", 0, width); - } - } - return result.n; - } - fpBones->ReleaseInterface(); - } - return NULL; -} - -void NifImporter::ImportBones(vector<NiNodeRef>& bones) -{ - for (vector<NiNodeRef>::iterator itr = bones.begin(), end = bones.end(); itr != end; ++itr){ - ImportBones(*itr); - } -} - -void NifImporter::ImportBones(NiNodeRef node) -{ - try - { - string name = node->GetName(); - vector<NiNodeRef> children = DynamicCast<NiNode>(node->GetChildren()); - NiNodeRef parent = node->GetParent(); - - PosRotScale prs = prsDefault; - Matrix3 im = TOMATRIX3(node->GetWorldTransform(), true); - Point3 p = im.GetTrans(); - Quat q(im); - q.Normalize(); - Vector3 ppos; - Point3 zAxis(0,1,0); - if (!children.empty()) { - for (vector<NiNodeRef>::iterator itr=children.begin(), end = children.end(); itr != end; ++itr) { - Matrix44 cwt = (*itr)->GetWorldTransform(); - Vector3 cpos; Matrix33 crot; float cscale; - cwt.Decompose(cpos, crot, cscale); - ppos += cpos; - } - ppos /= children.size(); - } - else if (parent) - { - Matrix44 pwt = parent->GetWorldTransform(); - Matrix33 prot; float pscale; - pwt.Decompose(ppos, prot, pscale); - if (forceRotation) - prs = prsPos; - } - else - { - if (forceRotation) - prs = prsPos; - } - Point3 pp(ppos.x, ppos.y, ppos.z); - - INode *bone = gi->GetINodeByName(name.c_str()); - if (bone) - { - // Is there a better way of "Affect Pivot Only" behaviors? - INode *pinode = bone->GetParentNode(); - if (pinode) - bone->Detach(0,1); - PositionAndRotateNode(bone, p, q, prs); - if (pinode) - pinode->AttachChild(bone, 1); - } - else - { - if (bone = CreateBone(name, p, pp, zAxis)) - { - PositionAndRotateNode(bone, p, q, prs); - if (parent) - { - if (INode *pn = gi->GetINodeByName(parent->GetName().c_str())) - pn->AttachChild(bone, 1); - } - } - } - if (bone) - { - ImportBones(children); - } - } - catch( exception & e ) - { - e=e; - } - catch( ... ) - { - } -} - -bool NifImporter::ImportMeshes(NiNodeRef node) -{ - bool ok = true; - try - { - vector<NiTriShapeRef> trinodes = DynamicCast<NiTriShape>(node->GetChildren()); - for (vector<NiTriShapeRef>::iterator itr = trinodes.begin(), end = trinodes.end(); ok && itr != end; ++itr){ - ok |= ImportMesh(*itr); - } - vector<NiTriStripsRef> tristrips = DynamicCast<NiTriStrips>(node->GetChildren()); - for (vector<NiTriStripsRef>::iterator itr = tristrips.begin(), end = tristrips.end(); ok && itr != end; ++itr){ - ok |= ImportMesh(*itr); - } - vector<NiNodeRef> nodes = DynamicCast<NiNode>(node->GetChildren()); - for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){ - ok |= ImportMeshes(*itr); - } - } - catch( exception & e ) - { - e=e; - ok = false; - } - catch( ... ) - { - ok = false; - } - return ok; -} - -Texmap* NifImporter::CreateTexture(TexDesc& desc) -{ - BitmapManager *bmpMgr = TheManager; - if (NiSourceTextureRef texSrc = desc.source){ - string filename = texSrc->GetExternalFileName(); - if (bmpMgr->CanImport(filename.c_str())){ - BitmapTex *bmpTex = NewDefaultBitmapTex(); - bmpTex->SetName(texSrc->GetName().c_str()); - bmpTex->SetMapName(const_cast<TCHAR*>(FindImage(filename).c_str())); - bmpTex->SetAlphaAsMono(TRUE); - bmpTex->SetAlphaSource(ALPHA_NONE); - return bmpTex; - } - } - return NULL; -} - -bool NifImporter::ImportMaterialAndTextures(ImpNode *node, NiAVObjectRef avObject) -{ - // Texture - NiTexturingPropertyRef texRef = avObject->GetPropertyByType( NiTexturingProperty::TypeConst() ); - NiMaterialPropertyRef matRef = avObject->GetPropertyByType( NiMaterialProperty::TypeConst() ); - - bool hasTexture = (texRef && matRef); - if (hasTexture){ - StdMat2 *m = NewDefaultStdMat(); - m->SetName(matRef->GetName().c_str()); - if (showTextures) - m->SetMtlFlag(MTL_DISPLAY_ENABLE_FLAGS, TRUE); - m->SetAmbient(TOCOLOR(matRef->GetAmbientColor()),0); - m->SetDiffuse(TOCOLOR(matRef->GetDiffuseColor()),0); - m->SetSpecular(TOCOLOR(matRef->GetSpecularColor()),0); - Color c = TOCOLOR(matRef->GetEmissiveColor()); - if (c.r != 0 || c.b != 0 || c.g != 0) { - m->SetSelfIllumColorOn(TRUE); - m->SetSelfIllumColor(c,0); - } - m->SetShinStr(0.0,0); - m->SetShininess(matRef->GetGlossiness()/100.0,0); - m->SetOpacity(matRef->GetTransparency(),0); - - // Handle Base/Detail ??? - if (texRef->HasTexture(DECAL_0_MAP)){ - if (Texmap* tex = CreateTexture(texRef->GetTexture(DECAL_0_MAP))) - m->SetSubTexmap(ID_DI, tex); - if (texRef->HasTexture(BASE_MAP)){ - m->LockAmbDiffTex(FALSE); - if (Texmap* tex = CreateTexture(texRef->GetTexture(BASE_MAP))) - m->SetSubTexmap(ID_AM, tex); - } - } else if (texRef->HasTexture(BASE_MAP)) { - if (Texmap* tex = CreateTexture(texRef->GetTexture(BASE_MAP))) - m->SetSubTexmap(ID_DI, tex); - } - // Handle Bump map - if (texRef->HasTexture(BUMP_MAP)) { - if (Texmap* tex = CreateTexture(texRef->GetTexture(BUMP_MAP))) - m->SetSubTexmap(ID_BU, tex); - } - // Shiny map - if (texRef->HasTexture(GLOSS_MAP)) { - if (Texmap* tex = CreateTexture(texRef->GetTexture(GLOSS_MAP))) - m->SetSubTexmap(ID_SS, tex); - } - - // Self illumination - if (texRef->HasTexture(GLOW_MAP)) { - if (Texmap* tex = CreateTexture(texRef->GetTexture(GLOW_MAP))) - m->SetSubTexmap(ID_SI, tex); - } - - gi->GetMaterialLibrary().Add(m); - node->GetINode()->SetMtl(m); - } - return hasTexture; -} - -bool NifImporter::ImportTransform(ImpNode *node, NiAVObjectRef avObject) -{ - node->SetTransform(0,TOMATRIX3(avObject->GetWorldTransform())); - return true; -} - -bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, bool hasTexture) -{ - Mesh& mesh = o->GetMesh(); - - // Vertex info - { - int nVertices = triGeomData->GetVertexCount(); - vector<Vector3> vertices = triGeomData->GetVertices(); - mesh.setNumVerts(nVertices); - for (int i=0; i < nVertices; ++i){ - Vector3 &v = vertices[i]; - mesh.verts[i].Set(v.x, v.y, v.z); - } - } - if (hasTexture) // uv texture info - { - int nUVSet = triGeomData->GetUVSetCount(); - int n = 0; - for (int j=0; j<nUVSet; j++){ - vector<TexCoord> texCoords = triGeomData->GetUVSet(j); - n = texCoords.size(); - mesh.setNumTVerts(n, FALSE); - for (int i=0; i<n; ++i) { - TexCoord& texCoord = texCoords[i]; - mesh.tVerts[i].Set(texCoord.u, (flipUVTextures) ? -texCoord.v : texCoord.v, 0); - } - } - } - if (removeDegenerateFaces) - mesh.RemoveDegenerateFaces(); - if (removeIllegalFaces) - mesh.RemoveIllegalFaces(); - if (enableAutoSmooth) - mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); - - // Normals - { - mesh.checkNormals(TRUE); - vector<Vector3> n = triGeomData->GetNormals(); - for (int i=0; i<n.size(); i++){ - Vector3 v = n[i]; - mesh.setNormal(i, Point3(v.x, v.y, v.z)); - } - } - // vertex coloring - { - bool hasAlpha = false; - vector<Color4> cv = triGeomData->GetColors(); - mesh.setNumVertCol(cv.size()); - for (int i=0; i<cv.size(); i++){ - Color4& c = cv[i]; - mesh.vertCol[i].Set(c.r, c.g, c.b); - hasAlpha |= (c.a != 0.0); - } - } - return true; -} - -void NifImporter::SetTrangles(Mesh& mesh, vector<Triangle>& v, bool hasTexture) -{ - int n = v.size(); - mesh.setNumFaces(n); - if (hasTexture) - mesh.setNumTVFaces(n); - for (int i=0; i<n; ++i) { - Triangle& t = v[i]; - Face& f = mesh.faces[i]; - f.setVerts(t.v1, t.v2, t.v3); - f.Show(); - f.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS); - if (hasTexture) { - TVFace& tf = mesh.tvFace[i]; - tf.setTVerts(t.v1, t.v2, t.v3); - } - } -} - -bool NifImporter::ImportMesh(NiTriShapeRef triShape) -{ - bool ok = true; - - ImpNode *node = i->CreateNode(); - if(!node) return false; - TriObject *triObject = CreateNewTriObject(); - node->Reference(triObject); - string name = triShape->GetName(); - node->SetName(name.c_str()); - - // Texture - bool hasTexture = ImportMaterialAndTextures(node, triShape); - ImportTransform(node, triShape); - - Mesh& mesh = triObject->GetMesh(); - NiTriShapeDataRef triShapeData = DynamicCast<NiTriShapeData>(triShape->GetData()); - if (triShapeData == NULL) - return false; - - ok |= ImportMesh(node, triObject, triShape, triShapeData, hasTexture); - - // Triangles and texture vertices - if (ok) - { - vector<Triangle> v = triShapeData->GetTriangles(); - SetTrangles(mesh, v, hasTexture); - } - if (enableSkinSupport) - ImportSkin(node, triShape); - - i->AddNodeToScene(node); - - if (enableAutoSmooth){ - if (TriObject *tri = GetTriObject(node->GetINode()->GetObjectRef())){ - tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); - } - } - - return ok; -} - -bool NifImporter::ImportMesh(NiTriStripsRef triStrips) -{ - bool ok = true; - - ImpNode *node = i->CreateNode(); - if(!node) return false; - TriObject *triObject = CreateNewTriObject(); - node->Reference(triObject); - string name = triStrips->GetName(); - node->SetName(name.c_str()); - - // Texture - bool hasTexture = ImportMaterialAndTextures(node, triStrips); - ImportTransform(node, triStrips); - - Mesh& mesh = triObject->GetMesh(); - NiTriStripsDataRef triStripsData = DynamicCast<NiTriStripsData>(triStrips->GetData()); - if (triStripsData == NULL) - return false; - - ok |= ImportMesh(node, triObject, triStrips, triStripsData, hasTexture); - - // Triangles and texture vertices - if (ok) - { - vector<Triangle> v = triStripsData->GetTriangles(); - SetTrangles(mesh, v, hasTexture); - } - if (enableSkinSupport) - ImportSkin(node, triStrips); - - i->AddNodeToScene(node); - - // apply autosmooth after object creation for it to take hold - if (enableAutoSmooth){ - if (TriObject *tri = GetTriObject(node->GetINode()->GetObjectRef())){ - tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); - } - } - - return ok; -} - -struct VertexHolder -{ - VertexHolder() : vertIndex(0), count(0) {} - - int vertIndex; - int count; - Tab<INode*> boneNodeList; - Tab<float> weights; -}; - -bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom) -{ - bool ok = true; - NiSkinInstanceRef nifSkin = triGeom->GetSkinInstance(); - if (!nifSkin) - return false; - - INode *tnode = node->GetINode(); - - NiSkinDataRef data = nifSkin->GetSkinData(); - NiSkinPartitionRef part = nifSkin->GetSkinPartition(); - - vector<NiNodeRef> nifBones = nifSkin->GetBones(); - - //create a skin modifier and add it - Modifier *skinMod = GetSkin(tnode); - TriObject *triObject = GetTriObject(tnode->GetObjectRef()); - Mesh& m = triObject->GetMesh(); - - //get the skin interface - if (ISkin *skin = (ISkin *) skinMod->GetInterface(I_SKIN)){ - ISkinImportData* iskinImport = (ISkinImportData*) skinMod->GetInterface(I_SKINIMPORTDATA); - - // Create Bone List - Tab<INode*> bones; - int i=0; - for (vector<NiNodeRef>::iterator itr = nifBones.begin(), end = nifBones.end(); itr != end; ++itr, ++i){ - string name = (*itr)->GetName(); - 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); - } - ObjectState os = tnode->EvalWorldState(0); - - // Need to trigger ModifyObject in BonesDefMod prior to adding vertices or nothing is added - GetCOREInterface()->ForceCompleteRedraw(); - - // Need to get a list of bones and weights for each vertex. - vector<VertexHolder> vertexHolders; - vertexHolders.resize(m.numVerts); - for (int i=0, n=data->GetBoneCount();i<n; ++i){ - if (INode *boneRef = bones[i]){ - vector<SkinWeight> weights = data->GetBoneWeights(i); - for (vector<SkinWeight>::iterator itr=weights.begin(), end=weights.end(); itr != end; ++itr){ - VertexHolder& h = vertexHolders[itr->index]; - h.vertIndex = itr->index; - ++h.count; - h.weights.Append(1, &itr->weight); - h.boneNodeList.Append(1, &boneRef); - } - } - } - - // Assign the weights - for (vector<VertexHolder>::iterator itr=vertexHolders.begin(), end=vertexHolders.end(); itr != end; ++itr){ - VertexHolder& h = (*itr); - if (h.count){ - float sum = 0.0f; - for (int i=0; i<h.count; ++i) - sum += h.weights[i]; - ASSERT(fabs(sum-1.0) < 0.001); - BOOL add = iskinImport->AddWeights(tnode, h.vertIndex, h.boneNodeList, h.weights); - add = add; - } - } - } - return ok; -} - -string NifImporter::FindImage(const string& name) -{ - TCHAR buffer[MAX_PATH]; - - // Simply check for fully qualified path - if (PathIsRoot(name.c_str())) { - if (-1 != _taccess(name.c_str(), 0)) - return string(buffer); - } - if (!path.empty()) { - PathCombine(buffer, path.c_str(), name.c_str()); // try as-is - if (-1 != _taccess(buffer, 0)) - return string(buffer); - - // try only filename in nif directory - PathCombine(buffer, path.c_str(), PathFindFileName(name.c_str())); - if (-1 != _taccess(buffer, 0)) - return string(buffer); - } - if (appSettings != NULL) { - return appSettings->FindImage(name); - } - return name; -} \ No newline at end of file diff --git a/NifImport/MaxNifImport.h b/NifImport/MaxNifImport.h index 574cd65752e302dd91004b91711d20d18bdd4ffa..2106fa8139ef87b7c1192d05a4f72b84755cf89f 100644 --- a/NifImport/MaxNifImport.h +++ b/NifImport/MaxNifImport.h @@ -14,19 +14,64 @@ #ifndef __MaxNifImport__H #define __MaxNifImport__H -#include "Max.h" +#include <math.h> +#include <io.h> +#include <sstream> + +#include <Max.h> #include "resource.h" -#include "istdplug.h" -#include "iparamb2.h" -#include "iparamm2.h" -//SIMPLE TYPE +#include <istdplug.h> +#include <iparamb2.h> +#include <iparamm2.h> +#include <cs/Biped8Api.h> +#include <scenetraversal.h> +#include <plugapi.h> +#include <triobj.h> +#include <bitmap.h> +#include <modstack.h> +#include <iskin.h> +#include <strclass.h> +#include "objectParams.h" +#undef ALPHA_NONE + +#include <math.h> +#include <string.h> + +#include "niflib.h" +#include "obj\NiObject.h" +#include "obj\NiNode.h" +#include "obj\NiTriShape.h" +#include "obj\NiTriShapeData.h" +#include "obj\NiTriStrips.h" +#include "obj\NiTriStripsData.h" +#include "obj\NiMaterialProperty.h" +#include "obj\NiTexturingProperty.h" +#include "obj\NiSourceTexture.h" +#include "obj\NiExtraData.h" +#include "obj\BSBound.h" +#include "obj\NiSkinData.h" +#include "obj\NiSkinInstance.h" +#include "obj\NiSkinPartition.h" + +#include "niutils.h" +#include "AppSettings.h" + +#ifndef ASSERT +#ifdef _DEBUG +#include <crtdbg.h> +#define ASSERT _ASSERTE +#else +#define ASSERT(exp) +#endif +#endif #include <direct.h> #include <commdlg.h> +#include "NifImporter.h" extern TCHAR *GetString(int id); - +extern string shortDescription; extern HINSTANCE hInstance; #endif diff --git a/NifImport/MaxNifImport.rc b/NifImport/MaxNifImport.rc index 1161745f77540028eaf0692ad78e3ba706cb2624..d181dc0d091f4acf5a990cc2245387806be43da5 100644 --- a/NifImport/MaxNifImport.rc +++ b/NifImport/MaxNifImport.rc @@ -88,7 +88,7 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,3,0 + FILEVERSION 0,1,3,2 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, 0" + VALUE "FileVersion", "0, 1, 3, 2" 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 28757c46f1b069443ae2635566e4e622eace420b..8ef3a13b730a8f6c03aaa51146fe854744036a26 100644 --- a/NifImport/MaxNifImport.vcproj +++ b/NifImport/MaxNifImport.vcproj @@ -52,6 +52,7 @@ AdditionalIncludeDirectories="C:\3dsmax8\maxsdk\include" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;USE_NIFLIB_TEMPLATE_HELPERS;_USE_MATH_DEFINES" StringPooling="true" + ExceptionHandling="2" RuntimeLibrary="0" EnableFunctionLevelLinking="true" ForceConformanceInForLoopScope="false" @@ -62,6 +63,7 @@ ProgramDataBaseFileName=".\Release\" WarningLevel="3" SuppressStartupBanner="true" + DebugInformationFormat="3" CompileAs="0" /> <Tool @@ -78,7 +80,7 @@ <Tool Name="VCLinkerTool" AdditionalOptions="/MACHINE:I386" - AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib biped.lib bmm.lib niflib.lib" + AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib bmm.lib niflib.lib" OutputFile="C:\3dsmax8\plugins\MaxNifImport.dli" LinkIncremental="1" SuppressStartupBanner="true" @@ -115,7 +117,7 @@ /> <Tool Name="VCPostBuildEventTool" - CommandLine="if exist "C:\3dsmax8\plugcfg" (
if not exist "C:\3dsmax8\plugcfg\MaxNifTools.ini" (
copy "$(ProjectDir)MaxNifTools.ini" "C:\3dsmax8\plugcfg\MaxNifTools.ini"
)
)
if exist "C:\3dsmax8" (
xcopy /D /Y /I "$(ProjectDir)..\MaxNifPlugins_Readme.txt" "$(ProjectDir)Staging\"
)
xcopy /D /Y /I "$(TargetPath)" "$(ProjectDir)Staging\plugins\"
xcopy /D /Y /I "$(ProjectDir)MaxNifTools.ini" "$(ProjectDir)Staging\plugcfg"" + CommandLine="if exist "C:\3dsmax8\plugcfg" (
if not exist "C:\3dsmax8\plugcfg\MaxNifTools.ini" (
copy "$(ProjectDir)MaxNifTools.ini" "C:\3dsmax8\plugcfg\MaxNifTools.ini"
)
)
if exist "C:\3dsmax8" (
xcopy /D /Y /I "$(ProjectDir)..\MaxNifPlugins_Readme.txt" "$(ProjectDir)Staging\"
)
xcopy /D /Y /I "$(TargetPath)" "$(ProjectDir)Staging\plugins\"
xcopy /D /Y /I "$(ProjectDir)MaxNifTools.ini" "$(ProjectDir)Staging\plugcfg"
" /> </Configuration> <Configuration @@ -164,7 +166,7 @@ ProgramDataBaseFileName=".\Debug\" WarningLevel="3" SuppressStartupBanner="true" - DebugInformationFormat="4" + DebugInformationFormat="3" CompileAs="0" /> <Tool @@ -181,7 +183,7 @@ <Tool Name="VCLinkerTool" AdditionalOptions="/MACHINE:I386" - AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib biped.lib bmm.lib niflibd.lib" + AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib bmm.lib niflibd.lib" OutputFile="C:\3dsmax8\plugins\MaxNifImport.dli" LinkIncremental="2" SuppressStartupBanner="true" @@ -298,6 +300,34 @@ RelativePath=".\AppSettings.h" > </File> + <File + RelativePath=".\BaseImporter.h" + > + </File> + <File + RelativePath=".\ImportAnimation.cpp" + > + </File> + <File + RelativePath=".\ImportMeshAndSkin.cpp" + > + </File> + <File + RelativePath=".\ImportMtlAndTex.cpp" + > + </File> + <File + RelativePath=".\ImportSkeleton.cpp" + > + </File> + <File + RelativePath=".\KFMImporter.cpp" + > + </File> + <File + RelativePath=".\KFMImporter.h" + > + </File> <File RelativePath=".\MaxNifImport.cpp" > @@ -314,11 +344,23 @@ RelativePath=".\MaxNifImport.h" > </File> + <File + RelativePath=".\NIFImport.cpp" + > + </File> + <File + RelativePath=".\NIFImporter.h" + > + </File> <File RelativePath=".\niutils.cpp" > </File> </Filter> + <File + RelativePath="..\MaxNifPlugins_Readme.txt" + > + </File> <File RelativePath=".\MaxNifTools.ini" > diff --git a/NifImport/MaxNifTools.ini b/NifImport/MaxNifTools.ini index 2667f20bc5223edd7afd3412a3394f6dd839fc1a..c2b63fb5e163e571b90ac012d8e510c45ad2f504 100644 --- a/NifImport/MaxNifTools.ini +++ b/NifImport/MaxNifTools.ini @@ -44,15 +44,23 @@ BipedTrianglePelvis=0 ; Remove unused bones from the biped on import of a mesh. Default: 1 RemoveUnusedImportedBones=1 ; Minimum Bone Width / Maximum Bone Width / Ratio of Width to Length -MinBoneWidth=0.5 -MaxBoneWidth=3 -BoneWidthToLengthRatio=0.25 +MinBoneWidth=0.001 +MaxBoneWidth=0.1 +BoneWidthToLengthRatio=0.01 ; Force nub to point back to parent at expense of loss of rotation data. Default: 1 -ForceRotation=1 +ForceRotation=0 ; Browse for skeleton on import. Default: 1 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 + +[AnimationImport] +; Enable Animation Import. Default: 1 +EnableAnimations=1 +; Replace TCB Rotation with Bezier (workaround for unexpected behavior with TCB rotations) +ReplaceTCBRotationWithBezier=1 ; [Applications] ; RootPaths - Semicolon separated list of base directories to use when determining which app the imported file is from diff --git a/NifImport/NIFImport.cpp b/NifImport/NIFImport.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ee9a21b3b8db278cf9e5a0f8f332a38c308da44a --- /dev/null +++ b/NifImport/NIFImport.cpp @@ -0,0 +1,256 @@ +/********************************************************************** +*< +FILE: ImporterCore.cpp + +DESCRIPTION: Core Import helper routines + +CREATED BY: tazpn (Theo) + +HISTORY: + +*> Copyright (c) 2006, All Rights Reserved. +**********************************************************************/ +#include "stdafx.h" +#include "MaxNifImport.h" + +using namespace Niflib; + +// Define the standard section names used in the ini file +LPCTSTR NifImportSection = TEXT("MaxNifImport"); +LPCTSTR SystemSection = TEXT("System"); +LPCTSTR BipedImportSection = TEXT("BipedImport"); +LPCTSTR AnimImportSection = TEXT("AnimationImport"); + +class IBipMaster; +IBipMaster * (_cdecl * Max8CreateNewBiped)(float,float,class Point3 const &,int,int,int,int,int,int,int,int,int,int,int,int,float,int,int,int,int,int,int,int,int) = 0; + +NifImporter::NifImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts) + : BaseImporter() +{ + BaseInit(Name,I,GI,SuppressPrompts); +} + +NifImporter::NifImporter() +{ +} + +void NifImporter::ReadBlocks() +{ + //blocks = ReadNifList( name ); + root = ReadNifTree(name); + BuildNodes(); +} + + +static void BuildNodes(NiNodeRef object, vector<NiNodeRef>& nodes) +{ + if (!object) + return; + 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); +} + +void NifImporter::BuildNodes() +{ + ::BuildNodes(root, nodes); + std::sort(nodes.begin(), nodes.end(), NodeEquivalence()); +} + +void NifImporter::Initialize() +{ + // Apply post processing checks after reading blocks + if (isValid()){ + if (goToSkeletonBindPosition && !nodes.empty()) + GoToSkeletonBindPosition(nodes); + + // Only support biped if CreateNewBiped can be found. + useBiped &= (Max8CreateNewBiped != NULL); + + hasSkeleton = HasSkeleton(); + isBiped = IsBiped(); + skeleton = (appSettings != NULL) ? appSettings->Skeleton : ""; + importSkeleton = (appSettings != NULL) ? appSettings->useSkeleton : false; + importSkeleton &= hasSkeleton; + + // Guess that the skeleton is the same one in the current directory + if (importSkeleton && !defaultSkeletonName.empty()) { + TCHAR buffer[MAX_PATH]; + GetFullPathName(name.c_str(), _countof(buffer), buffer, NULL); + PathRemoveFileSpec(buffer); + PathAddBackslash(buffer); + PathAppend(buffer, defaultSkeletonName.c_str()); + if (-1 != _taccess(buffer, 0)) + skeleton = buffer; + } + } +} + +void NifImporter::LoadIniSettings() +{ + TCHAR iniName[MAX_PATH]; + LPCTSTR pluginDir = gi->GetDir(APP_PLUGCFG_DIR); + PathCombine(iniName, pluginDir, "MaxNifTools.ini"); + this->iniFileName = iniName; + iniFileValid = (-1 != _access(iniName, 0)); + + // Locate which application to use. If Auto, find first app where this file appears in the root path list + appSettings = NULL; + string curapp = GetIniValue<string>(NifImportSection, "CurrentApp", "AUTO"); + if (0 == _tcsicmp(curapp.c_str(), "AUTO")) { + // Scan Root paths + for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){ + if ((*itr).IsFileInRootPaths(this->name)) { + appSettings = &(*itr); + break; + } + } + } else { + appSettings = FindAppSetting(curapp); + } + if (appSettings == NULL && !TheAppSettings.empty()){ + appSettings = &TheAppSettings.front(); + } + + useBiped = GetIniValue<bool>(NifImportSection, "UseBiped", false); + skeletonCheck = GetIniValue<string>(NifImportSection, "SkeletonCheck", "Bip*"); + showTextures = GetIniValue<bool>(NifImportSection, "ShowTextures", true); + + removeIllegalFaces = GetIniValue<bool>(NifImportSection, "RemoveIllegalFaces", true); + removeDegenerateFaces = GetIniValue<bool>(NifImportSection, "RemoveDegenerateFaces", true); + enableAutoSmooth = GetIniValue<bool>(NifImportSection, "EnableAutoSmooth", true); + autoSmoothAngle = GetIniValue<float>(NifImportSection, "AutoSmoothAngle", 30.0f); + flipUVTextures = GetIniValue<bool>(NifImportSection, "FlipUVTextures", true); + enableSkinSupport = GetIniValue<bool>(NifImportSection, "EnableSkinSupport", true); + + bipedHeight = GetIniValue<float>(BipedImportSection, "BipedHeight", 131.90f); + bipedAngle = GetIniValue<float>(BipedImportSection, "BipedAngle", 90.0f); + bipedAnkleAttach = GetIniValue<float>(BipedImportSection, "BipedAnkleAttach", 0.2f); + bipedTrianglePelvis = GetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", false); + removeUnusedImportedBones = GetIniValue<bool>(BipedImportSection, "RemoveUnusedImportedBones", false); + forceRotation = GetIniValue<bool>(BipedImportSection, "ForceRotation", true); + browseForSkeleton = GetIniValue<bool>(BipedImportSection, "BrowseForSkeleton", true); + defaultSkeletonName = GetIniValue<string>(BipedImportSection, "DefaultSkeletonName", "Skeleton.Nif"); + minBoneWidth = GetIniValue<float>(BipedImportSection, "MinBoneWidth", 0.5f); + maxBoneWidth = GetIniValue<float>(BipedImportSection, "MaxBoneWidth", 3.0f); + boneWidthToLengthRatio = GetIniValue<float>(BipedImportSection, "BoneWidthToLengthRatio", 0.25f); + createNubsForBones = GetIniValue<bool>(BipedImportSection, "CreateNubsForBones", true); + + replaceTCBRotationWithBezier = GetIniValue<bool>(AnimImportSection, "ReplaceTCBRotationWithBezier", true); + enableAnimations = GetIniValue<bool>(AnimImportSection, "EnableAnimations", true); + + goToSkeletonBindPosition = (appSettings ? appSettings->goToSkeletonBindPosition : false); +} + +void NifImporter::SaveIniSettings() +{ + SetIniValue<bool>(NifImportSection, "UseBiped", useBiped); + SetIniValue<string>(NifImportSection, "Skeleton", skeleton); + SetIniValue<string>(NifImportSection, "SkeletonCheck", skeletonCheck); + + SetIniValue<float>(BipedImportSection, "BipedHeight", bipedHeight); + SetIniValue<float>(BipedImportSection, "BipedAngle", bipedAngle); + SetIniValue<float>(BipedImportSection, "BipedAnkleAttach", bipedAnkleAttach); + SetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", bipedTrianglePelvis); +} + +INode *NifImporter::GetNode(Niflib::NiNodeRef node) +{ + // may want to make this a map if its hit a lot + if (NULL == node) return NULL; + return gi->GetINodeByName(node->GetName().c_str()); +} + +bool NifImporter::DoImport() +{ + bool ok = true; + NiNodeRef rootNode = root; + if (isBiped) + { + if (useBiped){ + ImportBipeds(nodes); + } else { + ImportBones(DynamicCast<NiNode>(rootNode->GetChildren())); + } + } + else + { + vector<string> importedBones; + if (importSkeleton) + { + if (browseForSkeleton) + { + TCHAR filter[64], *pfilter=filter; + pfilter = _tcscpy(filter, shortDescription.c_str()); + pfilter = _tcscat(pfilter, " (*.NIF)"); + pfilter += strlen(pfilter); + *pfilter++ = '\0'; + _tcscpy(pfilter, "*.NIF"); + pfilter += strlen(pfilter); + *pfilter++ = '\0'; + *pfilter++ = '\0'; + + TCHAR filename[MAX_PATH]; + GetFullPathName(skeleton.c_str(), _countof(filename), filename, NULL); + + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = gi->GetMAXHWnd(); + ofn.lpstrFilter = filter; + ofn.lpstrFile = filename; + ofn.nMaxFile = _countof(filename); + ofn.lpstrTitle = TEXT("Browse for Skeleton NIF..."); + ofn.lpstrDefExt = TEXT("NIF"); + ofn.Flags = OFN_HIDEREADONLY|OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST; + importSkeleton = GetOpenFileName(&ofn) ? true : false; + if (importSkeleton) { + skeleton = filename; + } + } + if (importSkeleton && !skeleton.empty()) { + NifImporter skelImport(skeleton.c_str(), i, gi, suppressPrompts); + if (skelImport.isValid()) + { + if (skelImport.useBiped){ + skelImport.ImportBipeds(skelImport.nodes); + } else { + NiNodeRef skelRoot = skelImport.root; + skelImport.ImportBones(DynamicCast<NiNode>(skelRoot->GetChildren())); + if (removeUnusedImportedBones){ + importedBones = GetNamesOfNodes(skelImport.nodes); + } + } + } + } + } else if (hasSkeleton && useBiped) { + ImportBipeds(nodes); + } + + if (isValid()) { + ImportBones(DynamicCast<NiNode>(rootNode->GetChildren())); + ok = ImportMeshes(rootNode); + + if (importSkeleton && removeUnusedImportedBones){ + vector<string> importedNodes = GetNamesOfNodes(nodes); + sort(importedBones.begin(), importedBones.end()); + sort(importedNodes.begin(), importedNodes.end()); + vector<string> results; + results.resize(importedBones.size()); + vector<string>::iterator end = set_difference ( + importedBones.begin(), importedBones.end(), + 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); + } + } + } + } + } + + // Kick of animation import + ImportAnimation(); + return true; +} \ No newline at end of file diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h new file mode 100644 index 0000000000000000000000000000000000000000..2bec037bc4c09282aaf542c87c0379585724519d --- /dev/null +++ b/NifImport/NIFImporter.h @@ -0,0 +1,103 @@ +/********************************************************************** +*< +FILE: NifImporter.h + +DESCRIPTION: NIF Importer + +CREATED BY: tazpn (Theo) + +HISTORY: + +*> Copyright (c) 2006, All Rights Reserved. +**********************************************************************/ + +#ifndef __NIFIMPORTER_H__ +#define __NIFIMPORTER_H__ + +#include "BaseImporter.h" +// NIF Importer +class NifImporter : public BaseImporter +{ +public: + // Ini settings + bool showTextures; // show textures in viewport + bool removeIllegalFaces; + bool removeDegenerateFaces; + bool enableAutoSmooth; + float autoSmoothAngle; + bool flipUVTextures; + bool enableSkinSupport; + bool goToSkeletonBindPosition; + + // Biped/Bones related settings + string skeleton; + float bipedHeight; + string skeletonCheck; + float bipedAngle; + float bipedAnkleAttach; + bool bipedTrianglePelvis; + bool importSkeleton; + bool useBiped; + bool hasSkeleton; + bool isBiped; + bool removeUnusedImportedBones; + bool forceRotation; + bool browseForSkeleton; + string defaultSkeletonName; + float minBoneWidth; + float maxBoneWidth; + float boneWidthToLengthRatio; + bool createNubsForBones; + + // Animation related Settings + bool replaceTCBRotationWithBezier; + bool enableAnimations; + + vector<Niflib::NiObjectRef> blocks; + vector<Niflib::NiNodeRef> nodes; + + NifImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts); + virtual void Initialize(); + virtual void ReadBlocks(); + void BuildNodes(); + + // Ini File related routines + void LoadIniSettings(); + void SaveIniSettings(); + + bool HasSkeleton(); + bool IsBiped(); + void ImportBones(vector<Niflib::NiNodeRef>& bones); + void ImportBones(Niflib::NiNodeRef blocks); + 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); + + void SetTrangles(Mesh& mesh, vector<Niflib::Triangle>& v, bool hasTexture); + bool ImportMesh(Niflib::NiTriShapeRef triShape); + bool ImportMesh(Niflib::NiTriStripsRef triStrips); + bool ImportMaterialAndTextures(ImpNode *node, Niflib::NiAVObjectRef avObject); + bool ImportTransform(ImpNode *node, Niflib::NiAVObjectRef avObject); + bool ImportMesh(ImpNode *node, TriObject *o, Niflib::NiTriBasedGeomRef triGeom, Niflib::NiTriBasedGeomDataRef triGeomData, bool hasTexture); + + bool ImportSkin(ImpNode *node, Niflib::NiTriBasedGeomRef triGeom); + Texmap* CreateTexture(Niflib::TexDesc& desc); + INode *CreateBone(const string& name, Point3 startPos, Point3 endPos, Point3 zAxis); + INode *CreateHelper(const string& name, Point3 startPos); + + INode *GetNode(Niflib::NiNodeRef node); + + virtual bool DoImport(); + + // Animation Helpers + bool ImportAnimation(); + + protected: + NifImporter(); +}; + +#endif diff --git a/NifImport/Staging/plugcfg/MaxNifTools.ini b/NifImport/Staging/plugcfg/MaxNifTools.ini index 2667f20bc5223edd7afd3412a3394f6dd839fc1a..c2b63fb5e163e571b90ac012d8e510c45ad2f504 100644 --- a/NifImport/Staging/plugcfg/MaxNifTools.ini +++ b/NifImport/Staging/plugcfg/MaxNifTools.ini @@ -44,15 +44,23 @@ BipedTrianglePelvis=0 ; Remove unused bones from the biped on import of a mesh. Default: 1 RemoveUnusedImportedBones=1 ; Minimum Bone Width / Maximum Bone Width / Ratio of Width to Length -MinBoneWidth=0.5 -MaxBoneWidth=3 -BoneWidthToLengthRatio=0.25 +MinBoneWidth=0.001 +MaxBoneWidth=0.1 +BoneWidthToLengthRatio=0.01 ; Force nub to point back to parent at expense of loss of rotation data. Default: 1 -ForceRotation=1 +ForceRotation=0 ; Browse for skeleton on import. Default: 1 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 + +[AnimationImport] +; Enable Animation Import. Default: 1 +EnableAnimations=1 +; Replace TCB Rotation with Bezier (workaround for unexpected behavior with TCB rotations) +ReplaceTCBRotationWithBezier=1 ; [Applications] ; RootPaths - Semicolon separated list of base directories to use when determining which app the imported file is from diff --git a/NifImport/Staging/plugins/MaxNifImport.dli b/NifImport/Staging/plugins/MaxNifImport.dli index 443d6d01b59fb740a6b2eabaed571a697786bc1a..b9316bfd64e32b4dadd5e7285510553699794aa2 100644 Binary files a/NifImport/Staging/plugins/MaxNifImport.dli and b/NifImport/Staging/plugins/MaxNifImport.dli differ diff --git a/NifImport/niutils.cpp b/NifImport/niutils.cpp index 52353b86788966fb6ce912f9235861623cbce087..8b94298dbb80cb64c6874a6418e32ac2708e3fbb 100644 --- a/NifImport/niutils.cpp +++ b/NifImport/niutils.cpp @@ -306,61 +306,20 @@ void RenameNode(Interface *gi, LPCTSTR SrcName, LPCTSTR DstName) if (node != NULL) node->SetName(const_cast<LPTSTR>(DstName)); } -// Locate a TriObject in an Object if it exists -TriObject* GetTriObject(Object *o) +void PosRotScaleNode(INode *n, Matrix3& m3, PosRotScale prs, TimeValue t) { - if (o && o->CanConvertToType(triObjectClassID)) - return (TriObject *)o->ConvertToType(0, triObjectClassID); - while (o->SuperClassID() == GEN_DERIVOB_CLASS_ID && o) - { - IDerivedObject* dobj = (IDerivedObject *)(o); - o = dobj->GetObjRef(); - if (o && o->CanConvertToType(triObjectClassID)) - return (TriObject *)o->ConvertToType(0, triObjectClassID); - } - return NULL; -} - -// Get or Create the Skin Modifier -Modifier *GetSkin(INode *node) -{ - 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() == SKIN_CLASSID) - { - // is this the correct Physique Modifier based on index? - return mod; - } - Idx++; - } - pObj = pDerObj->GetObjRef(); - } - - IDerivedObject *dobj = CreateDerivedObject(node->GetObjectRef()); - - //create a skin modifier and add it - Modifier *skinMod = (Modifier*) CreateInstance(OSM_CLASS_ID, SKIN_CLASSID); - dobj->SetAFlag(A_LOCK_TARGET); - dobj->AddModifier(skinMod); - dobj->ClearAFlag(A_LOCK_TARGET); - node->SetObjectRef(dobj); - return skinMod; + Point3 p = m3.GetTrans(); + Quat q = m3; + PosRotScaleNode(n, p, q, 1.0f, prs, t); } // Set Position and Rotation on a standard controller will need to handle bipeds // Always in World Transform coordinates -void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs, TimeValue t) +void PosRotScaleNode(INode *n, Point3 p, Quat& q, float s, PosRotScale prs, TimeValue t) { if (Control *c = n->GetTMController()) { + ScaleValue sv(Point3(s,s,s)); // Bipeds are special. And will crash if you dont treat them with care if ( (c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) ||(c->ClassID() == BIPBODY_CONTROL_CLASS_ID) @@ -369,6 +328,8 @@ void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs, TimeVal // Get the Biped Export Interface from the controller //IBipedExport *BipIface = (IBipedExport *) c->GetInterface(I_BIPINTERFACE); IOurBipExport *BipIface = (IOurBipExport *) c->GetInterface(I_OURINTERFACE); + if (prs & prsScale) + BipIface->SetBipedScale(sv, t, n); if (prs & prsRot) BipIface->SetBipedRotation(q, t, n, 0/*???*/); if (prs & prsPos) @@ -376,6 +337,9 @@ void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs, TimeVal } else { + if (prs & prsScale) + if (Control *sclCtrl = c->GetScaleController()) + sclCtrl->SetValue(t, &sv, 1, CTRL_ABSOLUTE); if (prs & prsRot) if (Control *rotCtrl = c->GetRotationController()) rotCtrl->SetValue(t, &q, 1, CTRL_ABSOLUTE); @@ -516,13 +480,3 @@ void FindImages(NameValueCollection& images, const string& rootPath, const strin } -void GoToSkeletonBindPosition(vector<NiNodeRef>& blocks) -{ - //Send all skeleton roots to bind position - for (uint i = 0; i < blocks.size(); ++i) { - NiNodeRef node = blocks[i]; - if ( node != NULL && node->IsSkeletonRoot() ) { - node->GoToSkeletonBindPosition(); - } - } -} \ No newline at end of file diff --git a/NifImport/niutils.h b/NifImport/niutils.h index 2458ee3c5f5d1ae3992d9a650b4c254d03f6c752..b52b713728411b4deb9cdccb06281cd6ca4d0beb 100644 --- a/NifImport/niutils.h +++ b/NifImport/niutils.h @@ -31,6 +31,9 @@ INFO: See Implementation for minimalist comments #include <color.h> // Niflib Headers +#include <obj\NiObject.h> +#include <obj\NiAVObject.h> +#include <obj\NiObjectNET.h> #include <obj\NiNode.h> #include <nif_math.h> @@ -192,8 +195,6 @@ extern string ExpandEnvironment(const string& src); extern void FindImages(NameValueCollection& images, const string& rootPath, const stringlist& searchpaths, const stringlist& extensions); extern void RenameNode(Interface *gi, LPCTSTR SrcName, LPCTSTR DstName); -extern TriObject* GetTriObject(Object *o); -extern Modifier *GetSkin(INode *node); enum PosRotScale { @@ -202,13 +203,37 @@ enum PosRotScale prsScale = 0x4, prsDefault = prsPos | prsRot | prsScale, }; -extern void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs = prsDefault, TimeValue t = 0); +extern void PosRotScaleNode(INode *n, Point3 p, Quat& q, float s, PosRotScale prs = prsDefault, TimeValue t = 0); +extern void PosRotScaleNode(INode *n, Matrix3& m3, PosRotScale prs = prsDefault, TimeValue t = 0); extern Niflib::NiNodeRef FindNodeByName( const vector<Niflib::NiNodeRef>& blocks, const string& name ); extern std::vector<Niflib::NiNodeRef> SelectNodesByName( const vector<Niflib::NiNodeRef>& blocks, LPCTSTR match); extern int CountNodesByName( const vector<Niflib::NiNodeRef>& blocks, LPCTSTR match ); extern std::vector<std::string> GetNamesOfNodes( const vector<Niflib::NiNodeRef>& blocks ); -extern void GoToSkeletonBindPosition(std::vector<Niflib::NiNodeRef>& blocks); +extern std::vector<Niflib::NiNodeRef> SelectNodesByName( const vector<Niflib::NiNodeRef>& blocks, LPCTSTR match); + +struct NodeEquivalence +{ + bool operator()(const Niflib::NiNodeRef& lhs, const Niflib::NiNodeRef& rhs) const{ + return (!lhs || !rhs) ? (lhs < rhs) : (lhs->GetName() < rhs->GetName()); + } + bool operator()(const Niflib::NiNodeRef& lhs, const std::string& rhs) const{ + return (lhs->GetName() < rhs); + } + bool operator()(const std::string& lhs, const Niflib::NiNodeRef& rhs) const{ + return (lhs < rhs->GetName()); + } +}; + +inline Niflib::NiNodeRef BinarySearch(vector<Niflib::NiNodeRef> &nodes, const string& name) +{ + typedef std::pair<vector<Niflib::NiNodeRef>::iterator, vector<Niflib::NiNodeRef>::iterator> NiNodePair; + NiNodePair pair = std::equal_range(nodes.begin(), nodes.end(), name, NodeEquivalence()); + if (pair.first != pair.second) { + return (*pair.first); + } + return Niflib::NiNodeRef(); +} // Simple conversion helpers static inline float TODEG(float x) { return x * 180.0f / PI; } @@ -218,7 +243,7 @@ static inline Color TOCOLOR(const Niflib::Color3& c3) { return Color(c3.r, c3.g, c3.b); } -static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = true){ +static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = false){ Niflib::Vector3 pos; Niflib::Matrix33 rot; float scale; tm.Decompose(pos, rot, scale); Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3()); @@ -227,8 +252,39 @@ static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = true){ return m; } -static inline Quat TOQUAT(const Niflib::Quaternion& q){ - return Quat(q.x, q.y, q.z, q.w); + +static inline Point3 TOPOINT3(const Niflib::Vector3& v){ + return Point3(v.x, v.y, v.z); +} + +static inline Quat TOQUAT(const Niflib::Quaternion& q, bool inverse = false){ + Quat qt(q.x, q.y, q.z, q.w); + return (inverse) ? qt.Inverse() : qt; +} + +static inline AngAxis TOANGAXIS(const Niflib::Quaternion& q, bool inverse = false){ + Quat qt(q.x, q.y, q.z, q.w); + if (inverse) qt.Invert(); + return AngAxis(q.x, q.y, q.z, q.w); +} + + +template <typename U, typename T> +inline Niflib::Ref<U> SelectFirstObjectOfType( vector<Niflib::Ref<T> > const & objs ) { + for (vector<Niflib::Ref<T> >::const_iterator itr = objs.begin(), end = objs.end(); itr != end; ++itr) { + Niflib::Ref<U> obj = DynamicCast<U>(*itr); + if (obj) return obj; + } + return Niflib::Ref<U>(); +} + +template <typename U, typename T> +inline Niflib::Ref<U> SelectFirstObjectOfType( list<Niflib::Ref<T> > const & objs ) { + for (list<Niflib::Ref<T> >::const_iterator itr = objs.begin(), end = objs.end(); itr != end; ++itr) { + Niflib::Ref<U> obj = DynamicCast<U>(*itr); + if (obj) return obj; + } + return Niflib::Ref<U>(); } #endif // _NIUTILS_H_ \ No newline at end of file diff --git a/NifImport/objectParams.h b/NifImport/objectParams.h index d0367e6f0e28ba5b42ba0fb9ba8db7220d3ef306..faa7c15cb4b77adac8948ae010edf6153e84f1e8 100644 --- a/NifImport/objectParams.h +++ b/NifImport/objectParams.h @@ -190,7 +190,7 @@ bool getMAXScriptValue(ReferenceTarget* obj, LPTSTR name, TimeValue t, T& value) } // Get the parameter controller -Control* getMAXScriptController(ReferenceTarget* obj, LPTSTR name, ParamDimension*& dim) +inline Control* getMAXScriptController(ReferenceTarget* obj, LPTSTR name, ParamDimension*& dim) { Control* rval = NULL; assert(obj != NULL); @@ -217,7 +217,7 @@ Control* getMAXScriptController(ReferenceTarget* obj, LPTSTR name, ParamDimensio } // Set the parameter controller -bool setMAXScriptController(ReferenceTarget* obj, LPTSTR name, Control* control, ParamDimension* dim) +inline bool setMAXScriptController(ReferenceTarget* obj, LPTSTR name, Control* control, ParamDimension* dim) { bool rval = false; assert(obj != NULL); diff --git a/NifImport/stdafx.h b/NifImport/stdafx.h index 9f724ac266426cd0f9fe74c0a6f68ca963e1b76e..5b53f29d025cbac02581dc56aa11e6a98e06cb93 100644 --- a/NifImport/stdafx.h +++ b/NifImport/stdafx.h @@ -21,8 +21,4 @@ #include <commctrl.h> #include <shlwapi.h> -#include "Max.h" -#include "resource.h" -#include "istdplug.h" -#include "iparamb2.h" -#include "iparamm2.h" +#include "MaxNifImport.h"