Skip to content
Snippets Groups Projects
Commit e3ddff05 authored by Tazpn's avatar Tazpn
Browse files

Add some new options to better auto-detect where the imported nif is coming...

Add some new options to better auto-detect where the imported nif is coming from and default to correct application based on root paths.  Also add some assistance for locating skeleton.nif files if needed.
parent fb61ccae
No related branches found
No related tags found
No related merge requests found
......@@ -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;
}
......@@ -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
......@@ -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;
......
......@@ -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 &quot;C:\3dsmax8\plugcfg&quot; (&#x0D;&#x0A;if not exist &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot; (&#x0D;&#x0A;copy &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot;&#x0D;&#x0A;)&#x0D;&#x0A;)&#x0D;&#x0A;"
CommandLine="if exist &quot;C:\3dsmax8\plugcfg&quot; (&#x0D;&#x0A; if not exist &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot; (&#x0D;&#x0A; copy &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot;&#x0D;&#x0A; )&#x0D;&#x0A;)&#x0D;&#x0A;if exist &quot;C:\3dsmax8&quot; (&#x0D;&#x0A; xcopy /D /Y /I &quot;$(ProjectDir)MaxNifImport_Readme.txt&quot; &quot;C:\3dsmax8\&quot;&#x0D;&#x0A;)&#x0D;&#x0A;"
/>
</Configuration>
<Configuration
......@@ -214,7 +215,7 @@
/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="if exist &quot;C:\3dsmax8\plugcfg&quot; (&#x0D;&#x0A;if not exist &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot; (&#x0D;&#x0A;copy &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot;&#x0D;&#x0A;)&#x0D;&#x0A;)&#x0D;&#x0A;"
CommandLine="if exist &quot;C:\3dsmax8\plugcfg&quot; (&#x0D;&#x0A; if not exist &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot; (&#x0D;&#x0A; copy &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot;&#x0D;&#x0A; )&#x0D;&#x0A;)&#x0D;&#x0A;if exist &quot;C:\3dsmax8&quot; (&#x0D;&#x0A; xcopy /D /Y /I &quot;$(ProjectDir)MaxNifImport_Readme.txt&quot; &quot;C:\3dsmax8\&quot;&#x0D;&#x0A;)&#x0D;&#x0A;"
/>
</Configuration>
</Configurations>
......
......@@ -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
......
......@@ -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
......@@ -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
......@@ -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; }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment