diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt index cdc748b693785f6b571da56cb8d80a71034c5936..7bcfc267ee9d79054f7b5e70c56811c77ad54a9d 100644 --- a/MaxNifPlugins_Readme.txt +++ b/MaxNifPlugins_Readme.txt @@ -27,6 +27,31 @@ Change log ---------- + 0.2.6 + ----- + o All + - Consolidate code to single plugin: NifPlugins.dlu + - Please remove the following when upgrading to this release. + o MaxNifImport.dli, NifExport.dle, NifProps.dlu, NifFurniture.dlu + - Started Wiki Documentation Project: + o http://www.niftools.org/wiki/index.php/3ds_Max + + o Exporter + - Fix bug in Skin Partitioning that would cause max to hang + - Add links to website and wiki to dialog + - Tag NIF files with Plugin version in first comment field. + - Fix export of selected nodes. + - Support Prn strings for Oblivion and Morrowind when selected + mesh is bound to bone bug does not have skin modifier. + - Change export to place all data in NonAccum nodes when available + Plan to do proper support for NonAccum nodes in the future. + - Add progress bar updates while exporting + + o Importer + - Add links to website and wiki to dialog + - Add support for Prn string imports for Oblivion and Morrowind + + 0.2.5 ----- o Exporter @@ -179,10 +204,8 @@ Installation ------------ - Copy NifExport.dle, NifProps.dlu, MaxNifImport.dli to your 3dsmax\plugins - directory. - - Copy MaxNifTools.ini to your 3dsmax\plugcfg directory. + Copy NifPlugins.dlu, to your 3dsmax\plugins directory. + Copy MaxNifTools.ini to your 3dsmax\plugcfg directory. Usage ----- diff --git a/MaxNifTools.ini b/MaxNifTools.ini index 806e0bfad4b9f3b9afa479e96f7dbe14b1594599..4dcf39a7e11228d6ec13fddd6a19ddd46d043070 100644 --- a/MaxNifTools.ini +++ b/MaxNifTools.ini @@ -9,6 +9,10 @@ ShortDescription=Netimmerse/Gamebryo KnownApplications=Oblivion;Morrowind;Civilization 4;Dark Age of Camelot;User ; Reparse the Applications (and therefore Texture directory cache) on every import/export Reparse=0 +; Website - Primary website +Website=http://www.niftools.org +; Wiki - Documentation website +Wiki=http://www.niftools.org/wiki/index.php/3ds_Max [MaxNifExport] ; Max SDK Plugin Version (0 - AutoSelect; 0x17700d00 - Max6, 0x1f401100 - Max8) Default: 0 @@ -42,10 +46,29 @@ ExportExtraNodes=0 ExportSkin=1 ; Export UserPropBuffer. Default: 0 UserPropBuffer=0 -; Flatten Node Hierarchy. Default: 1 -FlattenHierarchy=1 +; Flatten Node Hierarchy. Default: 0 +FlattenHierarchy=0 ; Remove Unreferenced Bones. Default: 1 RemoveUnreferencedBones=1 +; Sort Nodes To End of Child Lists. Default: 0 +SortNodesToEnd=1 +SkeletonOnly=0 +Cameras=0 +GenerateBoneCollision=0 +ExportTransforms=1 +ExportType=0 +MultiplePartitions=0 +BonesPerVertex=4 +BonesPerPartition=18 +UseTimeTags=0 +AllowAccum=1 +Creator= +; Collapse Transforms to final position. Default: 0 +CollapseTransforms=0 +; Add Tangent and Binormal Extra Data block. Default: 0 +TangentAndBinormalExtraData=0 +FixNormals=0 +UseAlternateStripper=0 [MaxNifImport] ; Max SDK Plugin Version (0 - AutoSelect; 0x17700d00 - Max6, 0x1f401100 - Max8) Default: 0 @@ -70,8 +93,8 @@ RemoveDegenerateFaces=1 RemoveIllegalFaces=1 ; EnableSkinSupport attempt to skin the mesh if bones are available. Default:1 EnableSkinSupport=1 -; Enable Havok Collision Support. Default:1 -EnableCollision=1 +; Enable Havok Collision Support. Default:0 +EnableCollision=0 ; Vertex Color Support mode. (0-Disable; 1-Bake into mesh; 2-Use VertexPaint Modifier) Default:1 VertexColorMode=1 ; Merge NonAccum transforms into base node. Default: 1 @@ -80,6 +103,10 @@ MergeNonAccum=1 Lights=0 ; Import Cameras. Default: 0 Cameras=0 +; Use Civilization 4 Shader for Materials if available. Default:1 +UseCiv4Shader=1 +ImportUPB=1 +IgnoreRootNode=0 [BipedImport] ; Top level bone import setting. Default:1 @@ -123,6 +150,8 @@ ReplaceTCBRotationWithBezier=1 ApplyOverallTransformToSkinAndBones=1 ; Clear Animation on Import. Default: 1 ClearAnimation=1 +AddNoteTracks=1 +AddTimeTags=1 [Collision] ; Scale Factor when blowing up bhk Shapes @@ -139,25 +168,30 @@ bhkScaleFactor=7.0 ; ; 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 +; TextureUseFullPath - Whether to use fully qualified names when exporting. Default: 0 +; SupportPrnStrings - Whether this application supports Prn Extra Strings as a substitute mechanism for bones parenting +; Rotate90Degrees - Semicolon separated list of nodes that need to be rotated 90 degrees in Y Axis when importing through Prn lists [Oblivion] NiVersion=20.0.0.5 NiUserVersion=11 ; Installation Folder InstallPath=[HKLM\SOFTWARE\Bethesda Softworks\Oblivion]=@"Installed Path" -ExtractFolder=E:\Nifs\Oblivion - RootPath=${InstallPath}\Data + +;ExtractFolder=E:\Nifs\Oblivion ; This is the folder where you extracted the BSA contents for the Oblvion models +ExtractFolder=$(RootPath) 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=1 +SupportPrnStrings=1 +Rotate90Degrees=Bip?? Head [Morrowind] NiVersion=4.0.0.2 @@ -170,6 +204,8 @@ TextureRootPaths=${RootPath}\Textures;${ExtractFolder}\Textures TextureExtensions=.tga; TextureSearchPaths=${RootPath}\Textures GoToSkeletonBindPosition=1 +SupportPrnStrings=1 +Rotate90Degrees=Bip?? Head [Civilization 4] NiVersion=20.0.0.4 @@ -181,8 +217,8 @@ RootPaths=${ExtractFolder};${InstallPath}\Assets;${InstallPath}\Mods;%USERPROFIL TextureRootPaths=$(ExtractFolder)\art\shared\ TextureExtensions=.dds;.bmp TextureSearchPaths= -GoToSkeletonBindPosition=1 DummyNodeMatches=MD;Bip;Bip??;* NonAccum;Effect*;Sound*;Dummy* +TextureUseFullPath=1 [Dark Age of Camelot] NiVersion=10.1.0.0 @@ -194,8 +230,6 @@ RootPaths=${Isles_InstallPath};${ExtractFolder} TextureRootPaths=$(ExtractFolder) TextureExtensions=.dds;.bmp;.tga TextureSearchPaths= -GoToSkeletonBindPosition=1 -ApplyOverallTransformToSkinAndBones=0 [User] NiVersion=20.0.0.5 diff --git a/NifCommon/AppSettings.cpp b/NifCommon/AppSettings.cpp index d25330a5567c6fa437450c59048f67e64d414574..d4697021169436ed1ebfeb1c287f2678d7068a40 100644 --- a/NifCommon/AppSettings.cpp +++ b/NifCommon/AppSettings.cpp @@ -65,6 +65,8 @@ void AppSettings::ReadSettings(string iniFile) textureUseFullPath = GetSetting<bool>("TextureUseFullPath", textureUseFullPath); dummyNodeMatches = TokenizeString(GetSetting<string>("DummyNodeMatches").c_str(), ";"); + rotate90Degrees = TokenizeString(GetSetting<string>("Rotate90Degrees").c_str(), ";"); + supportPrnStrings = GetSetting<bool>("SupportPrnStrings", supportPrnStrings); } void AppSettings::WriteSettings(Interface *gi) diff --git a/NifCommon/AppSettings.h b/NifCommon/AppSettings.h index 87d93ad389f5b13fbd1f1a1383ab83dc217388be..7a0dd224af6ff83777eaa2cf28732bf7092738e5 100644 --- a/NifCommon/AppSettings.h +++ b/NifCommon/AppSettings.h @@ -26,6 +26,7 @@ public: , goToSkeletonBindPosition(true) , disableCreateNubsForBones(false) , textureUseFullPath(false) + , supportPrnStrings(false) {} std::string Name; @@ -46,6 +47,8 @@ public: int applyOverallTransformToSkinAndBones; std::string NiVersion; int NiUserVersion; + stringlist rotate90Degrees; + bool supportPrnStrings; static void Initialize(Interface *gi); void ReadSettings(std::string iniFile); diff --git a/NifCommon/Hyperlinks.cpp b/NifCommon/Hyperlinks.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0fead5cb320d296a5e30b896a13685898bede550 --- /dev/null +++ b/NifCommon/Hyperlinks.cpp @@ -0,0 +1,179 @@ +// Hyperlinks.cpp +// +// Copyright 2002 Neal Stublen +// All rights reserved. +// +// http://www.awesoftware.com +// + +#include <windows.h> + +#include "Hyperlinks.h" + + +#define PROP_ORIGINAL_FONT TEXT("_Hyperlink_Original_Font_") +#define PROP_ORIGINAL_PROC TEXT("_Hyperlink_Original_Proc_") +#define PROP_STATIC_HYPERLINK TEXT("_Hyperlink_From_Static_") +#define PROP_UNDERLINE_FONT TEXT("_Hyperlink_Underline_Font_") + + +LRESULT CALLBACK _HyperlinkParentProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + WNDPROC pfnOrigProc = (WNDPROC) GetProp(hwnd, PROP_ORIGINAL_PROC); + + switch (message) + { + case WM_CTLCOLORSTATIC: + { + HDC hdc = (HDC) wParam; + HWND hwndCtl = (HWND) lParam; + + BOOL fHyperlink = (NULL != GetProp(hwndCtl, PROP_STATIC_HYPERLINK)); + if (fHyperlink) + { + LRESULT lr = CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam); + SetTextColor(hdc, RGB(0, 0, 192)); + return lr; + } + + break; + } + case WM_DESTROY: + { + SetWindowLong(hwnd, GWL_WNDPROC, (LONG) pfnOrigProc); + RemoveProp(hwnd, PROP_ORIGINAL_PROC); + break; + } + } + return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam); +} + +LRESULT CALLBACK _HyperlinkProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + WNDPROC pfnOrigProc = (WNDPROC) GetProp(hwnd, PROP_ORIGINAL_PROC); + + switch (message) + { + case WM_DESTROY: + { + SetWindowLong(hwnd, GWL_WNDPROC, (LONG) pfnOrigProc); + RemoveProp(hwnd, PROP_ORIGINAL_PROC); + + HFONT hOrigFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT); + SendMessage(hwnd, WM_SETFONT, (WPARAM) hOrigFont, 0); + RemoveProp(hwnd, PROP_ORIGINAL_FONT); + + HFONT hFont = (HFONT) GetProp(hwnd, PROP_UNDERLINE_FONT); + DeleteObject(hFont); + RemoveProp(hwnd, PROP_UNDERLINE_FONT); + + RemoveProp(hwnd, PROP_STATIC_HYPERLINK); + + break; + } + case WM_MOUSEMOVE: + { + if (GetCapture() != hwnd) + { + HFONT hFont = (HFONT) GetProp(hwnd, PROP_UNDERLINE_FONT); + SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE); + InvalidateRect(hwnd, NULL, FALSE); + SetCapture(hwnd); + } + else + { + RECT rect; + GetWindowRect(hwnd, &rect); + + POINT pt = { LOWORD(lParam), HIWORD(lParam) }; + ClientToScreen(hwnd, &pt); + + if (!PtInRect(&rect, pt)) + { + HFONT hFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT); + SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE); + InvalidateRect(hwnd, NULL, FALSE); + ReleaseCapture(); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + } + } + break; + } + case WM_SETCURSOR: + { + // Since IDC_HAND is not available on all operating systems, + // we will load the arrow cursor if IDC_HAND is not present. + HCURSOR hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)); + if (NULL == hCursor) + { + hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); + } + SetCursor(hCursor); + return TRUE; + } + case WM_KILLFOCUS: + { + if (GetCapture() == hwnd) + { + HFONT hFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT); + SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE); + InvalidateRect(hwnd, NULL, FALSE); + ReleaseCapture(); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + } + } + break; + } + + return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam); +} + +BOOL ConvertStaticToHyperlink(HWND hwndCtl) +{ + // Subclass the parent so we can color the controls as we desire. + + HWND hwndParent = GetParent(hwndCtl); + if (NULL != hwndParent) + { + WNDPROC pfnOrigProc = (WNDPROC) GetWindowLong(hwndParent, GWL_WNDPROC); + if (pfnOrigProc != _HyperlinkParentProc) + { + SetProp(hwndParent, PROP_ORIGINAL_PROC, (HANDLE) pfnOrigProc); + SetWindowLong(hwndParent, GWL_WNDPROC, (LONG) (WNDPROC) _HyperlinkParentProc); + } + } + + // Make sure the control will send notifications. + + DWORD dwStyle = GetWindowLong(hwndCtl, GWL_STYLE); + SetWindowLong(hwndCtl, GWL_STYLE, dwStyle | SS_NOTIFY); + + // Subclass the existing control. + + WNDPROC pfnOrigProc = (WNDPROC) GetWindowLong(hwndCtl, GWL_WNDPROC); + SetProp(hwndCtl, PROP_ORIGINAL_PROC, (HANDLE) pfnOrigProc); + SetWindowLong(hwndCtl, GWL_WNDPROC, (LONG) (WNDPROC) _HyperlinkProc); + + // Create an updated font by adding an underline. + + HFONT hOrigFont = (HFONT) SendMessage(hwndCtl, WM_GETFONT, 0, 0); + SetProp(hwndCtl, PROP_ORIGINAL_FONT, (HANDLE) hOrigFont); + + LOGFONT lf; + GetObject(hOrigFont, sizeof(lf), &lf); + lf.lfUnderline = TRUE; + + HFONT hFont = CreateFontIndirect(&lf); + SetProp(hwndCtl, PROP_UNDERLINE_FONT, (HANDLE) hFont); + + // Set a flag on the control so we know what color it should be. + + SetProp(hwndCtl, PROP_STATIC_HYPERLINK, (HANDLE) 1); + + return TRUE; +} + +BOOL ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId) +{ + return ConvertStaticToHyperlink(GetDlgItem(hwndParent, uiCtlId)); +} diff --git a/NifCommon/Hyperlinks.h b/NifCommon/Hyperlinks.h new file mode 100644 index 0000000000000000000000000000000000000000..2fc4d0b088f302ad46fa56324e4ba21f7d0c1910 --- /dev/null +++ b/NifCommon/Hyperlinks.h @@ -0,0 +1,10 @@ +// Hyperlinks.h +// +// Copyright 2002 Neal Stublen +// All rights reserved. +// +// http://www.awesoftware.com +// + +BOOL ConvertStaticToHyperlink(HWND hwndCtl); +BOOL ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId); diff --git a/NifCommon/niutils.cpp b/NifCommon/niutils.cpp index b46ca0d562248312e56887780d56b560cf81b5ab..89c629024b465a72bbae345bd096e4f7ced4ab90 100644 --- a/NifCommon/niutils.cpp +++ b/NifCommon/niutils.cpp @@ -937,3 +937,74 @@ void CalcCenteredSphere(const vector<Vector3>& vertices, Vector3& center, float& radius = max(radius, mag); } } + + + +static void TransformVector3(Matrix44& tm, vector<Vector3>& pts) +{ + Matrix44::IDENTITY; + for (vector<Vector3>::iterator itr = pts.begin(); itr != pts.end(); ++itr) + { + Matrix44 m4(*itr, Matrix33::IDENTITY, 1.0f); + Matrix44 ntm = m4 * tm; + Vector3 v = ntm.GetTranslation(); + (*itr) = v; + } +} + +void CollapseGeomTransform(NiNode node) { + +} + +void CollapseGeomTransform(NiTriBasedGeomRef shape) +{ + NiTriBasedGeomDataRef data = shape->GetData(); + vector<Vector3> verts = data->GetVertices(); + vector<Vector3> norms = data->GetNormals(); + int nuvsets = data->GetUVSetCount(); + vector< vector<TexCoord> > uvSets; + uvSets.resize(nuvsets); + for (int i=0; i<nuvsets; ++i) + uvSets[i] = data->GetUVSet(i); + + Matrix44 ltm = shape->GetLocalTransform(); + Matrix44 invtm = ltm.Inverse(); + Matrix44 tm = ltm * invtm; + shape->SetLocalTransform(tm); + + TransformVector3(ltm, verts); + + data->SetVertices(verts); + data->SetNormals(norms); + data->SetUVSetCount(nuvsets); + for (int i=0; i<nuvsets; ++i) + data->SetUVSet(i, uvSets[i]); +} + +void CollapseGeomTransforms(vector<NiTriBasedGeomRef>& shapes) +{ + for (vector<NiTriBasedGeomRef>::iterator itr = shapes.begin(); itr != shapes.end(); ++itr) { + CollapseGeomTransform(*itr); + } +} + +void FixNormals(vector<Triangle>& tris, vector<Vector3>& verts, vector<Vector3>& norms) +{ + if (tris.size() != norms.size()) + return; + + int n = tris.size(); + for (int i=0; i < n; ++i) + { + Triangle& tri = tris[i]; + Vector3 v1 = verts[tri.v1]; + Vector3 v2 = verts[tri.v2]; + Vector3 v3 = verts[tri.v3]; + Vector3 n1 = (v2-v1).CrossProduct(v3-v1).Normalized(); + Vector3 n2 = norms[i]; + float dp = n1.DotProduct(n2); + if ( dp < 0.0f ) { + std::swap(tri.v3, tri.v2); + } + } +} \ No newline at end of file diff --git a/NifCommon/niutils.h b/NifCommon/niutils.h index 294205978136de04e80ad65b70568f4c8006bc94..3403a7d2595f5e1c1951d36d320d73fc849445d9 100644 --- a/NifCommon/niutils.h +++ b/NifCommon/niutils.h @@ -395,4 +395,8 @@ inline Niflib::Ref<T> CreateNiObject() { return Niflib::StaticCast<T>(Niflib::CreateObject(T::TypeConst().GetTypeName())); } +void CollapseGeomTransform(Niflib::NiTriBasedGeomRef shape); +void CollapseGeomTransforms(std::vector<Niflib::NiTriBasedGeomRef>& shapes); +void FixNormals(std::vector<Niflib::Triangle>& tris, std::vector<Niflib::Vector3>& verts, std::vector<Niflib::Vector3>& norms); + #endif // _NIUTILS_H_ \ No newline at end of file diff --git a/NifExport/Animation.cpp b/NifExport/Animation.cpp index 9496485d03cc33c8fbcf94f72626d760012e9c85..865f94c79821c52f16c44ec2a8c392752e600d3c 100644 --- a/NifExport/Animation.cpp +++ b/NifExport/Animation.cpp @@ -52,7 +52,7 @@ struct AnimationExport bool doExport(NiControllerSequenceRef seq); bool doExport(NiControllerManagerRef ctrl, INode *node); - bool exportController(INode *node); + bool exportController(INode *node, bool hasAccum); Control *GetTMController(INode* node); NiTimeControllerRef exportController(INode *node, Interval range, bool setTM ); bool GetTextKeys(INode *node, vector<StringKey>& textKeys); @@ -134,7 +134,7 @@ NiNodeRef Exporter::createAccumNode(NiNodeRef parent, INode *node) { NiNodeRef accumNode; bool isTracked = isNodeTracked(node); - if (!Exporter::mAllowAccum || !isTracked) + if (!Exporter::mAllowAccum || (!isTracked && !isSkeletonRoot(node))) { accumNode = parent; } @@ -369,28 +369,7 @@ bool AnimationExport::doExport(NiControllerSequenceRef seq) // Now let the fun begin. - bool ok = exportController(node); - - // Handle NonAccum - if (ok && Exporter::mAllowAccum) - { - NiNodeRef ninode = new NiNode(); - ninode->SetName(FormatString("%s NonAccum", node->GetName())); - - if (Exporter::mNifVersionInt >= VER_10_2_0_0) - { - NiTransformControllerRef control = new NiTransformController(); - NiTransformInterpolatorRef interp = new NiTransformInterpolator(); - ninode->AddController(StaticCast<NiTimeController>(control)); - control->SetInterpolator(StaticCast<NiInterpolator>(interp)); - - interp->SetTranslation( Vector3(0.0f, 0.0f, 0.0f) ); - interp->SetScale( 1.0f ); - interp->SetRotation( Quaternion(1.0f, 0.0f, 0.0f, 0.0f) ); - seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(control), Exporter::mDefaultPriority); - } - } - return ok; + return exportController(node, true); } bool AnimationExport::doExport(NiControllerManagerRef mgr, INode *node) @@ -521,30 +500,7 @@ bool AnimationExport::doExport(NiControllerManagerRef mgr, INode *node) this->range = this->ranges[this->seq]; // Now let the fun begin. - bool ok = exportController(node); - - // Handle NonAccum - if (ok && Exporter::mAllowAccum) - { - NiNodeRef ninode = ne.getNode( FormatString("%s NonAccum", node->GetName()) ); - objRefs.insert( StaticCast<NiAVObject>(ninode) ); - - if (Exporter::mNifVersionInt >= VER_10_2_0_0) - { - NiTransformControllerRef control = new NiTransformController(); - NiTransformInterpolatorRef interp = new NiTransformInterpolator(); - ninode->AddController(StaticCast<NiTimeController>(control)); - control->SetInterpolator(StaticCast<NiInterpolator>(interp)); - - interp->SetTranslation( Vector3(0.0f, 0.0f, 0.0f) ); - interp->SetScale( 1.0f ); - interp->SetRotation( Quaternion(1.0f, 0.0f, 0.0f, 0.0f) ); - seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(control), Exporter::mDefaultPriority); - - // now remove temporary controller - ninode->RemoveController(StaticCast<NiTimeController>(control)); - } - } + bool ok = exportController(node, true); } // Set objects with animation @@ -598,6 +554,8 @@ NiTimeControllerRef AnimationExport::exportController(INode *node, Interval rang bool skip = false; NiTimeControllerRef timeControl; + ne.ProgressUpdate(Exporter::Animation, FormatText("'%s' Animation", node->GetName())); + // Primary recursive decent routine ObjectState os = node->EvalWorldState(range.Start()); @@ -626,11 +584,11 @@ NiTimeControllerRef AnimationExport::exportController(INode *node, Interval rang bool keepData = false; // Set default transform to NaN except for root node - Vector3 trans(FloatNegINF, FloatNegINF, FloatNegINF); - Quaternion rot(FloatNegINF, FloatNegINF, FloatNegINF, FloatNegINF); + //Vector3 trans(FloatNegINF, FloatNegINF, FloatNegINF); + //Quaternion rot(FloatNegINF, FloatNegINF, FloatNegINF, FloatNegINF); float scale = FloatNegINF; - //Vector3 trans = TOVECTOR3(tm.GetTrans()); - //Quaternion rot = TOQUAT( Quat(tm), true ); + Vector3 trans = TOVECTOR3(tm.GetTrans()); + Quaternion rot = TOQUAT( Quat(tm), true ); NiNodeRef ninode = ne.getNode( node->GetName() ); if (setTM) { @@ -867,7 +825,7 @@ NiTimeControllerRef AnimationExport::exportController(INode *node, Interval rang return timeControl; } -bool AnimationExport::exportController(INode *node) +bool AnimationExport::exportController(INode *node, bool hasAccum) { bool ok = true; @@ -880,11 +838,42 @@ bool AnimationExport::exportController(INode *node) if (control != NULL) { NiSingleInterpolatorControllerRef interpControl = DynamicCast<NiSingleInterpolatorController>(control); - if (interpControl) { + if (interpControl) + { // Get Priority from node float priority; npGetProp(node, NP_ANM_PRI, priority, Exporter::mDefaultPriority); seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(control), priority); + + // Handle NonAccum + if (Exporter::mAllowAccum && hasAccum) + { + NiTransformInterpolatorRef interp = DynamicCast<NiTransformInterpolator>(interpControl->GetInterpolator()); + NiNodeRef accnode = ne.getNode( FormatString("%s NonAccum", node->GetName()) ); + objRefs.insert( StaticCast<NiAVObject>(accnode) ); + + if (Exporter::mNifVersionInt >= VER_10_2_0_0) + { + NiTransformControllerRef acccontrol = new NiTransformController(); + NiTransformInterpolatorRef accinterp = new NiTransformInterpolator(); + accnode->AddController(StaticCast<NiTimeController>(acccontrol)); + acccontrol->SetInterpolator(StaticCast<NiInterpolator>(accinterp)); + + accinterp->SetTranslation( Vector3(0.0f, 0.0f, 0.0f) ); + accinterp->SetScale( 1.0f ); + accinterp->SetRotation( Quaternion(1.0f, 0.0f, 0.0f, 0.0f) ); + + // Transfer entire data to accum node + if (interp != NULL) { + accinterp->SetData( interp->GetData() ); + interp->SetData( NiTransformDataRef() ); + } + seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(acccontrol), Exporter::mDefaultPriority); + + accnode->RemoveController(acccontrol); + } + } + } else { @@ -899,7 +888,7 @@ bool AnimationExport::exportController(INode *node) for (int i=0, n=node->NumberOfChildren(); ok && i<n; ++i) { INode *child = node->GetChildNode(i); - ok |= exportController(child); + ok |= exportController(child, false); } return ok; } \ No newline at end of file diff --git a/NifExport/Coll.cpp b/NifExport/Coll.cpp index 39a4c81feddab0d3de873233bf178d270dfe61a4..5503d203fc12cd047e0657cb7202159058c01234 100755 --- a/NifExport/Coll.cpp +++ b/NifExport/Coll.cpp @@ -249,6 +249,8 @@ bool Exporter::makeCollisionHierarchy(NiNodeRef &parent, INode *node, TimeValue Exporter::Result Exporter::exportCollision(NiNodeRef &parent, INode *node) { + ProgressUpdate(Collision, FormatText("'%s' Collision", node->GetName())); + // marked as collision? bool coll = npIsCollision(node); diff --git a/NifExport/Config.cpp b/NifExport/Config.cpp index e4655ff47fd769cc0c3f9a6954665e112a410015..39ab0efcbaf817a43e6c64f0e34dc51e241611b2 100755 --- a/NifExport/Config.cpp +++ b/NifExport/Config.cpp @@ -76,9 +76,14 @@ void Exporter::writeConfig(Interface *i) SetIniValue(NifExportSection, "MultiplePartitions", mMultiplePartitions, iniName); SetIniValue<int>(NifExportSection, "BonesPerVertex", mBonesPerVertex, iniName); SetIniValue<int>(KfExportSection, "BonesPerPartition", mBonesPerPartition, iniName); - SetIniValue(NifExportSection, "UseTimeTags", mUseTimeTags, iniName); + //SetIniValue(NifExportSection, "UseTimeTags", mUseTimeTags, iniName); SetIniValue(NifExportSection, "AllowAccum", mAllowAccum, iniName); + SetIniValue(NifExportSection, "CollapseTransforms", mCollapseTransforms, iniName); + SetIniValue(NifExportSection, "FixNormals", mFixNormals, iniName); + SetIniValue(NifExportSection, "TangentAndBinormalExtraData", mTangentAndBinormalExtraData, iniName); + SetIniValue(NifExportSection, "UseAlternateStripper", mUseAlternateStripper, iniName); + SetIniValue<string>(NifExportSection, "Creator", mCreatorName, iniName); @@ -143,11 +148,15 @@ void Exporter::readConfig(Interface *i) mMultiplePartitions = GetIniValue(NifExportSection, "MultiplePartitions", false, iniName); mBonesPerVertex = GetIniValue<int>(NifExportSection, "BonesPerVertex", 4, iniName); - mBonesPerPartition = GetIniValue<int>(KfExportSection, "BonesPerPartition", 20, iniName); + mBonesPerPartition = GetIniValue<int>(NifExportSection, "BonesPerPartition", 20, iniName); - mUseTimeTags = GetIniValue(NifExportSection, "UseTimeTags", false, iniName); + //mUseTimeTags = GetIniValue(NifExportSection, "UseTimeTags", false, iniName); mAllowAccum = GetIniValue(NifExportSection, "AllowAccum", true, iniName); + mCollapseTransforms = GetIniValue(NifExportSection, "CollapseTransforms", false, iniName); + mFixNormals = GetIniValue(NifExportSection, "FixNormals", false, iniName); + mTangentAndBinormalExtraData = GetIniValue(NifExportSection, "TangentAndBinormalExtraData", false, iniName); + mUseAlternateStripper = GetIniValue(NifExportSection, "UseAlternateStripper", false, iniName); mCreatorName = GetIniValue<string>(NifExportSection, "Creator", "", iniName); } } @@ -180,6 +189,69 @@ void Exporter::writeKfConfig(Interface *i) } +AppSettings * Exporter::exportAppSettings() +{ + TCHAR iniName[MAX_PATH]; + LPCTSTR pluginDir = GetCOREInterface()->GetDir(APP_PLUGCFG_DIR); + PathCombine(iniName, pluginDir, "MaxNifTools.ini"); + + // Update the current app version + AppSettings * appSettings = FindAppSetting(Exporter::mGameName); + if (appSettings == NULL && !TheAppSettings.empty()) + appSettings = &TheAppSettings.front(); + + if (Exporter::mAutoDetect){ + SetIniValue<string>(NifExportSection, "CurrentApp", "AUTO", iniName); + } else { + SetIniValue<string>(NifExportSection, "CurrentApp", appSettings->Name, iniName); + } + + appSettings->NiVersion = Exporter::mNifVersion; + appSettings->NiUserVersion = Exporter::mNifUserVersion; + appSettings->rotate90Degrees = Exporter::mRotate90Degrees; + appSettings->supportPrnStrings = Exporter::mSupportPrnStrings; + + return appSettings; +} + +AppSettings *Exporter::importAppSettings(string fname) +{ + AppSettings *appSettings = NULL; + TCHAR iniName[MAX_PATH]; + LPCTSTR pluginDir = GetCOREInterface()->GetDir(APP_PLUGCFG_DIR); + PathCombine(iniName, pluginDir, "MaxNifTools.ini"); + + // Locate which application to use. If Auto, find first app where this file appears in the root path list + string curapp = GetIniValue<string>(NifExportSection, "CurrentApp", "AUTO", iniName); + if (0 == _tcsicmp(curapp.c_str(), "AUTO")) { + Exporter::mAutoDetect = true; + // Scan Root paths + for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){ + if ((*itr).IsFileInRootPaths(fname)) { + appSettings = &(*itr); + break; + } + } + } else { + Exporter::mAutoDetect = false; + appSettings = FindAppSetting(curapp); + } + if (appSettings == NULL && !TheAppSettings.empty()) + appSettings = &TheAppSettings.front(); + + if (!appSettings) + return NULL; + + Exporter::mGameName = appSettings->Name; + Exporter::mNifVersion = appSettings->NiVersion; + Exporter::mNifUserVersion = appSettings->NiUserVersion; + if (!appSettings->rotate90Degrees.empty()) + Exporter::mRotate90Degrees = appSettings->rotate90Degrees; + Exporter::mSupportPrnStrings = appSettings->supportPrnStrings; + + return appSettings; +} + void regSet(HKEY hKey, const char *value, float f) { DWORD dw = *((DWORD*)&f); diff --git a/NifExport/Exporter.cpp b/NifExport/Exporter.cpp index 2067aba6a31a04bc79e246be547623b785990eb4..d65cecd7f6c693649ea4576b197127f4f3e7baaa 100755 --- a/NifExport/Exporter.cpp +++ b/NifExport/Exporter.cpp @@ -40,71 +40,152 @@ bool Exporter::mUseTimeTags = false; bool Exporter::mAutoDetect = true; bool Exporter::mAllowAccum = true; string Exporter::mCreatorName; +bool Exporter::mCollapseTransforms = false; +bool Exporter::mFixNormals = false; +bool Exporter::mTangentAndBinormalExtraData = false; +bool Exporter::mSupportPrnStrings = false; +stringlist Exporter::mRotate90Degrees; +bool Exporter::mSuppressPrompts = false; +bool Exporter::mUseAlternateStripper = false; + +static bool IsNodeOrParentSelected(INode *node) { + if (node == NULL) + return false; + if (node->Selected()) + return true; + return IsNodeOrParentSelected(node->GetParentNode()); +} Exporter::Exporter(Interface *i, AppSettings *appSettings) : mI(i), mAppSettings(appSettings) { + memset(progressCounters, 0, sizeof(progressCounters)); + memset(progressMax, 0, sizeof(progressMax)); } - Exporter::Result Exporter::doExport(NiNodeRef &root, INode *node) { - //root->SetName("Scene Root"); + root->SetName("Scene Root"); - int nifVersion = GetVersion(Exporter::mNifVersion); + int nifVersion = ParseVersionString(Exporter::mNifVersion); mIsBethesda = (nifVersion == VER_20_0_0_5 || nifVersion == VER_20_0_0_4) && (Exporter::mNifUserVersion == 11); if (mUseTimeTags && nifVersion >= VER_20_0_0_4) { throw runtime_error("Time tag sequences are not supported for version 20.0.0.4 or higher."); } - CalcBoundingBox(node, mBoundingBox); - - if (mSkeletonOnly && mIsBethesda) + if (!Exporter::mSelectedOnly) { - BSBoundRef bsb = CreateNiObject<BSBound>(); - bsb->SetName("BBX"); - bsb->SetCenter( TOVECTOR3(mBoundingBox.Center()) ); - bsb->SetDimensions( TOVECTOR3(mBoundingBox.Width() / 2.0f) ); - root->AddExtraData(DynamicCast<NiExtraData>(bsb)); - - BSXFlagsRef bsx = CreateNiObject<BSXFlags>(); - bsx->SetName("BSX"); - bsx->SetFlags( 0x00000007 ); - root->AddExtraData(DynamicCast<NiExtraData>(bsx)); + CalcBoundingBox(node, mBoundingBox); + + if (mSkeletonOnly && mIsBethesda) + { + BSBoundRef bsb = CreateNiObject<BSBound>(); + bsb->SetName("BBX"); + bsb->SetCenter( TOVECTOR3(mBoundingBox.Center()) ); + bsb->SetDimensions( TOVECTOR3(mBoundingBox.Width() / 2.0f) ); + root->AddExtraData(DynamicCast<NiExtraData>(bsb)); + + BSXFlagsRef bsx = CreateNiObject<BSXFlags>(); + bsx->SetName("BSX"); + bsx->SetFlags( 0x00000007 ); + root->AddExtraData(DynamicCast<NiExtraData>(bsx)); + } + else if (mExportCollision && mIsBethesda) + { + BSXFlagsRef bsx = CreateNiObject<BSXFlags>(); + bsx->SetName("BSX"); + bsx->SetFlags( 0x00000002 ); + root->AddExtraData(DynamicCast<NiExtraData>(bsx)); + } + + exportUPB(root, node); } - else if (mExportCollision && mIsBethesda) - { - BSXFlagsRef bsx = CreateNiObject<BSXFlags>(); - bsx->SetName("BSX"); - bsx->SetFlags( 0x00000002 ); - root->AddExtraData(DynamicCast<NiExtraData>(bsx)); + + mNiRoot = root; + if (mSelectedOnly) { + int count = 0; + int n = mI->GetSelNodeCount(); + vector<INode*> selectedRoots; + for (int i=0; i<n; ++i) { + INode * selNode = mI->GetSelNode(i); + if (!IsNodeOrParentSelected(selNode->GetParentNode())) { + selectedRoots.push_back(selNode); + count += countNodes(selNode); + } + } + if (selectedRoots.size() == 0) { + throw runtime_error("No Nodes have been selected for Export."); + } + + progressMax[Geometry] = progressMax[Skin] = count; + if (mExportCollision) + progressMax[Collision] = progressMax[Geometry]; + if (mExportType == NIF_WO_ANIM) + progressMax[Animation] = progressMax[Geometry]; + + for (int i=0; i<selectedRoots.size(); i++){ + Result result = exportNodes(root, selectedRoots[i]); + if (result != Ok && result != Skip) + return result; + if (mExportCollision) { + result = exportCollision(root, selectedRoots[i]); + if (result != Ok) + return result; + } + } + // Always Zero out root transforms + vector<NiAVObjectRef> children = root->GetChildren(); + for (int i=0; i<children.size(); ++i){ + children[i]->SetLocalTransform(Matrix44::IDENTITY); + } + + // Fix Used Nodes that where never properly initialized. Happens normally during select export + for (NodeMap::iterator itr = mNodeMap.begin(); itr != mNodeMap.end(); ++itr) { + NiNodeRef bone = (*itr).second; + if (bone->GetParent() == NULL) { + if (INode* boneNode = mI->GetINodeByName((*itr).first.c_str())) { + makeNode(root, boneNode, false); + } + } + } + + // Special case when exporting a single branch, use first child as scene root + if (selectedRoots.size() == 1 ) { + vector<NiNodeRef> childnodes = DynamicCast<NiNode>(root->GetChildren()); + if (childnodes.size() == 1) { + NiNodeRef child = childnodes[0]; + root->RemoveChild(child); + root = child; + mNiRoot = root; + exportPrn(root, selectedRoots[0]); + } + } + + } else { + // Estimate progress bar + int count = countNodes(node); + progressMax[Geometry] = progressMax[Skin] = count; + if (mExportCollision) + progressMax[Collision] = progressMax[Geometry]; + if (mExportType == NIF_WO_ANIM) + progressMax[Animation] = progressMax[Geometry]; + + // Normal export + Result result = exportNodes(root, node); + if (result != Ok) + return result; + if (mExportCollision) { + result = exportCollision(root, node); + if (result != Ok) + return result; + } } - bool ok = exportUPB(root, node); - //if (!ok && Exporter::mExportCollision) - //{ - // NiStringExtraDataRef strings = DynamicCast<NiStringExtraData>(CreateBlock("NiStringExtraData")); - // strings->SetName("UPB"); - // strings->SetData("Ellasticity = 0.300000\r\nFriction = 0.300000\r\nUnyielding = 0\r\nProxy_Geometry = <None>\r\nUse_Display_Proxy = 0\r\nDisplay_Children = 1\r\nDisable_Collisions = 0\r\nInactive = 0\r\nDisplay_Proxy = <None>\r\nMass = 0.000000\r\nSimulation_Geometry = 2\r\nCollision_Groups = 589825\r\n"); - // root->AddExtraData(DynamicCast<NiExtraData>(strings)); - //} - - mNiRoot = root; - - Result result = exportNodes(root, node); - if (result != Ok) - return result; - - if (mExportCollision) - { - result = exportCollision(root, node); - if (result != Ok) - return result; - } // handle post export callbacks (like skin) - for (CallbackList::iterator cb = mPostExportCallbacks.begin(); cb != mPostExportCallbacks.end(); cb = mPostExportCallbacks.erase(cb)) - { + progressMax[Skin] = mPostExportCallbacks.size(); + for (CallbackList::iterator cb = mPostExportCallbacks.begin(); cb != mPostExportCallbacks.end(); cb = mPostExportCallbacks.erase(cb)) { + ProgressUpdate(Skin, NULL); (*cb)->execute(); delete (*cb); } @@ -113,15 +194,18 @@ Exporter::Result Exporter::doExport(NiNodeRef &root, INode *node) removeUnreferencedBones(mNiRoot); if (mSortNodesToEnd) sortNodes(mNiRoot); - + root = mNiRoot; return Ok; } // Primary recursive decent routine Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node) { + TSTR nodeName = node->GetName(); bool coll = npIsCollision(node); - if (coll || (node->IsHidden() && !mExportHidden && !coll) || (mSelectedOnly && !node->Selected())) + + ProgressUpdate(Geometry, FormatText("'%s' Geometry", nodeName.data())); + if (coll || (node->IsHidden() && !mExportHidden && !coll)) return Skip; bool local = !mFlattenHierarchy; @@ -134,7 +218,6 @@ Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node) // Always skip bones and bipeds SClass_ID scid = node->SuperClassID(); Class_ID ncid = node->ClassID(); - TSTR nodeName = node->GetName(); TSTR nodeClass; node->GetClassName(nodeClass); // For some unusual reason, bones named Helper are converted to Meshes and @@ -168,9 +251,13 @@ Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node) // Else dont create a node newParent = nodeParent; } - Result result = exportMesh(newParent, node, t); - if (result != Ok) - return result; + // No need to export meshes when NIF is not exported. + if (mExportType != SINGLE_KF_WO_NIF && mExportType != MULTI_KF_WO_NIF) + { + Result result = exportMesh(newParent, node, t); + if (result != Ok) + return result; + } } } else if (mExportCameras && os.obj && os.obj->SuperClassID()==CAMERA_CLASS_ID) @@ -196,3 +283,24 @@ Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node) } return Ok; } + +void Exporter::ProgressUpdate(ProgressSection section, const TCHAR *s) +{ + if (mSuppressPrompts) + return; + + if (mI->GetCancel()){ + throw CancelExporterException(); + } + int total = 1; + int percent = 1; + for (int i=0; i< int(ProgressSectionCount); i++){ + total += progressMax[i]; + if (i < section) { + percent += progressMax[i]; + } else if (i == section ) { + percent += (++progressCounters[i]); + } + } + mI->ProgressUpdate( (percent * 100) / total , s == NULL ? TRUE : FALSE, const_cast<TCHAR*>(s)); +} diff --git a/NifExport/Exporter.h b/NifExport/Exporter.h index 4f0a481a34ec803ebf9c743dffd60edbade1f271..146a3b6a271bc62ead3a36e93ede46e0ae1ce11a 100755 --- a/NifExport/Exporter.h +++ b/NifExport/Exporter.h @@ -46,6 +46,8 @@ public: virtual Result execute() = 0; }; + class CancelExporterException{}; + /* exporter version */ static int mVersion; @@ -85,6 +87,13 @@ public: static bool mAutoDetect; static bool mAllowAccum; static string mCreatorName; + static bool mCollapseTransforms; + static bool mFixNormals; + static bool mTangentAndBinormalExtraData; + static stringlist mRotate90Degrees; + static bool mSupportPrnStrings; + static bool mSuppressPrompts; + static bool mUseAlternateStripper; Exporter(Interface *i, AppSettings *appSettings); @@ -109,6 +118,9 @@ public: // reads config from registry static void readKfConfig(Interface *i); + static AppSettings * exportAppSettings(); + static AppSettings * importAppSettings(string fname); + public: typedef vector<unsigned short> TriStrip; typedef list<TriStrip> TriStrips; @@ -225,8 +237,23 @@ public: NiNodeRef exportBone(NiNodeRef parent, INode *node); Result exportLight(NiNodeRef root, INode *node, GenLight* light); void getChildNodes(INode *node, vector<NiNodeRef>&list); - + bool exportPrn(NiNodeRef &root, INode *node); NiNodeRef createAccumNode(NiNodeRef parent, INode *node); + int countNodes(INode *node); + bool isSkeletonRoot(INode *node); + + /* Progress Bar stuff */ + enum ProgressSection + { + Geometry, + Animation, + Collision, + Skin, + ProgressSectionCount + }; + int progressCounters[ProgressSectionCount]; + int progressMax[ProgressSectionCount]; + void ProgressUpdate( ProgressSection section, const TCHAR *s ); }; #endif diff --git a/NifExport/KfExport.cpp b/NifExport/KfExport.cpp index 8036afb0dd00f206a29f74a3257816236f6ebc72..0d85c97b45a488494901a02fd618b3f0a19ae56f 100644 --- a/NifExport/KfExport.cpp +++ b/NifExport/KfExport.cpp @@ -8,6 +8,7 @@ using namespace Niflib; #define KFEXPORT_CLASS_ID Class_ID(0xa57ff0a4, 0xa0374ffc) static LPCTSTR KfExportSection = TEXT("KfExport"); +extern TSTR shortDescription; class KfExport : public SceneExport { @@ -16,8 +17,9 @@ public: static HWND hParams; int mDlgResult; TSTR iniFileName; - TSTR shortDescription; TSTR fileVersion; + TSTR webSite; + TSTR wikiSite; int ExtCount(); // Number of extensions supported const TCHAR *Ext(int n); // Extension #n (i.e. "3DS") @@ -114,8 +116,8 @@ static BOOL CALLBACK KfExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH); if (tmp[0] != 0) { - int nifVersion = GetVersion(tmp); - if (!IsVersionSupported(nifVersion)) + int nifVersion = ParseVersionString(tmp); + if (!IsSupportedVersion(nifVersion)) { MessageBox(hWnd, FormatString("Version '%s' is not a supported version.", tmp).c_str(), "NifExport", MB_OK|MB_ICONSTOP); return FALSE; @@ -148,19 +150,19 @@ static BOOL CALLBACK KfExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam EndDialog(hWnd, imp->mDlgResult=IDCANCEL); close = true; break; + + case IDC_LBL_LINK: + ShellExecute(hWnd, "open", imp->webSite, NULL, NULL, SW_SHOWNORMAL); + break; + + case IDC_LBL_WIKI: + ShellExecute(hWnd, "open", imp->wikiSite, NULL, NULL, SW_SHOWNORMAL); + break; } if (close) SendMessage(hWnd, WM_CLOSE, 0, 0); } - else if (HIWORD(wParam) == STN_CLICKED) - { - if (LOWORD(wParam) == IDC_LBL_LINK) - { - ShellExecute(hWnd, "open", "http://www.niftools.org", - NULL, NULL, SW_SHOWDEFAULT); - } - } else if (HIWORD(wParam) == CBN_SELCHANGE) { if (LOWORD(wParam) == IDC_CB_GAME) @@ -200,6 +202,8 @@ KfExport::KfExport() iniFileName = iniName; shortDescription = GetIniValue<TSTR>("System", "ShortDescription", "Netimmerse/Gamebryo", iniFileName); fileVersion = GetFileVersion(NULL); + webSite = GetIniValue<TSTR>("System", "Website", "http://www.niftools.org", iniFileName); + wikiSite = GetIniValue<TSTR>("System", "Wiki", "http://www.niftools.org/wiki/index.php/3ds_Max", iniFileName); } KfExport::~KfExport() @@ -328,8 +332,8 @@ int KfExport::DoExport(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL s if (!Exporter::mNifVersion.empty()) { - nifVersion = GetVersion(Exporter::mNifVersion); - if (!IsVersionSupported(nifVersion)) + nifVersion = ParseVersionString(Exporter::mNifVersion); + if (!IsSupportedVersion(nifVersion)) throw exception(FormatString("Version '%s' is not a supported version.").c_str()); } diff --git a/NifExport/Mesh.cpp b/NifExport/Mesh.cpp index 1601f417f6240e5c97575efb81c19a83cd38826e..6752fb9e2917d887bc1f58d9d2b694bc79f7c8d4 100755 --- a/NifExport/Mesh.cpp +++ b/NifExport/Mesh.cpp @@ -112,6 +112,11 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue shape->SetName(name); shape->SetLocalTransform(tm); + + if (Exporter::mCollapseTransforms) { + CollapseGeomTransform(shape); + } + makeSkin(shape, node, grp->second, t); } @@ -129,9 +134,13 @@ NiTriBasedGeomRef Exporter::makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp NiTriBasedGeomRef shape; NiTriBasedGeomDataRef data; + //if (Exporter::mFixNormals) { + // FixNormals(grp.faces, grp.verts, grp.vnorms); + //} + if (exportStrips) { shape = new NiTriStrips(); - data = new NiTriStripsData(grp.faces); + data = new NiTriStripsData(grp.faces, !mUseAlternateStripper); } else { shape = new NiTriShape(); data = new NiTriShapeData(grp.faces); @@ -150,6 +159,9 @@ NiTriBasedGeomRef Exporter::makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp shape->SetData(data); + if (Exporter::mTangentAndBinormalExtraData) + shape->UpdateTangentSpace(); + NiAVObjectRef av(DynamicCast<NiAVObject>(shape)); makeMaterial(av, mtl); @@ -380,17 +392,6 @@ Exporter::Result SkinInstance::execute() shape->GenHardwareSkinInfo(Exporter::mBonesPerPartition, Exporter::mBonesPerVertex); else shape->GenHardwareSkinInfo(0, 0); - - //NiSkinInstanceRef skinInst = shape->GetSkinInstance(); - //NiSkinDataRef skinData = skinInst->GetSkinData(); - //Matrix44 tm = skinData->GetOverallTransform(); - //Matrix3 otm = TOMATRIX3(tm); - //Matrix3 stm = TOMATRIX3(shape->GetLocalTransform()); - //Matrix3 btm = Inverse(bone_init_tm) * stm; - - //Matrix3 asdf = btm * otm; - //skinData->SetOverallTransform(TOMATRIX4(btm)); - return Exporter::Ok; } @@ -567,22 +568,21 @@ NiNodeRef Exporter::exportBone(NiNodeRef parent, INode *node) newParent->SetCollisionObject( NiCollisionObjectRef() ); } newParent = accumNode; - } else if (wildmatch("Bip??", node->GetName())) { + } else if (isSkeletonRoot(node)) { newParent = createAccumNode(newParent, node); } } else // normal handling { // Check for Accum Root using - if (mExportType == NIF_WO_KF) - { + if (mExportType == NIF_WO_KF){ // Add controllers if (Exporter::mAllowAccum) { newParent = createAccumNode(newParent, node); } - } - else if (mExportType != NIF_WO_ANIM && isNodeTracked(node)) - { + } else if (mExportType != NIF_WO_ANIM && isNodeTracked(node)) { + newParent = createAccumNode(newParent, node); + } else if (isSkeletonRoot(node)) { newParent = createAccumNode(newParent, node); } } diff --git a/NifExport/NifExport.cpp b/NifExport/NifExport.cpp index cc14df14e5dfefa2fcc57f36178d7e15fed8b002..13a6e5d2a96661a8dc67c5e398d8a58185e57f4c 100755 --- a/NifExport/NifExport.cpp +++ b/NifExport/NifExport.cpp @@ -5,12 +5,13 @@ #include "obj/NiControllerManager.h" #include "obj/NiTimeController.h" #include "obj/NiControllerSequence.h" - +#include "Hyperlinks.h" using namespace Niflib; #define NifExport_CLASS_ID Class_ID(0xa57ff0a4, 0xa0374ffb) LPCTSTR NifExportSection = TEXT("MaxNifExport"); +TSTR shortDescription; class NifExport : public SceneExport { @@ -19,8 +20,9 @@ public: static HWND hParams; int mDlgResult; TSTR iniFileName; - TSTR shortDescription; TSTR fileVersion; + TSTR webSite; + TSTR wikiSite; int ExtCount(); // Number of extensions supported const TCHAR *Ext(int n); // Extension #n (i.e. "3DS") @@ -34,7 +36,8 @@ public: void ShowAbout(HWND hWnd); // Show DLL's "About..." box BOOL SupportsOptions(int ext, DWORD options); - int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0); + int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0); + int DoExportInternal(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options); NifExport(); ~NifExport(); @@ -63,199 +66,201 @@ ClassDesc2* GetNifExportDesc() { return &NifExportDesc; } extern list<NiObjectRef> GetAllObjectsByType( NiObjectRef const & root, const Type & type ); - - BOOL CALLBACK NifExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { static NifExport *imp = NULL; + char buffer[256]; + static bool setCursor = false; switch (message) - { - case WM_INITDIALOG: - { - imp = (NifExport *)lParam; - - // Append file version to dialog - if (!imp->fileVersion.isNull()) { - char buffer[256]; - GetWindowText(hWnd, buffer, _countof(buffer)); - _tcscat(buffer, TEXT(" ")); - _tcscat(buffer, imp->fileVersion); - SetWindowText(hWnd, buffer); + { + case WM_INITDIALOG: + { + imp = (NifExport *)lParam; + + // Append file version to dialog + if (!imp->fileVersion.isNull()) { + GetWindowText(hWnd, buffer, _countof(buffer)); + _tcscat(buffer, TEXT(" ")); + _tcscat(buffer, imp->fileVersion); + SetWindowText(hWnd, buffer); + } + + CenterWindow(hWnd,GetParent(hWnd)); + CheckDlgButton(hWnd, IDC_CHK_STRIPS, Exporter::mTriStrips); + CheckDlgButton(hWnd, IDC_CHK_HIDDEN, Exporter::mExportHidden); + CheckDlgButton(hWnd, IDC_CHK_FURN, Exporter::mExportFurn); + CheckDlgButton(hWnd, IDC_CHK_LIGHTS, Exporter::mExportLights); + CheckDlgButton(hWnd, IDC_CHK_VCOLORS, Exporter::mVertexColors); + SetDlgItemText(hWnd, IDC_ED_TEXPREFIX, Exporter::mTexPrefix.c_str()); + CheckDlgButton(hWnd, IDC_CHK_COLL, Exporter::mExportCollision); + CheckDlgButton(hWnd, IDC_CHK_REMAP, Exporter::mRemapIndices); + + CheckDlgButton(hWnd, IDC_CHK_EXTRA, Exporter::mExportExtraNodes); + CheckDlgButton(hWnd, IDC_CHK_UPB, Exporter::mUserPropBuffer); + CheckDlgButton(hWnd, IDC_CHK_HIER, Exporter::mFlattenHierarchy); + CheckDlgButton(hWnd, IDC_CHK_REM_BONES, Exporter::mRemoveUnreferencedBones); + CheckDlgButton(hWnd, IDC_CHK_SORTNODES, Exporter::mSortNodesToEnd); + CheckDlgButton(hWnd, IDC_CHK_SKEL_ONLY, Exporter::mSkeletonOnly); + CheckDlgButton(hWnd, IDC_CHK_CAMERA, Exporter::mExportCameras); + CheckDlgButton(hWnd, IDC_CHK_BONE_COLL, Exporter::mGenerateBoneCollision); + + string selection = Exporter::mGameName; + string version = Exporter::mNifVersion; + string userVer = FormatString("%d", Exporter::mNifUserVersion); + for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr) + SendDlgItemMessage(hWnd, IDC_CB_GAME, CB_ADDSTRING, 0, LPARAM(itr->Name.c_str())); + SendDlgItemMessage(hWnd, IDC_CB_GAME, CB_SELECTSTRING, WPARAM(-1), LPARAM(selection.c_str())); + SendDlgItemMessage(hWnd, IDC_CB_VERSION, WM_SETTEXT, 0, LPARAM(version.c_str())); + SendDlgItemMessage(hWnd, IDC_CB_USER_VERSION, WM_SETTEXT, 0, LPARAM(userVer.c_str())); + CheckDlgButton(hWnd, IDC_CHK_AUTO_DETECT, Exporter::mAutoDetect); + + // Populate Type options + SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WO_ANIM, LPARAM("NIF w/o Animation")); + SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WO_KF, LPARAM("NIF with Animation")); + SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::SINGLE_KF_WITH_NIF, LPARAM("Single KF with NIF")); + SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::SINGLE_KF_WO_NIF, LPARAM("Single KF w/o NIF")); + SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::MULTI_KF_WITH_NIF, LPARAM("Multi KF with NIF")); + SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::MULTI_KF_WO_NIF, LPARAM("Multi KF w/o NIF")); + SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WITH_MGR, LPARAM("NIF w/ Manager")); + + SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_SETCURSEL, WPARAM(Exporter::mExportType), 0); + + CheckDlgButton(hWnd, IDC_CHK_TRANSFORMS2, Exporter::mExportTransforms); + SetDlgItemText(hWnd, IDC_ED_PRIORITY2, FormatText("%.1f", Exporter::mDefaultPriority)); + //CheckDlgButton(hWnd, IDC_CHK_USE_TIME_TAGS, Exporter::mUseTimeTags); + + // Skin + CheckDlgButton(hWnd, IDC_CHK_SKIN, Exporter::mExportSkin); + CheckDlgButton(hWnd, IDC_CHK_SKINPART, Exporter::mMultiplePartitions); + SetDlgItemText(hWnd, IDC_ED_BONES_PART, FormatText("%d", Exporter::mBonesPerPartition)); + SetDlgItemText(hWnd, IDC_ED_BONES_VERTEX, FormatText("%d", Exporter::mBonesPerVertex)); + + CheckDlgButton(hWnd, IDC_CHK_ALLOW_ACCUM, Exporter::mAllowAccum); + + TSTR tmp; + tmp.printf("%.4f", Exporter::mWeldThresh); + SetDlgItemText(hWnd, IDC_ED_WELDTHRESH, tmp); + + ConvertStaticToHyperlink(hWnd, IDC_LBL_LINK); + ConvertStaticToHyperlink(hWnd, IDC_LBL_WIKI); + + imp->mDlgResult = IDCANCEL; + } + return TRUE; + + case WM_CLOSE: + EndDialog(hWnd, imp->mDlgResult); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + char tmp[MAX_PATH], *end; + bool close = false; + switch (LOWORD(wParam)) + { + case IDOK: + // Validity Check + GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH); + if (tmp[0] != 0) + { + int nifVersion = ParseVersionString(tmp); + if (!IsSupportedVersion(nifVersion)) + { + MessageBox(hWnd, FormatString("Version '%s' is not a supported version.", tmp).c_str(), "NifExport", MB_OK|MB_ICONSTOP); + return FALSE; + } } - CenterWindow(hWnd,GetParent(hWnd)); - CheckDlgButton(hWnd, IDC_CHK_STRIPS, Exporter::mTriStrips); - CheckDlgButton(hWnd, IDC_CHK_HIDDEN, Exporter::mExportHidden); - CheckDlgButton(hWnd, IDC_CHK_FURN, Exporter::mExportFurn); - CheckDlgButton(hWnd, IDC_CHK_LIGHTS, Exporter::mExportLights); - CheckDlgButton(hWnd, IDC_CHK_VCOLORS, Exporter::mVertexColors); - SetDlgItemText(hWnd, IDC_ED_TEXPREFIX, Exporter::mTexPrefix.c_str()); - CheckDlgButton(hWnd, IDC_CHK_COLL, Exporter::mExportCollision); - CheckDlgButton(hWnd, IDC_CHK_REMAP, Exporter::mRemapIndices); - - CheckDlgButton(hWnd, IDC_CHK_EXTRA, Exporter::mExportExtraNodes); - CheckDlgButton(hWnd, IDC_CHK_UPB, Exporter::mUserPropBuffer); - CheckDlgButton(hWnd, IDC_CHK_HIER, Exporter::mFlattenHierarchy); - CheckDlgButton(hWnd, IDC_CHK_REM_BONES, Exporter::mRemoveUnreferencedBones); - CheckDlgButton(hWnd, IDC_CHK_SORTNODES, Exporter::mSortNodesToEnd); - CheckDlgButton(hWnd, IDC_CHK_SKEL_ONLY, Exporter::mSkeletonOnly); - CheckDlgButton(hWnd, IDC_CHK_CAMERA, Exporter::mExportCameras); - CheckDlgButton(hWnd, IDC_CHK_BONE_COLL, Exporter::mGenerateBoneCollision); - - string selection = Exporter::mGameName; - string version = Exporter::mNifVersion; - string userVer = FormatString("%d", Exporter::mNifUserVersion); - for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr) - SendDlgItemMessage(hWnd, IDC_CB_GAME, CB_ADDSTRING, 0, LPARAM(itr->Name.c_str())); - SendDlgItemMessage(hWnd, IDC_CB_GAME, CB_SELECTSTRING, WPARAM(-1), LPARAM(selection.c_str())); - SendDlgItemMessage(hWnd, IDC_CB_VERSION, WM_SETTEXT, 0, LPARAM(version.c_str())); - SendDlgItemMessage(hWnd, IDC_CB_USER_VERSION, WM_SETTEXT, 0, LPARAM(userVer.c_str())); - CheckDlgButton(hWnd, IDC_CHK_AUTO_DETECT, Exporter::mAutoDetect); - - // Populate Type options - SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WO_ANIM, LPARAM("NIF w/o Anim")); - SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WO_KF, LPARAM("NIF with Anim")); - SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::SINGLE_KF_WITH_NIF, LPARAM("Single KF with NIF")); - SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::SINGLE_KF_WO_NIF, LPARAM("Single KF w/o NIF")); - SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::MULTI_KF_WITH_NIF, LPARAM("Multi KF with NIF")); - SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::MULTI_KF_WO_NIF, LPARAM("Multi KF w/o NIF")); - SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WITH_MGR, LPARAM("NIF w/ Manager")); - - SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_SETCURSEL, WPARAM(Exporter::mExportType), 0); - - CheckDlgButton(hWnd, IDC_CHK_TRANSFORMS2, Exporter::mExportTransforms); - SetDlgItemText(hWnd, IDC_ED_PRIORITY2, FormatText("%.1f", Exporter::mDefaultPriority)); - CheckDlgButton(hWnd, IDC_CHK_USE_TIME_TAGS, Exporter::mUseTimeTags); + Exporter::mTriStrips = IsDlgButtonChecked(hWnd, IDC_CHK_STRIPS); + Exporter::mExportHidden = IsDlgButtonChecked(hWnd, IDC_CHK_HIDDEN); + Exporter::mExportFurn = IsDlgButtonChecked(hWnd, IDC_CHK_FURN); + Exporter::mExportLights = IsDlgButtonChecked(hWnd, IDC_CHK_LIGHTS); + Exporter::mVertexColors = IsDlgButtonChecked(hWnd, IDC_CHK_VCOLORS); + Exporter::mExportCollision = IsDlgButtonChecked(hWnd, IDC_CHK_COLL); + Exporter::mRemapIndices = IsDlgButtonChecked(hWnd, IDC_CHK_REMAP); + + Exporter::mExportExtraNodes = IsDlgButtonChecked(hWnd, IDC_CHK_EXTRA); + Exporter::mUserPropBuffer = IsDlgButtonChecked(hWnd, IDC_CHK_UPB); + Exporter::mFlattenHierarchy = IsDlgButtonChecked(hWnd, IDC_CHK_HIER); + Exporter::mRemoveUnreferencedBones = IsDlgButtonChecked(hWnd, IDC_CHK_REM_BONES); + Exporter::mSortNodesToEnd = IsDlgButtonChecked(hWnd, IDC_CHK_SORTNODES); + Exporter::mSkeletonOnly = IsDlgButtonChecked(hWnd, IDC_CHK_SKEL_ONLY); + Exporter::mExportCameras = IsDlgButtonChecked(hWnd, IDC_CHK_CAMERA); + Exporter::mGenerateBoneCollision = IsDlgButtonChecked(hWnd, IDC_CHK_BONE_COLL); + + Exporter::mExportTransforms = IsDlgButtonChecked(hWnd, IDC_CHK_TRANSFORMS2); + //Exporter::mUseTimeTags = IsDlgButtonChecked(hWnd, IDC_CHK_USE_TIME_TAGS); + + Exporter::mExportType = Exporter::ExportType(SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_GETCURSEL, 0, 0)); + GetDlgItemText(hWnd, IDC_ED_PRIORITY2, tmp, MAX_PATH); + Exporter::mDefaultPriority = atof(tmp); + + GetDlgItemText(hWnd, IDC_ED_TEXPREFIX, tmp, MAX_PATH); + Exporter::mTexPrefix = tmp; + + GetDlgItemText(hWnd, IDC_ED_WELDTHRESH, tmp, MAX_PATH); + Exporter::mWeldThresh = atof(tmp); + + Exporter::mAllowAccum = IsDlgButtonChecked(hWnd, IDC_CHK_ALLOW_ACCUM); // Skin - CheckDlgButton(hWnd, IDC_CHK_SKIN, Exporter::mExportSkin); - CheckDlgButton(hWnd, IDC_CHK_SKINPART, Exporter::mMultiplePartitions); - SetDlgItemText(hWnd, IDC_ED_BONES_PART, FormatText("%d", Exporter::mBonesPerPartition)); - SetDlgItemText(hWnd, IDC_ED_BONES_VERTEX, FormatText("%d", Exporter::mBonesPerVertex)); - - CheckDlgButton(hWnd, IDC_CHK_ALLOW_ACCUM, Exporter::mAllowAccum); - - TSTR tmp; - tmp.printf("%.4f", Exporter::mWeldThresh); - SetDlgItemText(hWnd, IDC_ED_WELDTHRESH, tmp); - - imp->mDlgResult = IDCANCEL; - } - return TRUE; - - case WM_CLOSE: - EndDialog(hWnd, imp->mDlgResult); - return TRUE; - - case WM_COMMAND: - if (HIWORD(wParam) == BN_CLICKED) - { - char tmp[MAX_PATH], *end; - bool close = false; - switch (LOWORD(wParam)) - { - case IDOK: - // Validity Check - GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH); - if (tmp[0] != 0) - { - int nifVersion = GetVersion(tmp); - if (!IsVersionSupported(nifVersion)) - { - MessageBox(hWnd, FormatString("Version '%s' is not a supported version.", tmp).c_str(), "NifExport", MB_OK|MB_ICONSTOP); - return FALSE; - } - } - - Exporter::mTriStrips = IsDlgButtonChecked(hWnd, IDC_CHK_STRIPS); - Exporter::mExportHidden = IsDlgButtonChecked(hWnd, IDC_CHK_HIDDEN); - Exporter::mExportFurn = IsDlgButtonChecked(hWnd, IDC_CHK_FURN); - Exporter::mExportLights = IsDlgButtonChecked(hWnd, IDC_CHK_LIGHTS); - Exporter::mVertexColors = IsDlgButtonChecked(hWnd, IDC_CHK_VCOLORS); - Exporter::mExportCollision = IsDlgButtonChecked(hWnd, IDC_CHK_COLL); - Exporter::mRemapIndices = IsDlgButtonChecked(hWnd, IDC_CHK_REMAP); - - Exporter::mExportExtraNodes = IsDlgButtonChecked(hWnd, IDC_CHK_EXTRA); - Exporter::mUserPropBuffer = IsDlgButtonChecked(hWnd, IDC_CHK_UPB); - Exporter::mFlattenHierarchy = IsDlgButtonChecked(hWnd, IDC_CHK_HIER); - Exporter::mRemoveUnreferencedBones = IsDlgButtonChecked(hWnd, IDC_CHK_REM_BONES); - Exporter::mSortNodesToEnd = IsDlgButtonChecked(hWnd, IDC_CHK_SORTNODES); - Exporter::mSkeletonOnly = IsDlgButtonChecked(hWnd, IDC_CHK_SKEL_ONLY); - Exporter::mExportCameras = IsDlgButtonChecked(hWnd, IDC_CHK_CAMERA); - Exporter::mGenerateBoneCollision = IsDlgButtonChecked(hWnd, IDC_CHK_BONE_COLL); - - Exporter::mExportTransforms = IsDlgButtonChecked(hWnd, IDC_CHK_TRANSFORMS2); - Exporter::mUseTimeTags = IsDlgButtonChecked(hWnd, IDC_CHK_USE_TIME_TAGS); - - Exporter::mExportType = Exporter::ExportType(SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_GETCURSEL, 0, 0)); - GetDlgItemText(hWnd, IDC_ED_PRIORITY2, tmp, MAX_PATH); - Exporter::mDefaultPriority = atof(tmp); - - GetDlgItemText(hWnd, IDC_ED_TEXPREFIX, tmp, MAX_PATH); - Exporter::mTexPrefix = tmp; - - GetDlgItemText(hWnd, IDC_ED_WELDTHRESH, tmp, MAX_PATH); - Exporter::mWeldThresh = atof(tmp); - - Exporter::mAllowAccum = IsDlgButtonChecked(hWnd, IDC_CHK_ALLOW_ACCUM); - - // Skin - Exporter::mExportSkin = IsDlgButtonChecked(hWnd, IDC_CHK_SKIN); - Exporter::mMultiplePartitions = IsDlgButtonChecked(hWnd, IDC_CHK_SKINPART); - GetDlgItemText(hWnd, IDC_ED_BONES_PART, tmp, MAX_PATH); - Exporter::mBonesPerPartition = atoi(tmp); - GetDlgItemText(hWnd, IDC_ED_BONES_VERTEX, tmp, MAX_PATH); - Exporter::mBonesPerVertex = atoi(tmp); - - GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH); - if (AppSettings *appSettings = FindAppSetting(tmp)) - { - Exporter::mGameName = appSettings->Name; - GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH); - Exporter::mNifVersion = tmp; - GetDlgItemText(hWnd, IDC_CB_USER_VERSION, tmp, MAX_PATH); - Exporter::mNifUserVersion = strtol(tmp, &end, 0); - } - Exporter::mAutoDetect = IsDlgButtonChecked(hWnd, IDC_CHK_AUTO_DETECT); - - EndDialog(hWnd, imp->mDlgResult=IDOK); - close = true; - break; - - case IDCANCEL: - EndDialog(hWnd, imp->mDlgResult=IDCANCEL); - close = true; - break; - } - - if (close) - SendMessage(hWnd, WM_CLOSE, 0, 0); - } - else if (HIWORD(wParam) == STN_CLICKED) - { - if (LOWORD(wParam) == IDC_LBL_LINK) - { - ShellExecute(hWnd, "open", "http://www.niftools.org", - NULL, NULL, SW_SHOWDEFAULT); - } - } - else if (HIWORD(wParam) == CBN_SELCHANGE) + Exporter::mExportSkin = IsDlgButtonChecked(hWnd, IDC_CHK_SKIN); + Exporter::mMultiplePartitions = IsDlgButtonChecked(hWnd, IDC_CHK_SKINPART); + GetDlgItemText(hWnd, IDC_ED_BONES_PART, tmp, MAX_PATH); + Exporter::mBonesPerPartition = atoi(tmp); + GetDlgItemText(hWnd, IDC_ED_BONES_VERTEX, tmp, MAX_PATH); + Exporter::mBonesPerVertex = atoi(tmp); + + GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH); + if (AppSettings *appSettings = FindAppSetting(tmp)) + { + Exporter::mGameName = appSettings->Name; + GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH); + Exporter::mNifVersion = tmp; + GetDlgItemText(hWnd, IDC_CB_USER_VERSION, tmp, MAX_PATH); + Exporter::mNifUserVersion = strtol(tmp, &end, 0); + } + Exporter::mAutoDetect = IsDlgButtonChecked(hWnd, IDC_CHK_AUTO_DETECT); + + EndDialog(hWnd, imp->mDlgResult=IDOK); + close = true; + break; + + case IDCANCEL: + EndDialog(hWnd, imp->mDlgResult=IDCANCEL); + close = true; + break; + + case IDC_LBL_LINK: + ShellExecute(hWnd, "open", imp->webSite, NULL, NULL, SW_SHOWNORMAL); + break; + + case IDC_LBL_WIKI: + ShellExecute(hWnd, "open", imp->wikiSite, NULL, NULL, SW_SHOWNORMAL); + break; + } + + if (close) + SendMessage(hWnd, WM_CLOSE, 0, 0); + } + else if (HIWORD(wParam) == CBN_SELCHANGE) + { + if (LOWORD(wParam) == IDC_CB_GAME) { - if (LOWORD(wParam) == IDC_CB_GAME) + char tmp[MAX_PATH]; + GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH); + if (AppSettings *appSettings = FindAppSetting(tmp)) { - char tmp[MAX_PATH]; - GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH); - if (AppSettings *appSettings = FindAppSetting(tmp)) - { - string version = appSettings->NiVersion; - string userVer = FormatString("%d", appSettings->NiUserVersion); - SendDlgItemMessage(hWnd, IDC_CB_VERSION, WM_SETTEXT, 0, LPARAM(version.c_str())); - SendDlgItemMessage(hWnd, IDC_CB_USER_VERSION, WM_SETTEXT, 0, LPARAM(userVer.c_str())); - } + string version = appSettings->NiVersion; + string userVer = FormatString("%d", appSettings->NiUserVersion); + SendDlgItemMessage(hWnd, IDC_CB_VERSION, WM_SETTEXT, 0, LPARAM(version.c_str())); + SendDlgItemMessage(hWnd, IDC_CB_USER_VERSION, WM_SETTEXT, 0, LPARAM(userVer.c_str())); } } - break; - } + } + break; + } return FALSE; } @@ -279,6 +284,9 @@ NifExport::NifExport() iniFileName = iniName; shortDescription = GetIniValue<TSTR>("System", "ShortDescription", "Netimmerse/Gamebryo", iniFileName); fileVersion = GetFileVersion(NULL); + + webSite = GetIniValue<TSTR>("System", "Website", "http://www.niftools.org", iniFileName); + wikiSite = GetIniValue<TSTR>("System", "Wiki", "http://www.niftools.org/wiki/index.php/3ds_Max", iniFileName); } NifExport::~NifExport() @@ -346,194 +354,140 @@ BOOL NifExport::SupportsOptions(int ext, DWORD options) return TRUE; } +static DWORD WINAPI dummyProgress(LPVOID arg) { + return(0); +} int NifExport::DoExport(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options) { + TSTR title = FormatText("Exporting '%s'...", PathFindFileName(name)); + i->PushPrompt(title); + if (!suppressPrompts) + i->ProgressStart(title, TRUE, dummyProgress, NULL); try { - TCHAR path[MAX_PATH]; - GetFullPathName(name, MAX_PATH, path, NULL); - PathRenameExtension(path, ".nif"); + DoExportInternal(name, ei, i, suppressPrompts, options); + } + catch (Exporter::CancelExporterException&) + { + // Special user cancellation exception + } + catch (exception &e) + { + if (!suppressPrompts) + MessageBox(NULL, e.what(), "Export Error", MB_OK); + } + catch (...) + { + if (!suppressPrompts) + MessageBox(NULL, "Unknown error.", "Export Error", MB_OK); + } + i->PopPrompt(); + if (!suppressPrompts) + i->ProgressEnd(); + return true; +} + +int NifExport::DoExportInternal(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options) +{ + TCHAR path[MAX_PATH]; + GetFullPathName(name, MAX_PATH, path, NULL); + PathRenameExtension(path, ".nif"); + + Exporter::mSelectedOnly = (options&SCENE_EXPORT_SELECTED) != 0; - // read application settings - AppSettings::Initialize(i); + // read application settings + AppSettings::Initialize(i); - TCHAR iniName[MAX_PATH]; - LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR); - PathCombine(iniName, pluginDir, "MaxNifTools.ini"); - bool iniNameIsValid = (-1 != _taccess(iniName, 0)); - - // Set whether Config should use registry or not - Exporter::mUseRegistry = !iniNameIsValid || GetIniValue<bool>(NifExportSection, "UseRegistry", false, iniName); - // read config from registry - Exporter::readConfig(i); - // read config from root node - Exporter::readConfig(i->GetRootNode()); - - // locate the "default" app setting - AppSettings *appSettings = NULL; - if (iniNameIsValid) - { - string fname = path; - // Locate which application to use. If Auto, find first app where this file appears in the root path list - string curapp = GetIniValue<string>(NifExportSection, "CurrentApp", "AUTO", iniName); - if (0 == _tcsicmp(curapp.c_str(), "AUTO")) { - Exporter::mAutoDetect = true; - // Scan Root paths - for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){ - if ((*itr).IsFileInRootPaths(fname)) { - appSettings = &(*itr); - break; - } - } - } else { - Exporter::mAutoDetect = false; - appSettings = FindAppSetting(curapp); - } - } - if (appSettings == NULL && !TheAppSettings.empty()) - appSettings = &TheAppSettings.front(); + TCHAR iniName[MAX_PATH]; + LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR); + PathCombine(iniName, pluginDir, "MaxNifTools.ini"); + bool iniNameIsValid = (-1 != _taccess(iniName, 0)); + + // Set whether Config should use registry or not + Exporter::mUseRegistry = !iniNameIsValid || GetIniValue<bool>(NifExportSection, "UseRegistry", false, iniName); + // read config from registry + Exporter::readConfig(i); + // read config from root node + Exporter::readConfig(i->GetRootNode()); + + // locate the "default" app setting + string fname = path; + AppSettings *appSettings = Exporter::importAppSettings(fname); + + Exporter::mSuppressPrompts = suppressPrompts; + if(!suppressPrompts) + { + if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_NIF_PANEL), GetActiveWindow(), NifExportOptionsDlgProc, (LPARAM)this) != IDOK) + return true; - Exporter::mGameName = appSettings->Name; - Exporter::mNifVersion = appSettings->NiVersion; - Exporter::mNifUserVersion = appSettings->NiUserVersion; + // write config to registry + Exporter::writeConfig(i); + // write config to root node + Exporter::writeConfig(i->GetRootNode()); - if(!suppressPrompts) - { - if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_NIF_PANEL), GetActiveWindow(), NifExportOptionsDlgProc, (LPARAM)this) != IDOK) - return true; - - // write config to registry - Exporter::writeConfig(i); - // write config to root node - Exporter::writeConfig(i->GetRootNode()); - - // Update the current app version - appSettings = FindAppSetting(Exporter::mGameName); - if (appSettings == NULL && !TheAppSettings.empty()) - appSettings = &TheAppSettings.front(); - - if (Exporter::mAutoDetect){ - SetIniValue<string>(NifExportSection, "CurrentApp", "AUTO", iniName); - } else { - SetIniValue<string>(NifExportSection, "CurrentApp", appSettings->Name, iniName); - } - - appSettings->NiVersion = Exporter::mNifVersion; - appSettings->NiUserVersion = Exporter::mNifUserVersion; - appSettings->WriteSettings(i); - } - - int nifVersion = VER_20_0_0_5; - int nifUserVer = Exporter::mNifUserVersion; + appSettings = Exporter::exportAppSettings(); + appSettings->WriteSettings(i); + } - if (!Exporter::mNifVersion.empty()) - { - nifVersion = GetVersion(Exporter::mNifVersion); - if (!IsVersionSupported(nifVersion)) - throw exception(FormatString("Version '%s' is not a supported version.").c_str()); - } - Exporter::mNifVersionInt = nifVersion; + int nifVersion = VER_20_0_0_5; + int nifUserVer = Exporter::mNifUserVersion; + if (!Exporter::mNifVersion.empty()) + { + nifVersion = ParseVersionString(Exporter::mNifVersion); + if (!IsSupportedVersion(nifVersion)) + throw exception(FormatString("Version '%s' is not a supported version.").c_str()); + } + Exporter::mNifVersionInt = nifVersion; - Exporter::ExportType exportType = Exporter::mExportType; + Exporter::ExportType exportType = Exporter::mExportType; - // Hack so MW exports cleaner. Basically write tree without NiControllerManager - if ( nifVersion <= VER_10_0_1_0 - && (Exporter::mExportType != Exporter::NIF_WO_ANIM && Exporter::mExportType != Exporter::NIF_WITH_MGR) - ) - { - Exporter::mExportType = Exporter::NIF_WO_KF; - } - Niflib::NifInfo info(nifVersion, nifUserVer, nifUserVer); - info.creator = Exporter::mCreatorName; - info.exportInfo1 = "Niflib"; - info.exportInfo2 = FormatText("Niftools Max Plugins %s", fileVersion.data()); + // Hack so MW exports cleaner. Basically write tree without NiControllerManager + if ( nifVersion <= VER_10_0_1_0 + && (Exporter::mExportType != Exporter::NIF_WO_ANIM && Exporter::mExportType != Exporter::NIF_WITH_MGR) + ) + { + Exporter::mExportType = Exporter::NIF_WO_KF; + } + Niflib::NifInfo info(nifVersion, nifUserVer, nifUserVer); + info.endian = Niflib::NifInfo::INFO_LITTLE_ENDIAN; + info.creator = Exporter::mCreatorName; + info.exportInfo1 = FormatText("Niftools 3ds Max Plugins %s", fileVersion.data()); - Exporter::mSelectedOnly = (options&SCENE_EXPORT_SELECTED) != 0; - Exporter exp(i, appSettings); - - Ref<NiNode> root = DynamicCast<NiNode>(CreateBlock( "NiNode" )); - Exporter::Result result = exp.doExport(root, i->GetRootNode()); - - if (result!=Exporter::Ok) - throw exception("Unknown error."); + Exporter exp(i, appSettings); + + Ref<NiNode> root = new NiNode(); + Exporter::Result result = exp.doExport(root, i->GetRootNode()); + + if (result!=Exporter::Ok) + throw exception("Unknown error."); - if (exportType == Exporter::NIF_WO_ANIM || exportType == Exporter::NIF_WITH_MGR) - { - WriteNifTree(path, NiObjectRef(root), info); + if (exportType == Exporter::NIF_WO_ANIM || exportType == Exporter::NIF_WITH_MGR) + { + WriteNifTree(path, NiObjectRef(root), info); + } + else + { + Niflib::ExportOptions export_type = EXPORT_NIF; + switch (exportType) { + case Exporter::SINGLE_KF_WITH_NIF: export_type = EXPORT_NIF_KF; break; + case Exporter::SINGLE_KF_WO_NIF: export_type = EXPORT_KF; break; + case Exporter::MULTI_KF_WITH_NIF: export_type = EXPORT_NIF_KF_MULTI; break; + case Exporter::MULTI_KF_WO_NIF: export_type = EXPORT_KF_MULTI; break; } - else - { - Niflib::ExportOptions export_type = EXPORT_NIF; - switch (exportType) { - case Exporter::SINGLE_KF_WITH_NIF: export_type = EXPORT_NIF_KF; break; - case Exporter::SINGLE_KF_WO_NIF: export_type = EXPORT_KF; break; - case Exporter::MULTI_KF_WITH_NIF: export_type = EXPORT_NIF_KF_MULTI; break; - case Exporter::MULTI_KF_WO_NIF: export_type = EXPORT_KF_MULTI; break; - } - - Niflib::NifGame game = KF_MW; - if (nifVersion <= VER_4_0_0_2) { - game = KF_MW; - } else if (nifVersion <= VER_20_0_0_4) { - game = KF_DAOC; - } else { - game = KF_CIV4; - } - // - //if (nifVersion <= VER_10_0_1_0) { - - // // Now search and locate newer timeframe controllers and convert to keyframecontrollers - // list<NiObjectRef> mgrs = GetAllObjectsByType( root, NiControllerManager::TypeConst() ); - // for ( list<NiObjectRef>::iterator it = mgrs.begin(); it != mgrs.end(); ++it) { - // if (NiControllerManagerRef mgr = DynamicCast<NiControllerManager>(*it)) { - // NiObjectNETRef target = mgr->GetTarget(); - // target->RemoveController( StaticCast<NiTimeController>(mgr) ); - // vector<NiControllerSequenceRef> seqs = mgr->GetControllerSequences(); - // for (vector<NiControllerSequenceRef>::iterator itr = seqs.begin(); itr != seqs.end(); ++itr) { - // NiControllerSequenceRef seq = (*itr); - // MergeNifTrees(DynamicCast<NiNode>(target), seq, nifVersion, nifUserVer); - // } - // } - // } - //} - - WriteFileGroup(path, StaticCast<NiObject>(root), info, export_type, game); -/* - for (NodeList::iterator itr = mAnimationRoots.begin(); itr != mAnimationRoots.end(); ++itr){ - list<NiTimeControllerRef> ctlrs = (*itr)->GetControllers(); - if ( NiControllerManagerRef mgr = SelectFirstObjectOfType<NiControllerManager>( ctlrs ) ) { - (*itr)->RemoveController( mgr ); - - vector<NiControllerSequenceRef> seqs = mgr->GetControllerSequences(); - WriteNifTree() - } - } - - if (mExportType == SINGLE_KF_WITH_NIF || mExportType == MULTI_KF_WITH_NIF) - { - GetFullPathName(name, MAX_PATH, path, NULL); - PathRenameExtension(path, ".nif"); - WriteNifTree(path, NiObjectRef(root), nifVersion, nifUserVer); - } -*/ + Niflib::NifGame game = KF_MW; + if (nifVersion <= VER_4_0_0_2) { + game = KF_MW; + } else if (nifVersion <= VER_20_0_0_4) { + game = KF_DAOC; + } else { + game = KF_CIV4; } - } - catch (exception &e) - { - MessageBox(NULL, e.what(), "Export Error", MB_OK); - return true; - } - - catch (...) - { - MessageBox(NULL, "Unknown error.", "Export Error", MB_OK); - return true; - } - - return true; + WriteFileGroup(path, StaticCast<NiObject>(root), info, export_type, game); + } + return true; } diff --git a/NifExport/NifExport.rc b/NifExport/NifExport.rc index 949309e0b65749897b1daa471e9a457a2f168e90..ef925e830f8e1f8e5987acf8f799ec91504ae39d 100755 --- a/NifExport/NifExport.rc +++ b/NifExport/NifExport.rc @@ -104,7 +104,7 @@ BEGIN EDITTEXT IDC_CB_USER_VERSION,167,89,30,12,ES_AUTOHSCROLL DEFPUSHBUTTON "&Export",IDOK,5,109,34,14 PUSHBUTTON "&Cancel",IDCANCEL,45,109,33,14 - LTEXT "http://www.niftools.org",IDC_LBL_LINK,103,109,95,14,SS_NOTIFY | SS_CENTERIMAGE + CONTROL "http://www.niftools.org",IDC_LBL_LINK,"Button",BS_OWNERDRAW | WS_TABSTOP,103,109,95,14 END IDD_NIF_PANEL DIALOGEX 0, 0, 229, 291 @@ -138,28 +138,28 @@ BEGIN CONTROL "&Vertex Colors",IDC_CHK_VCOLORS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,181,67,10 GROUPBOX "Skin Modifier",IDC_STATIC,116,123,108,73 CONTROL "Export Skin Modifier",IDC_CHK_SKIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,119,134,93,10 - LTEXT "Bones Per Vertex:",IDC_LBL_BONES_VERTEX,120,174,63,8 - EDITTEXT IDC_ED_BONES_VERTEX,195,173,24,12,ES_AUTOHSCROLL + CONTROL "Enable Multiple Partitions",IDC_CHK_SKINPART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,119,147,93,10 LTEXT "Bones Per Partition:",IDC_LBL_BONES_PART,130,160,62,8 EDITTEXT IDC_ED_BONES_PART,195,158,24,12,ES_AUTOHSCROLL - CONTROL "Gen. Bone Collision",IDC_CHK_BONE_COLL,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,151,7,73,10 - CONTROL "&Remap Indices",IDC_CHK_REMAP,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,152,7,72,10 + LTEXT "Bones Per Vertex:",IDC_LBL_BONES_VERTEX,120,174,63,8 + EDITTEXT IDC_ED_BONES_VERTEX,195,173,24,12,ES_AUTOHSCROLL GROUPBOX "Animation",IDC_GRP_ANIMATION,4,196,108,68 COMBOBOX IDC_CBO_ANIM_TYPE,7,207,85,69,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL "Transforms",IDC_CHK_TRANSFORMS2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,223,67,10 - LTEXT "Priority:",IDC_LBL_PRIORITY2,9,249,23,8 - EDITTEXT IDC_ED_PRIORITY2,37,248,55,12,ES_AUTOHSCROLL + LTEXT "Priority:",IDC_LBL_PRIORITY2,9,236,23,8 + EDITTEXT IDC_ED_PRIORITY2,37,235,55,12,ES_AUTOHSCROLL GROUPBOX "Miscellaneous:",IDC_STATIC,115,196,109,68 CONTROL "Add User Prop Buffer",IDC_CHK_UPB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,209,82,11 CONTROL "Sort Nodes",IDC_CHK_SORTNODES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,222,82,10 + CONTROL "Add Accum Nodes",IDC_CHK_ALLOW_ACCUM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,235,82,10 LTEXT "Auto-&Weld",IDC_LBL_WELDTHRESH,125,249,47,8,NOT WS_VISIBLE | WS_DISABLED EDITTEXT IDC_ED_WELDTHRESH,179,247,43,12,ES_AUTOHSCROLL | NOT WS_VISIBLE | WS_DISABLED DEFPUSHBUTTON "&Export",IDOK,5,270,34,14 PUSHBUTTON "&Cancel",IDCANCEL,45,270,33,14 - LTEXT "http://www.niftools.org",IDC_LBL_LINK,148,270,76,14,SS_NOTIFY | SS_CENTERIMAGE - CONTROL "Enable Multiple Partitions",IDC_CHK_SKINPART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,119,147,93,10 - CONTROL "Use Time Tags",IDC_CHK_USE_TIME_TAGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,235,96,10 - CONTROL "Add Accum Nodes",IDC_CHK_ALLOW_ACCUM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,235,82,10 + CONTROL "Gen. Bone Collision",IDC_CHK_BONE_COLL,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,151,7,73,10 + CONTROL "&Remap Indices",IDC_CHK_REMAP,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,152,7,72,10 + LTEXT "www.niftools.org",IDC_LBL_LINK,137,270,60,14,SS_NOTIFY | SS_CENTERIMAGE + LTEXT "Wiki",IDC_LBL_WIKI,203,270,22,14,SS_NOTIFY | SS_CENTERIMAGE END diff --git a/NifExport/Strips.cpp b/NifExport/Strips.cpp index a69a666bd87683b735c34910a294edeed6b68467..019af369d72126e0754998f1a18d510a0b4a4785 100755 --- a/NifExport/Strips.cpp +++ b/NifExport/Strips.cpp @@ -149,7 +149,7 @@ void Exporter::strippify(FaceGroup &grp) NiTriStripsDataRef Exporter::makeTriStripsData(const TriStrips &strips) { - NiTriStripsDataRef stripData = DynamicCast<NiTriStripsData>(CreateBlock("NiTriStripsData")); + NiTriStripsDataRef stripData = new NiTriStripsData(); if (strips.size() > 0) { diff --git a/NifExport/Util.cpp b/NifExport/Util.cpp index 6dd15345265f84ad5105f6ee20980af7a677330f..446731c8a631a8b51eb41aaaecffe1204a0af73f 100755 --- a/NifExport/Util.cpp +++ b/NifExport/Util.cpp @@ -431,3 +431,42 @@ void Exporter::getChildNodes(INode *node, vector<NiNodeRef>& list) getChildNodes(child, list); } } + + +// Special case of a single branch being exported + +bool Exporter::exportPrn(NiNodeRef &obj, INode *node) { + // Export Prn Text strings for any parent bones if parent is root + if (mSupportPrnStrings) { + if (INode *parentNode = node->GetParentNode()){ + string parentName = parentNode->GetName(); + NiStringExtraDataRef strings = new NiStringExtraData(); + strings->SetName("Prn"); + strings->SetData(parentName); + obj->AddExtraData(DynamicCast<NiExtraData>(strings)); + return true; + } + } + return false; +} + + +int Exporter::countNodes(INode *node) +{ + int counter = 1; + for (int i=0; i<node->NumberOfChildren(); i++) { + counter += countNodes(node->GetChildNode(i)); + } + return counter; +} + +bool Exporter::isSkeletonRoot(INode *node) +{ + if (wildmatch("Bip??", node->GetName())) { + return true; + } else if ( node->GetParentNode() == mI->GetRootNode() ) { + return true; + } + + return false; +} \ No newline at end of file diff --git a/NifExport/resource.h b/NifExport/resource.h index 2a1de2d1cf475590364ec678c0d5fa569637a360..dc059a1b5e514ac6d6fba9bc297b89b93a3c4ff5 100755 --- a/NifExport/resource.h +++ b/NifExport/resource.h @@ -62,6 +62,7 @@ #define IDC_COLOR 13456 #define IDC_EDIT 13490 #define IDC_SPIN 13496 +#define IDC_LBL_WIKI 13497 // Next default values for new objects // diff --git a/NifImport/BaseImporter.h b/NifImport/BaseImporter.h index d6c3b7d287cedcac8d02690b432876299a845c18..7b8180af1782d1598019a86866395116854559e3 100644 --- a/NifImport/BaseImporter.h +++ b/NifImport/BaseImporter.h @@ -30,6 +30,8 @@ public: bool iniFileValid; string iniFileName; AppSettings *appSettings; + TSTR webSite; + TSTR wikiSite; Niflib::NiObjectRef root; @@ -60,6 +62,10 @@ public: *(FARPROC*)&Max7CreateNewBiped = GetProcAddress(hBiped, Max7CreateNewBipedName); } } + + webSite = GetIniValue<TSTR>("System", "Website", "http://www.niftools.org"); + wikiSite = GetIniValue<TSTR>("System", "Wiki", "http://www.niftools.org/wiki/index.php/3ds_Max"); + // Load ini settings iniFileValid = false; LoadIniSettings(); diff --git a/NifImport/ImportSkeleton.cpp b/NifImport/ImportSkeleton.cpp index 1d8cfc5b8a1372cd0837a187157bd81291883d25..dc81a62d7f39a11eeeb59aed12b6937aba414688 100644 --- a/NifImport/ImportSkeleton.cpp +++ b/NifImport/ImportSkeleton.cpp @@ -579,7 +579,26 @@ void NifImporter::ImportBones(NiNodeRef node, bool recurse) // Do all node manipulations here NiNodeRef parent = node->GetParent(); + string parentname = (parent ? parent->GetName() : ""); Matrix44 m4 = node->GetWorldTransform(); + + // Check for Prn strings and change parent if necessary + if (supportPrnStrings) { + list<NiStringExtraDataRef> strings = DynamicCast<NiStringExtraData>(node->GetExtraData()); + for (list<NiStringExtraDataRef>::iterator itr = strings.begin(); itr != strings.end(); ++itr){ + if (strmatch((*itr)->GetName(), "Prn")) { + parentname = (*itr)->GetData(); + if (INode *pn = gi->GetINodeByName(parentname.c_str())){ + // Apparently Heads tend to need to be rotated 90 degrees on import for + if (!rotate90Degrees.empty() && wildmatch(rotate90Degrees, parentname)) { + m4 *= TOMATRIX4(RotateYMatrix(TORAD(90))); + } + m4 *= TOMATRIX4(pn->GetObjTMAfterWSM(0, NULL)); + } + } + } + } + float len = node->GetLocalTranslation().Magnitude(); // Remove NonAccum nodes and merge into primary bone @@ -653,9 +672,8 @@ void NifImporter::ImportBones(NiNodeRef node, bool recurse) } if (bone) { - if (parent) + if (!parentname.empty()) { - string parentname = parent->GetName(); if (mergeNonAccum && wildmatch("* NonAccum", parentname)) { parentname = parentname.substr(0, parentname.length() - 9); } diff --git a/NifImport/KfDialog.cpp b/NifImport/KfDialog.cpp index f127a74998f50f7d45c05171d2b15b4626eca975..87bcd42a9b7cf28dd1d965fe38460346fb878b21 100644 --- a/NifImport/KfDialog.cpp +++ b/NifImport/KfDialog.cpp @@ -15,7 +15,7 @@ HISTORY: #include "KFMImporter.h" #include "resource.h" #include "shellapi.h" - +#include "Hyperlinks.h" using namespace Niflib; static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { @@ -43,6 +43,10 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP CheckDlgButton(hWnd, IDC_CHK_CLEARANIM, imp->clearAnimation); CheckDlgButton(hWnd, IDC_CHK_KEYNOTES, imp->addNoteTracks); CheckDlgButton(hWnd, IDC_CHK_TIMETAGS, imp->addTimeTags); + + ConvertStaticToHyperlink(hWnd, IDC_LBL_LINK); + ConvertStaticToHyperlink(hWnd, IDC_LBL_WIKI); + } return TRUE; @@ -68,13 +72,14 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP case IDCANCEL: EndDialog(hWnd, dlgRes=IDCANCEL); return TRUE; - } - } - else if (HIWORD(wParam) == STN_CLICKED) - { - if (LOWORD(wParam) == IDC_LBL_LINK) - { - ShellExecute(hWnd, "open", "http://www.niftools.org", NULL, NULL, SW_SHOWDEFAULT); + + case IDC_LBL_LINK: + ShellExecute(hWnd, "open", imp->webSite, NULL, NULL, SW_SHOWNORMAL); + break; + + case IDC_LBL_WIKI: + ShellExecute(hWnd, "open", imp->wikiSite, NULL, NULL, SW_SHOWNORMAL); + break; } } } diff --git a/NifImport/MaxNifImport.cpp b/NifImport/MaxNifImport.cpp index acd397005f135bb0bd5a350d78bd082cfcad9c5c..797ffe51e82e18813cb475e160d5391e4b3baddf 100644 --- a/NifImport/MaxNifImport.cpp +++ b/NifImport/MaxNifImport.cpp @@ -18,12 +18,14 @@ using namespace Niflib; #define MaxNifImport_CLASS_ID Class_ID(0x794ac1c1, 0x8b4c64c7) -string shortDescription; class MaxNifImport : public SceneImport { public: static HWND hParams; + TSTR shortDescription; + TSTR webSite; + TSTR wikiSite; int ExtCount(); // Number of extensions supported const TCHAR * Ext(int n); // Extension #n (i.e. "3DS") @@ -82,7 +84,9 @@ MaxNifImport::MaxNifImport() PathAppend(iniName, "MaxNifTools.ini"); } iniFileName = iniName; - shortDescription = GetIniValue<string>("System", "ShortDescription", "Netimmerse/Gamebryo", iniFileName.c_str()); + shortDescription = GetIniValue<TSTR>("System", "ShortDescription", "Netimmerse/Gamebryo", iniFileName.c_str()); + webSite = GetIniValue<TSTR>("System", "Website", "http://www.niftools.org", iniFileName.c_str()); + wikiSite = GetIniValue<TSTR>("System", "Wiki", "http://www.niftools.org/wiki/index.php/3ds_Max", iniFileName.c_str()); } MaxNifImport::~MaxNifImport() @@ -115,7 +119,7 @@ const TCHAR *MaxNifImport::LongDesc() const TCHAR *MaxNifImport::ShortDesc() { //TODO: Return short ASCII description (i.e. "Targa") - return shortDescription.c_str(); + return shortDescription; } const TCHAR *MaxNifImport::AuthorName() diff --git a/NifImport/MaxNifImport.h b/NifImport/MaxNifImport.h index 378671d247205beb6d8df45eab3c0b09909fc2c9..303e3d7e2b9ee2d5f52efa636a21c0c3959fe577 100644 --- a/NifImport/MaxNifImport.h +++ b/NifImport/MaxNifImport.h @@ -74,7 +74,7 @@ #include "NifImporter.h" extern TCHAR *GetString(int id); -extern string shortDescription; +extern TSTR shortDescription; extern HINSTANCE hInstance; #endif diff --git a/NifImport/MaxNifImport.rc b/NifImport/MaxNifImport.rc index 953153fc46d211c9be623ec59dd6c077c217b91f..3f134013ec6b8b0c3d930fe4e6fa33f5654117a7 100644 --- a/NifImport/MaxNifImport.rc +++ b/NifImport/MaxNifImport.rc @@ -69,8 +69,9 @@ BEGIN PUSHBUTTON "...",IDC_BTN_BROWSE,184,215,16,13 DEFPUSHBUTTON "&Import",IDOK,5,236,34,14 PUSHBUTTON "&Cancel",IDCANCEL,45,236,33,14 - LTEXT "http://www.niftools.org",IDC_LBL_LINK,122,236,78,14,SS_NOTIFY | SS_CENTERIMAGE CONTROL "Ignore Root Node",IDC_CHK_IGNORE_ROOT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,109,161,89,10 + LTEXT "www.niftools.org",IDC_LBL_LINK,125,239,53,8,SS_NOTIFY | SS_CENTERIMAGE + LTEXT "Wiki",IDC_LBL_WIKI,185,239,16,8,SS_NOTIFY | SS_CENTERIMAGE END IDD_KF_PANEL DIALOGEX 0, 0, 118, 99 @@ -83,9 +84,10 @@ BEGIN CONTROL "Clear Animation",IDC_CHK_CLEARANIM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,18,72,10 CONTROL "Add Key Notes",IDC_CHK_KEYNOTES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,31,72,10 CONTROL "Add Time Tags",IDC_CHK_TIMETAGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,44,72,10 - LTEXT "http://www.niftools.org",IDC_LBL_LINK,7,61,95,14,SS_NOTIFY | SS_CENTERIMAGE DEFPUSHBUTTON "&Import",IDOK,5,78,34,14 PUSHBUTTON "&Cancel",IDCANCEL,45,78,33,14 + LTEXT "www.niftools.org",IDC_LBL_LINK,7,62,54,8,SS_NOTIFY | SS_CENTERIMAGE + LTEXT "Wiki",IDC_LBL_WIKI,90,62,16,9,SS_NOTIFY | SS_CENTERIMAGE END @@ -165,3 +167,14 @@ END ///////////////////////////////////////////////////////////////////////////// + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/NifImport/NIFImport.cpp b/NifImport/NIFImport.cpp index 36477856fa53ea28598e1cc09af3fbc37fe8eced..a49134caec06d393830119f1854f86b8644b5189 100644 --- a/NifImport/NIFImport.cpp +++ b/NifImport/NIFImport.cpp @@ -119,10 +119,16 @@ void NifImporter::LoadIniSettings() if (0 == _tcsicmp(curapp.c_str(), "AUTO")) { autoDetect = true; // Scan Root paths + bool versionmatch = false; + int version = GetNifVersion(this->name); for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){ if ((*itr).IsFileInRootPaths(this->name)) { appSettings = &(*itr); break; + } else if ( !versionmatch && ParseVersionString((*itr).NiVersion) == version ) { + // Version matching is an ok fit but we want the other if possible. And we want the first match if possible. + appSettings = &(*itr); + versionmatch = true; } } } else { @@ -179,6 +185,8 @@ void NifImporter::LoadIniSettings() addNoteTracks = GetIniValue(AnimImportSection, "AddNoteTracks", true); addTimeTags = GetIniValue(AnimImportSection, "AddTimeTags", true); + rotate90Degrees = TokenizeString(GetIniValue<string>(NifImportSection, "Rotate90Degrees", "").c_str(), ";"); + // Collision bhkScaleFactor = GetIniValue<float>(CollisionSection, "bhkScaleFactor", 7.0f); ApplyAppSettings(); @@ -195,6 +203,9 @@ void NifImporter::ApplyAppSettings() dummyNodeMatches = appSettings->dummyNodeMatches; if (appSettings->applyOverallTransformToSkinAndBones != -1) applyOverallTransformToSkinAndBones = appSettings->applyOverallTransformToSkinAndBones ? true : false; + if (!appSettings->rotate90Degrees.empty()) + rotate90Degrees = appSettings->rotate90Degrees; + supportPrnStrings = appSettings->supportPrnStrings; } } diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h index dbc191cb1cdb4774a7d4db7e66e0fb491b246c49..97718109b35b872dc9a439b0b397b15864f07711 100644 --- a/NifImport/NIFImporter.h +++ b/NifImport/NIFImporter.h @@ -68,6 +68,8 @@ public: bool uncontrolledDummies; bool ignoreRootNode; bool autoDetect; + stringlist rotate90Degrees; + bool supportPrnStrings; // Animation related Settings bool replaceTCBRotationWithBezier; diff --git a/NifImport/NifDialog.cpp b/NifImport/NifDialog.cpp index 4940baaa28e60eb465e39a11ed18e80b8d151227..2016256362afff9cf537e743d42eb84271812bbf 100644 --- a/NifImport/NifDialog.cpp +++ b/NifImport/NifDialog.cpp @@ -14,7 +14,7 @@ HISTORY: #include "MaxNifImport.h" #include "resource.h" #include "shellapi.h" - +#include "Hyperlinks.h" using namespace Niflib; static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { @@ -78,6 +78,8 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP EnableWindow(GetDlgItem(hWnd, IDC_CHK_BIPED), FALSE); EnableWindow(GetDlgItem(hWnd, IDC_CHK_REM_BONES), FALSE); } + ConvertStaticToHyperlink(hWnd, IDC_LBL_LINK); + ConvertStaticToHyperlink(hWnd, IDC_LBL_WIKI); } return TRUE; @@ -137,7 +139,7 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP case IDC_BTN_BROWSE: { TCHAR filter[64], *pfilter=filter; - pfilter = _tcscpy(filter, shortDescription.c_str()); + pfilter = _tcscpy(filter, shortDescription); pfilter = _tcscat(pfilter, " (*.NIF)"); pfilter += strlen(pfilter); *pfilter++ = '\0'; @@ -164,13 +166,14 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP } } break; - } - } - else if (HIWORD(wParam) == STN_CLICKED) - { - if (LOWORD(wParam) == IDC_LBL_LINK) - { - ShellExecute(hWnd, "open", "http://www.niftools.org", NULL, NULL, SW_SHOWDEFAULT); + + case IDC_LBL_LINK: + ShellExecute(hWnd, "open", imp->webSite, NULL, NULL, SW_SHOWNORMAL); + break; + + case IDC_LBL_WIKI: + ShellExecute(hWnd, "open", imp->wikiSite, NULL, NULL, SW_SHOWNORMAL); + break; } } else if (HIWORD(wParam) == CBN_SELCHANGE) diff --git a/NifImport/resource.h b/NifImport/resource.h index 3462e80a54feb062d55c534a3311e0b2982f3c7e..8569df00dc6afcdb2861b0790e3244c23d7e2bba 100644 --- a/NifImport/resource.h +++ b/NifImport/resource.h @@ -40,6 +40,7 @@ #define IDC_CHK_KEYNOTES 10024 #define IDC_CHK_CAMERA 10025 #define IDC_CHK_TIMETAGS 10026 +#define IDC_LBL_WIKI 10027 // Next default values for new objects // diff --git a/NifPlugins_VC80.sln b/NifPlugins_VC80.sln index 28cfd323c99f730f00c03f16b6084a0676c3c72d..e9cb84f106f0152d8c439afe74fbfd1cdbae54bb 100644 --- a/NifPlugins_VC80.sln +++ b/NifPlugins_VC80.sln @@ -38,8 +38,8 @@ Global {19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 6|Win32.Build.0 = Release - PCH|Win32 {19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 7|Win32.ActiveCfg = Release - PCH|Win32 {19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 7|Win32.Build.0 = Release - PCH|Win32 - {19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 8|Win32.ActiveCfg = Release|Win32 - {19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 8|Win32.Build.0 = Release|Win32 + {19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 8|Win32.ActiveCfg = Release - PCH|Win32 + {19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 8|Win32.Build.0 = Release - PCH|Win32 {466F2D3E-2663-4583-A05C-128683677617}.Debug - Max 5|Win32.ActiveCfg = Debug - Max 8|Win32 {466F2D3E-2663-4583-A05C-128683677617}.Debug - Max 5|Win32.Build.0 = Debug - Max 8|Win32 {466F2D3E-2663-4583-A05C-128683677617}.Debug - Max 6|Win32.ActiveCfg = Debug - Max 6|Win32 @@ -50,8 +50,8 @@ Global {466F2D3E-2663-4583-A05C-128683677617}.Debug - Max 8|Win32.Build.0 = Debug - Max 8|Win32 {466F2D3E-2663-4583-A05C-128683677617}.Release - Max 6|Win32.ActiveCfg = Release - Max 6|Win32 {466F2D3E-2663-4583-A05C-128683677617}.Release - Max 6|Win32.Build.0 = Release - Max 6|Win32 - {466F2D3E-2663-4583-A05C-128683677617}.Release - Max 7|Win32.ActiveCfg = Release - Max 8|Win32 - {466F2D3E-2663-4583-A05C-128683677617}.Release - Max 7|Win32.Build.0 = Release - Max 8|Win32 + {466F2D3E-2663-4583-A05C-128683677617}.Release - Max 7|Win32.ActiveCfg = Release - Max 7|Win32 + {466F2D3E-2663-4583-A05C-128683677617}.Release - Max 7|Win32.Build.0 = Release - Max 7|Win32 {466F2D3E-2663-4583-A05C-128683677617}.Release - Max 8|Win32.ActiveCfg = Release - Max 8|Win32 {466F2D3E-2663-4583-A05C-128683677617}.Release - Max 8|Win32.Build.0 = Release - Max 8|Win32 EndGlobalSection