diff --git a/NifImport/AppSettings.cpp b/NifImport/AppSettings.cpp index e21bb7460befd237af6b5408c4a1012eb09c8a38..6beb806947c59460a0c04e8d3a5fef912c993744 100644 --- a/NifImport/AppSettings.cpp +++ b/NifImport/AppSettings.cpp @@ -20,10 +20,10 @@ void AppSettings::Initialize(Interface *gi) string Applications = GetIniValue<string>("System", "KnownApplications", "", iniName); stringlist apps = TokenizeString(Applications.c_str(), ";"); for (stringlist::iterator appstr=apps.begin(); appstr != apps.end(); ++appstr){ - AppSettingsMap::iterator itr = TheAppSettings.find(*appstr); - if (itr == TheAppSettings.end()){ - itr = TheAppSettings.insert(TheAppSettings.end(), AppSettingsMap::value_type(*appstr, AppSettings(*appstr)) ); - (*itr).second.ReadSettings(iniName); + AppSettings* setting = FindAppSetting(*appstr); + if (NULL == setting){ + AppSettingsMap::iterator itr = TheAppSettings.insert(TheAppSettings.end(), AppSettings(*appstr)); + (*itr).ReadSettings(iniName); } } } @@ -48,17 +48,14 @@ void AppSettings::ReadSettings(string iniFile) std::swap(Environment, settings); rootPath = GetSetting<string>("RootPath"); - - string searchPathString = GetSetting<string>("TextureSearchPaths"); - searchPaths = TokenizeString(searchPathString.c_str(), ";"); - - string extensionString = GetSetting<string>("TextureExtensions"); - extensions = TokenizeString(extensionString.c_str(), ";"); - - string texRootPathString = GetSetting<string>("TextureRootPaths"); - rootPaths = TokenizeString(texRootPathString.c_str(), ";"); + rootPaths = TokenizeString(GetSetting<string>("RootPaths").c_str(), ";"); + searchPaths = TokenizeString(GetSetting<string>("TextureSearchPaths").c_str(), ";"); + extensions = TokenizeString(GetSetting<string>("TextureExtensions").c_str(), ";"); + textureRootPaths = TokenizeString(GetSetting<string>("TextureRootPaths").c_str(), ";"); Skeleton = GetSetting<string>("Skeleton"); + useSkeleton = GetSetting<bool>("UseSkeleton", useSkeleton); + goToSkeletonBindPosition = GetSetting<bool>("GoToSkeletonBindPosition", goToSkeletonBindPosition); } string AppSettings::FindImage(const string& fname){ @@ -71,7 +68,7 @@ string AppSettings::FindImage(const string& fname){ } // Test if its relative and in one of the specified root paths - for (stringlist::iterator itr = rootPaths.begin(), end = rootPaths.end(); itr != end; ++itr ){ + for (stringlist::iterator itr = textureRootPaths.begin(), end = textureRootPaths.end(); itr != end; ++itr ){ PathCombine(buffer, itr->c_str(), fname.c_str()); if (-1 != _taccess(buffer, 0)){ return string(buffer); @@ -100,3 +97,24 @@ string AppSettings::FindImage(const string& fname){ return fname; } + +// Check whether the given file is a child of the root paths +bool AppSettings::IsFileInRootPaths(const std::string& fname) +{ + TCHAR root[MAX_PATH]; + TCHAR file[MAX_PATH]; + GetFullPathName(fname.c_str(), _countof(file), file, NULL); + PathMakePretty(file); + + for (stringlist::iterator itr = rootPaths.begin(), end = rootPaths.end(); itr != end; ++itr ){ + GetFullPathName((*itr).c_str(), _countof(root), root, NULL); + PathAddBackslash(root); + PathMakePretty(root); + if (-1 != _taccess(root,0)) { + int len = _tcslen(root); + if (0 == _tcsncmp(root, file, len)) + return true; + } + } + return false; +} diff --git a/NifImport/AppSettings.h b/NifImport/AppSettings.h index ebfb17da52cd87bfae8002d30d15ebcbe92519dc..588cf97698b0f385766c20d97193533629110199 100644 --- a/NifImport/AppSettings.h +++ b/NifImport/AppSettings.h @@ -19,15 +19,23 @@ HISTORY: class AppSettings { public: - AppSettings(const std::string& name) : Name(name), parsedImages(false) {} + AppSettings(const std::string& name) + : Name(name) + , parsedImages(false) + , useSkeleton(false) + , goToSkeletonBindPosition(true) + {} std::string Name; std::string rootPath; bool parsedImages; stringlist searchPaths; + stringlist textureRootPaths; stringlist rootPaths; stringlist extensions; std::string Skeleton; + bool useSkeleton; + bool goToSkeletonBindPosition; NameValueCollection Environment; NameValueCollection imgTable; @@ -35,6 +43,9 @@ public: void ReadSettings(std::string iniFile); std::string FindImage(const std::string& fname); + // Check whether the given file is a child of the root paths + bool IsFileInRootPaths(const std::string& fname); + template<typename T> inline T GetSetting(std::string setting){ T v; @@ -73,10 +84,32 @@ public: } }; -typedef std::map<std::string, AppSettings, ltstr> AppSettingsMap; +typedef std::list<AppSettings> AppSettingsMap; + +struct AppSettingsNameEquivalence : public ltstr +{ + bool operator()(const AppSettings& n1, const AppSettings& n2) const { + return ltstr::operator()(n1.Name, n2.Name); + } + bool operator()(const string& n1, const AppSettings& n2) const { + return ltstr::operator()(n1, n2.Name); + } + bool operator()(const AppSettings& n1, const string& n2) const { + return ltstr::operator()(n1.Name, n2); + } +}; // The Global Map // Global so that I dont have to parse the texture directories on every import extern AppSettingsMap TheAppSettings; +inline AppSettings* FindAppSetting(const std::string& name){ + AppSettingsNameEquivalence equiv; + for (AppSettingsMap::iterator itr=TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){ + if (!equiv(*itr, name) && !equiv(name, *itr)) + return &(*itr); + } + return NULL; +} + #endif //_APPSETTINGS_H_ \ No newline at end of file diff --git a/NifImport/MaxNifImport.cpp b/NifImport/MaxNifImport.cpp index 503f24a1a5bb6c7b31e893991ea53f8cef753268..1bbe69a94bea89fe2193dfb391348d44b4c5df07 100644 --- a/NifImport/MaxNifImport.cpp +++ b/NifImport/MaxNifImport.cpp @@ -94,6 +94,7 @@ public: float autoSmoothAngle; bool flipUVTextures; bool enableSkinSupport; + bool goToSkeletonBindPosition; // Biped/Bones related settings string skeleton; @@ -108,6 +109,8 @@ public: bool isBiped; bool removeUnusedImportedBones; bool forceRotation; + bool browseForSkeleton; + string defaultSkeletonName; float minBoneWidth; float maxBoneWidth; @@ -124,12 +127,33 @@ public: if (p) *p = 0; path = buffer; - blocks = ReadNifList( name ); - nodes = DynamicCast<NiNode>(blocks); + // Load ini settings iniFileValid = false; + LoadIniSettings(); - if (isValid()) { - LoadIniSettings(); + // load file + blocks = ReadNifList( name ); + nodes = DynamicCast<NiNode>(blocks); + if (goToSkeletonBindPosition) + GoToSkeletonBindPosition(nodes); + + // Apply post processing checks after reading blocks + if (isValid()){ + 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()); } @@ -398,17 +422,49 @@ int MaxNifImport::DoImport(const TCHAR *filename,ImpInterface *i, Interface *gi, else { vector<string> importedBones; - if (importer.importSkeleton && !importer.skeleton.empty()) + if (importer.importSkeleton) { - NifImporter skelImport(importer.skeleton.c_str(), i, gi, suppressPrompts); - if (skelImport.isValid()) + if (importer.browseForSkeleton) { - if (skelImport.useBiped){ - skelImport.ImportBipeds(skelImport.nodes); - } else { - skelImport.ImportBones(DynamicCast<NiNode>(skelImport.nodes[0]->GetChildren())); - if (importer.removeUnusedImportedBones){ - importedBones = GetNamesOfNodes(skelImport.nodes); + 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); + } } } } @@ -417,7 +473,8 @@ int MaxNifImport::DoImport(const TCHAR *filename,ImpInterface *i, Interface *gi, importer.ImportBones(DynamicCast<NiNode>(importer.nodes[0]->GetChildren())); ok = importer.ImportMeshes(importer.nodes[0]); - if (importer.removeUnusedImportedBones){ + + if (importer.importSkeleton && importer.removeUnusedImportedBones){ vector<string> importedNodes = GetNamesOfNodes(importer.nodes); sort(importedBones.begin(), importedBones.end()); sort(importedNodes.begin(), importedNodes.end()); @@ -460,9 +517,20 @@ void NifImporter::LoadIniSettings() this->iniFileName = iniName; iniFileValid = (-1 != _access(iniName, 0)); - string curapp = GetIniValue<string>(NifImportSection, "CurrentApp", ""); - AppSettingsMap::iterator itr = TheAppSettings.find(curapp); - appSettings = (itr != TheAppSettings.end()) ? &(*itr).second : NULL; + // 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); + } useBiped = GetIniValue<bool>(NifImportSection, "UseBiped", false); skeletonCheck = GetIniValue<string>(NifImportSection, "SkeletonCheck", "Bip*"); @@ -475,21 +543,20 @@ void NifImporter::LoadIniSettings() flipUVTextures = GetIniValue<bool>(NifImportSection, "FlipUVTextures", true); enableSkinSupport = GetIniValue<bool>(NifImportSection, "EnableSkinSupport", true); - skeleton = (appSettings != NULL) ? appSettings->Skeleton : ""; - 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", 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); - importSkeleton = hasSkeleton = HasSkeleton(); - isBiped = IsBiped(); + goToSkeletonBindPosition = (appSettings ? appSettings->goToSkeletonBindPosition : false); } void NifImporter::SaveIniSettings() @@ -773,6 +840,7 @@ INode *NifImporter::CreateBone(const string& name, Point3 startPos, Point3 endPo } return result.n; } + fpBones->ReleaseInterface(); } return NULL; } @@ -797,6 +865,32 @@ void NifImporter::ImportBones(NiNodeRef node) 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) { @@ -810,31 +904,6 @@ void NifImporter::ImportBones(NiNodeRef node) } else { - 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); if (bone = CreateBone(name, p, pp, zAxis)) { PositionAndRotateNode(bone, p, q, prs); @@ -968,16 +1037,7 @@ bool NifImporter::ImportMaterialAndTextures(ImpNode *node, NiAVObjectRef avObjec bool NifImporter::ImportTransform(ImpNode *node, NiAVObjectRef avObject) { - Matrix44 wt = avObject->GetWorldTransform(); - - Vector3 pos; Matrix33 rot; float scale; - wt.Decompose(pos, rot, scale); - Point3 p(pos.x, pos.y, pos.z); - - Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0)); - m.Invert(); - m.SetTrans(p); - node->SetTransform(0,m); + node->SetTransform(0,TOMATRIX3(avObject->GetWorldTransform())); return true; } @@ -1173,54 +1233,55 @@ bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom) Mesh& m = triObject->GetMesh(); //get the skin interface - 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); + 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; + // 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/MaxNifImport.vcproj b/NifImport/MaxNifImport.vcproj index c118c76f3f8dec98d9f32a0c47754c77a75465fb..c942c2910854273c1f37cfc9cc90e503b456f6bd 100644 --- a/NifImport/MaxNifImport.vcproj +++ b/NifImport/MaxNifImport.vcproj @@ -22,6 +22,7 @@ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" + WholeProgramOptimization="0" > <Tool Name="VCPreBuildEventTool" @@ -112,7 +113,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"
)
)
" + 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)MaxNifImport_Readme.txt" "C:\3dsmax8\"
)
" /> </Configuration> <Configuration @@ -214,7 +215,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"
)
)
" + 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)MaxNifImport_Readme.txt" "C:\3dsmax8\"
)
" /> </Configuration> </Configurations> diff --git a/NifImport/MaxNifImport_Readme.txt b/NifImport/MaxNifImport_Readme.txt index 6026a04538ca9ba5059cdd9b358bc6bfda71cb06..329f250c4370367032c4b49df7bb5f8f4a318261 100644 --- a/NifImport/MaxNifImport_Readme.txt +++ b/NifImport/MaxNifImport_Readme.txt @@ -9,20 +9,37 @@ It uses the NIF File Format Library and Tools library. See the plugin INI file for advanced configuration. +========================= +REQUIREMENTS +========================= +3ds Max 8 + ========================= INSTALL ========================= Extract file to 3dsmax8 installation directory. -Edit the MaxNifTools.ini file to adjust paths and other settings. +Its highly recommended that you edit the MaxNifTools.ini file to adjust paths +and other settings to you machine. -========================= -REQUIREMENTS -========================= -3ds Max 8 +Most importantly: +1. Remove irrelevant applications from the KnownApplications list + +2. Fix the RootPaths, TexturePaths and TextureSearchPaths in the Applications for your machine. + + - The ini file has a substitution mechanism to make it easier to just change one location + and refer to that path in other areas. + + - RootPaths is used to determine if the imported nif is in one of those directories + if so it assumes the nif is from that app and uses those settings for import. + + - TexturePaths is used to test if the relative path in the nif is relative to that directory. + + - TextureSearchPaths will be recursively scanned for file names that match the extensions + (this can be time consuming so keep it to only relevant directories to you). ========================= -UNINSTALLING +UNINSTALL ========================= Delete the installed files from 3dsmax directory. See the list below in the Installed Files section. @@ -36,7 +53,7 @@ v 0.1 ========================= KNOWN ISSUES ========================= -None +o Bipeds do not import correctly. Bones are doing better at the moment. ========================= INSTALLED FILES diff --git a/NifImport/MaxNifTools.ini b/NifImport/MaxNifTools.ini index fb8ea1b472061a89b3f4bc039640a4942543cc4a..3726d4fea38b67a9fbd1f96c1d73616b807135d8 100644 --- a/NifImport/MaxNifTools.ini +++ b/NifImport/MaxNifTools.ini @@ -4,17 +4,16 @@ [System] ; ShortDescription used in the ShortDescription=Netimmerse/Gamebryo - ; KnownApplications - Used to indicate which sections in the ini file point ; to "Applications" which have their own settings in a section below. KnownApplications=Oblivion;Morrowind;Civ4 - ; Reparse the Applications (and therefore Texture directory cache) on every import/export Reparse=0 [MaxNifImport] -; Current Application to get setting/directory information from. Should match KnownApps above -CurrentApp=Oblivion +; Current Application to get setting/directory information from. Should match KnownApps above or Auto. +; Auto will check the import file against the known root paths use that app if a directory match is found. +CurrentApp=Auto ; Wildcard to use on all NiNodes to test whether a biped may be used. Default: Bip* SkeletonCheck=Bip* ; Use Bones or Biped. Biped is broken right now so use bones or nothing. Default: 0 @@ -48,42 +47,56 @@ RemoveUnusedImportedBones=1 MinBoneWidth=0.5 MaxBoneWidth=3 BoneWidthToLengthRatio=0.25 -; Force nub to point back to parent at expense of loss of rotation. Default: 1 +; Force nub to point back to parent at expense of loss of rotation data. Default: 1 ForceRotation=1 +; Browse for skeleton on import. Default: 1 +BrowseForSkeleton=1 +; DefaultName for Skeletons (use if in same directory as imported nif) +DefaultSkeletonName=skeleton.nif - -;; [Applications] -; TextureRootPaths - Semicolon separated list of base directories to look for files +; [Applications] +; RootPaths - Semicolon separated list of base directories to use when determining which app the imported file is from +; +; TextureRootPaths - Semicolon separated list of base directories to look for texturefiles ; TextureExtensions - Extensions to scan for when using TextureSearchPaths ; TextureSeachPaths - Semicolon separated list of directories to look recursively for files in +; +; UseSkeleton - Whether to use skeleton. Default: 0 ; Skeleton - Default Skeleton to use when importing oblivion meshes +; GoToSkeletonBindPosition - Morrowind trishape data is not in the correct bind position for import. Default: 1 [Oblivion] ; Installation Folder InstallPath=[HKLM\SOFTWARE\Bethesda Softworks\Oblivion]=@"Installed Path" -RootPath=${InstallPath}\Data ExtractFolder=E:\Nifs\Oblivion + +RootPath=${InstallPath}\Data MeshRootPath=${ExtractFolder}\Oblivion - Meshes TextureRootPath=${ExtractFolder}\Oblivion - Textures - Compressed - +UseSkeleton=1 Skeleton=${MeshRootPath}\meshes\characters\_male\skeleton.nif +RootPaths=${RootPath};${MeshRootPath};${TextureRootPath} TextureRootPaths=${RootPath};${TextureRootPath} TextureExtensions=.dds; TextureSearchPaths=${RootPath}\Textures;${TextureRootPath}\Textures\Characters;${TextureRootPath}\Textures\Armor +GoToSkeletonBindPosition=0 [Morrowind] InstallPath=[HKLM\SOFTWARE\Bethesda Softworks\Morrowind]=@"Installed Path" RootPath=${InstallPath}\Data Files ExtractFolder=E:\Nifs\Morrowind\Data Files +RootPaths=${RootPath};${ExtractFolder} TextureRootPaths=${RootPath}\Textures;${ExtractFolder}\Textures TextureExtensions=.tga; TextureSearchPaths=${RootPath}\Textures +GoToSkeletonBindPosition=1 [Civ4] InstallPath=[HKEY_LOCAL_MACHINE\SOFTWARE\Firaxis Games\Sid Meier's Civilization 4]=@"INSTALLDIR" RootPath= ExtractFolder=C:\Projects\Main\Civilization4\assets +RootPaths=${ExtractFolder};${InstallPath}\Assets;${InstallPath}\Mods;%USERPROFILE%\My Documents\My Games\Civilization 4\CustomAssets;%USERPROFILE%\My Documents\My Games\Civilization 4\Mods TextureRootPaths=$(ExtractFolder)\art\shared\ TextureExtensions=.dds;.bmp TextureSearchPaths= - +GoToSkeletonBindPosition=0 \ No newline at end of file diff --git a/NifImport/niutils.cpp b/NifImport/niutils.cpp index 1a494b12c77d03aa4fb4a35abb70de2a63eb52ec..52353b86788966fb6ce912f9235861623cbce087 100644 --- a/NifImport/niutils.cpp +++ b/NifImport/niutils.cpp @@ -106,6 +106,7 @@ NameValueCollection ReadIniSection(LPCTSTR Section, LPCTSTR iniFileName ) // Expand Qualifiers in string using a ${Name} syntax. Name will be looked up in the // NameValueCollection and expand in place. Missing names will expand to empty. +// - Please dont give self-referential strings string ExpandQualifiers(const string& src, const NameValueCollection& map) { string value; @@ -125,7 +126,7 @@ string ExpandQualifiers(const string& src, const NameValueCollection& map) string key = src.substr(i+1, term-i-1); NameValueCollection::const_iterator kvp = map.find(key); if (kvp != map.end()) { - value.append(kvp->second); + value.append(ExpandQualifiers(kvp->second, map)); } i = term; } @@ -137,7 +138,7 @@ string ExpandQualifiers(const string& src, const NameValueCollection& map) string key = src.substr(i+1, term-i-1); NameValueCollection::const_iterator kvp = map.find(key); if (kvp != map.end()) { - value.append(kvp->second); + value.append(ExpandQualifiers(kvp->second, map)); } i = term; } @@ -513,3 +514,15 @@ 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 6e2f12bda5c59f560fdf1ee0b82e0caaf6532626..2458ee3c5f5d1ae3992d9a650b4c254d03f6c752 100644 --- a/NifImport/niutils.h +++ b/NifImport/niutils.h @@ -208,6 +208,7 @@ extern Niflib::NiNodeRef FindNodeByName( const vector<Niflib::NiNodeRef>& blocks 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); // Simple conversion helpers static inline float TODEG(float x) { return x * 180.0f / PI; }