From a3dd518acedd515056c3906619877b3aaddadbed Mon Sep 17 00:00:00 2001 From: Lukas Cone Date: Tue, 17 Nov 2020 00:37:56 +0100 Subject: [PATCH] Release v1.11 --- .gitignore | 40 +- 3rd_party/HavokLib | 2 +- CHANGELOG | 52 ++ CMakeLists.txt | 30 + HavokMax.sln | 153 ---- HavokMax.vcxproj | 166 ---- HavokMax.vcxproj.filters | 58 -- HavokMax.vcxproj.user | 8 - README.md | 19 +- src/DllEntry.cpp | 160 ++-- src/HavokExport.cpp | 803 ++++++++-------- src/HavokImport.cpp | 842 +++++++++-------- src/HavokMax.cpp | 1872 ++++++++++++++++++++------------------ src/HavokMax.def | 10 - src/HavokMax.h | 208 ++--- src/HavokMax.rc | 2 +- 16 files changed, 2139 insertions(+), 2286 deletions(-) create mode 100644 CHANGELOG create mode 100644 CMakeLists.txt delete mode 100644 HavokMax.sln delete mode 100644 HavokMax.vcxproj delete mode 100644 HavokMax.vcxproj.filters delete mode 100644 HavokMax.vcxproj.user delete mode 100644 src/HavokMax.def diff --git a/.gitignore b/.gitignore index 410ee65..b6644c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,38 +1,6 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app *.bat -*.app -*.vs/* *.aps -*ObjDump* -*Bin/* \ No newline at end of file +*.7z +.vscode/ +build/ +[Bb]in/ diff --git a/3rd_party/HavokLib b/3rd_party/HavokLib index bc7f89a..481d6fe 160000 --- a/3rd_party/HavokLib +++ b/3rd_party/HavokLib @@ -1 +1 @@ -Subproject commit bc7f89adb145dba4b4b410a6b4fcb4dff88b9b33 +Subproject commit 481d6fe17030a38060692a16f72b2d34a7fcd0f0 diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..1372f4a --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,52 @@ +v1.11 +- More stable library version. +- Fixed incorrect old format additive animations. +- Added new format additive animations. +- Added root motion support. +- Moved to new config format (xml schema), presets will be migrated from old config format to new. +- External presets can be still loaded with both old version and new version format. Old format is not recommended to use anymore. +- Added support for 2020 and 2021 versions. +- Dropped 32bit support. +- Added support for exporting selected items (Export/Export selected) +v1.10 +- Fixed crash, when loading corrupted skeletons. ( #8 ) +- Added hka into file extensions. +v1.9 +- Added "disable scale" tracks option. (#3 ) +- Added keyboard shortcuts for dialogs. (#6 ) +- Changed first dialog item focus on process buttons. (#6 ) +v1.8 +- Fixed logging for non-unicode builds (2010 - 2012). +- Fixed crash caused by dialog for some versions (mainly 2012 as reported in #4 ). +- Fixed plugin registration bug reported in #5. +v1.7 +- Added support for loading version 2015 and 2017 skeletons. +- Added support for loading version 2015 interleaved animations. +- Added support for loading version 2015 - 2017 spline compressed animations. +- Fixed crash, when loading animations caused by 0 scale values. +v1.6 +- Fixed loading incorrect animation track for bone without annotation name and bind remaps. +- Fixed loading spline compressed animations when float tracks are present. +- Added Motion ID var into config file. +v1.5 +- Fixed not loading of newly added external presets for first time +- Added ability to choose animation index, in case of multiple animations in single file. +- Added ability to load config file with suppressPrompts enabled. +- Added ability to load bones by their hkaBone user property, in case of not being as annotation. +- Added ability to load additive animations. +v1.4 +- Added ability to load Spline Compressed animations. +- Fixed issue, when loading Delta Compressed animations, only first 2 frames were processed. +v1.3 +- Added support for loading 2016.2 toolset formats. +- Fixed crash, when loading incomplete 2016+ formats. +v1.2 +- Added ability to export/import animations. +- Added Environment generation for exported file. +- Added skeleton pose capture frame for export. +v1.1 +- Fixed loading version 2016 files under 32bit build. +- Fixed wrong class name links for XML exported files. +- Maxscript listener will now show whenever plugin tries to print any information and from now on, user will be always informed properly. User no longer needs to open listener manually. +v1 +- Initial release diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f9620b5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.3) + +project(HavokMax VERSION 1.11) + +set (HavokLibLibraryPath ../HavokLib) + +add_subdirectory(3rd_party/HavokLib ${HavokLibLibraryPath}) +include(${PRECORE_SOURCE_DIR}/cmake/3dsmax.cmake) + +build_target( + NAME HavokMax + TYPE SHARED + SOURCES + src/HavokExport.cpp + src/HavokImport.cpp + src/HavokMax.cpp + src/DllEntry.cpp + src/HavokMax.rc + ${MAX_EX_DIR}/win/About.rc + LINKS + gdiplus bmm core HavokLib flt mesh maxutil maxscrpt paramblk2 geom MaxSDKTarget + AUTHOR "Lukas Cone" + DESCR "Havok 3DS Max Unofficial Plugin" + START_YEAR 2016 + PROPERTIES + SUFFIX .dlu + ${MaxProperties} +) + +set_precore_sources(HavokMax directory_scanner) diff --git a/HavokMax.sln b/HavokMax.sln deleted file mode 100644 index b1531bb..0000000 --- a/HavokMax.sln +++ /dev/null @@ -1,153 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28621.142 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HavokMax", "HavokMax.vcxproj", "{6671F0BA-6FB8-4F35-8392-0676D05113A8}" - ProjectSection(ProjectDependencies) = postProject - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD} = {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HavokLib", "3rd_party\HavokLib\HavokLib.vcxproj", "{FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - 2010|x64 = 2010|x64 - 2010|x86 = 2010|x86 - 2011|x64 = 2011|x64 - 2011|x86 = 2011|x86 - 2012|x64 = 2012|x64 - 2012|x86 = 2012|x86 - 2013|x64 = 2013|x64 - 2013|x86 = 2013|x86 - 2014|x64 = 2014|x64 - 2014|x86 = 2014|x86 - 2015|x64 = 2015|x64 - 2015|x86 = 2015|x86 - 2016|x64 = 2016|x64 - 2016|x86 = 2016|x86 - 2017|x64 = 2017|x64 - 2017|x86 = 2017|x86 - 2018|x64 = 2018|x64 - 2018|x86 = 2018|x86 - 2019|x64 = 2019|x64 - 2019|x86 = 2019|x86 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release_Char|x64 = Release_Char|x64 - Release_Char|x86 = Release_Char|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2010|x64.ActiveCfg = 2010|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2010|x64.Build.0 = 2010|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2010|x86.ActiveCfg = 2010|Win32 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2010|x86.Build.0 = 2010|Win32 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2011|x64.ActiveCfg = 2011|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2011|x64.Build.0 = 2011|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2011|x86.ActiveCfg = 2011|Win32 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2011|x86.Build.0 = 2011|Win32 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2012|x64.ActiveCfg = 2012|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2012|x64.Build.0 = 2012|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2012|x86.ActiveCfg = 2012|Win32 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2012|x86.Build.0 = 2012|Win32 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2013|x64.ActiveCfg = 2013|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2013|x64.Build.0 = 2013|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2013|x86.ActiveCfg = 2013|Win32 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2013|x86.Build.0 = 2013|Win32 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2014|x64.ActiveCfg = 2014|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2014|x64.Build.0 = 2014|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2014|x86.ActiveCfg = 2014|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2014|x86.Build.0 = 2014|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2015|x64.ActiveCfg = 2015|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2015|x64.Build.0 = 2015|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2015|x86.ActiveCfg = 2015|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2015|x86.Build.0 = 2015|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2016|x64.ActiveCfg = 2016|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2016|x64.Build.0 = 2016|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2016|x86.ActiveCfg = 2016|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2016|x86.Build.0 = 2016|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2017|x64.ActiveCfg = 2017|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2017|x64.Build.0 = 2017|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2017|x86.ActiveCfg = 2017|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2017|x86.Build.0 = 2017|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2018|x64.ActiveCfg = 2018|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2018|x64.Build.0 = 2018|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2018|x86.ActiveCfg = 2018|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2018|x86.Build.0 = 2018|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2019|x64.ActiveCfg = 2019|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2019|x64.Build.0 = 2019|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2019|x86.ActiveCfg = 2019|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.2019|x86.Build.0 = 2019|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Debug|x64.ActiveCfg = 2017|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Debug|x64.Build.0 = 2017|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Debug|x86.ActiveCfg = 2017|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Debug|x86.Build.0 = 2017|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Release_Char|x64.ActiveCfg = Release_Char|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Release_Char|x64.Build.0 = Release_Char|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Release_Char|x86.ActiveCfg = Release_Char|Win32 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Release_Char|x86.Build.0 = Release_Char|Win32 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Release|x64.ActiveCfg = 2017|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Release|x64.Build.0 = 2017|x64 - {6671F0BA-6FB8-4F35-8392-0676D05113A8}.Release|x86.ActiveCfg = 2017|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2010|x64.ActiveCfg = Release_Char|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2010|x64.Build.0 = Release_Char|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2010|x86.ActiveCfg = Release_Char|Win32 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2010|x86.Build.0 = Release_Char|Win32 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2011|x64.ActiveCfg = Release_Char|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2011|x64.Build.0 = Release_Char|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2011|x86.ActiveCfg = Release_Char|Win32 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2011|x86.Build.0 = Release_Char|Win32 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2012|x64.ActiveCfg = Release_Char|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2012|x64.Build.0 = Release_Char|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2012|x86.ActiveCfg = Release_Char|Win32 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2012|x86.Build.0 = Release_Char|Win32 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2013|x64.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2013|x64.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2013|x86.ActiveCfg = Release|Win32 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2013|x86.Build.0 = Release|Win32 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2014|x64.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2014|x64.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2014|x86.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2014|x86.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2015|x64.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2015|x64.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2015|x86.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2015|x86.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2016|x64.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2016|x64.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2016|x86.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2016|x86.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2017|x64.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2017|x64.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2017|x86.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2017|x86.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2018|x64.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2018|x64.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2018|x86.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2018|x86.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2019|x64.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2019|x64.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2019|x86.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.2019|x86.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Debug|x64.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Debug|x64.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Debug|x86.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Debug|x86.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Release_Char|x64.ActiveCfg = Release_Char|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Release_Char|x64.Build.0 = Release_Char|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Release_Char|x86.ActiveCfg = Release_Char|Win32 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Release_Char|x86.Build.0 = Release_Char|Win32 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Release|x64.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Release|x64.Build.0 = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Release|x86.ActiveCfg = Release|x64 - {FFCA4C01-B4D8-46B2-8224-8D210C11B3CD}.Release|x86.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B7EA78DE-3D52-4FA9-B4DC-60EE12D72115} - EndGlobalSection -EndGlobal diff --git a/HavokMax.vcxproj b/HavokMax.vcxproj deleted file mode 100644 index 973aaba..0000000 --- a/HavokMax.vcxproj +++ /dev/null @@ -1,166 +0,0 @@ - - - - - 2010 - Win32 - - - 2010 - x64 - - - 2011 - Win32 - - - 2011 - x64 - - - 2012 - Win32 - - - 2012 - x64 - - - 2013 - Win32 - - - 2013 - x64 - - - 2014 - x64 - - - 2015 - x64 - - - 2016 - x64 - - - 2017 - x64 - - - 2019 - x64 - - - 2018 - x64 - - - Release_Char - Win32 - - - Release_Char - x64 - - - - - - - - - - - - - - - - - - - - - - - C:\Program Files\Autodesk\3ds Max $(Configuration) SDK\maxsdk - 2017|x64 - - - {6671F0BA-6FB8-4F35-8392-0676D05113A8} - HavokMax - DynamicLibrary - true - false - 10.0.17763.0 - - - /x64/lib - - - /lib - - - /lib/x64/Release - - - $(MaxSDK)$(MaxSDKLibsRPath) - - - - false - v140 - true - NotSet - Unicode - Bin\$(Platform)_$(Configuration)\ - ObjDump\$(Platform)_$(Configuration)\ - - - Release_Char - - - Release - - - - - - - - - - - - - Level3 - MaxSpeed - true - true - true - _USRDLL;WINVER=0x0601;_WIN32_WINNT=0x0601;_WIN32_WINDOWS=0x0601;_WIN32_IE=0x0800;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_SCL_SECURE_NO_DEPRECATE;ISOLATION_AWARE_ENABLED=1;MODULE_NAME=$(TargetFileName);BUILDVERSION=$(Configuration);%(PreprocessorDefinitions) - 3rd_party/HavokLib/include;$(MaxSDK)\include;3rd_party/HavokLib/3rd_party/PreCore;%(AdditionalIncludeDirectories) - /GR /w34996 /we4706 /we4390 /we4557 /we4546 /we4545 /we4295 /we4310 /we4130 /we4611 /we4213 /we4121 /we4715 /w34701 /w34265 /wd4244 /wd4018 /wd4819 - Fast - - - 3rd_party/HavokLib/lib/$(Platform)_$(MAXLibLinkNameSuffix);$(MaxSDKLibs);$(IntDir);%(AdditionalLibraryDirectories) - gdiplus.lib;HavokLib.lib;bmm.lib;core.lib;flt.lib;mesh.lib;maxutil.lib;maxscrpt.lib;paramblk2.lib;geom.lib;%(AdditionalDependencies) - true - true - Windows - src/HavokMax.def - $(IntDir)$(TargetName).lib - $(OutDir)$(TargetName).dlu - - - COPY /Y /V "$(OutDir)$(TargetName).dlu" /B "$(LocalDebuggerWorkingDirectory)Plugins\" /B - - - - - - \ No newline at end of file diff --git a/HavokMax.vcxproj.filters b/HavokMax.vcxproj.filters deleted file mode 100644 index ee20a3b..0000000 --- a/HavokMax.vcxproj.filters +++ /dev/null @@ -1,58 +0,0 @@ - - - - - {fbad0f2d-faaa-4b0b-865c-583fac8ddcb1} - cpp;c;cxx;rc;def;r;odl;idl;hpj;bat - - - {92f20462-242f-4a0c-883e-b3c33819b84b} - h;hpp;hxx;hm;inl - - - {ad6e765e-4300-41dc-b185-05b019286785} - ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Resource Files - - - Resource Files - - - - - Header Files - - - Header Files - - - - - Resource Files - - - Resource Files - - - Resource Files - - - \ No newline at end of file diff --git a/HavokMax.vcxproj.user b/HavokMax.vcxproj.user deleted file mode 100644 index b2c7e61..0000000 --- a/HavokMax.vcxproj.user +++ /dev/null @@ -1,8 +0,0 @@ - - - - C:\Program Files\Autodesk\3ds Max 2017\\3dsmax.exe - C:\Program Files\Autodesk\3ds Max 2017\ - WindowsLocalDebugger - - \ No newline at end of file diff --git a/README.md b/README.md index d0f26a3..8e8b46b 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,26 @@ # HavokMax + HavokMax is Havok importer/exporter for 3ds max.\ -Builded with VS2015.\ -Supported 3ds max versions: **2010 - 2019**\ +Buildable with VS2017.\ +Supported 3ds max versions: **2010 - 2021**\ Tested on 3ds max versions: **2017** ## Building -### Editing .vcxproj -All essential configurations are within **PropertyGroup Label="MAXConfigurations"** field. -- **MaxSDK**: changes path where is your MAX SDK installation. -If your MAX SDK installation is somewhere else than default path stated in this field, you can edit it here. -- **MaxDebugConfiguration**: changes 3ds max version and platform, so all necessary files are copied into plugin directory, this will enable post-build event. You must have set ***Working Directory*** under ***Debugging*** in ***Project Properties*** to location, where 3ds max is installed (where 3dsmax.exe is). + +Head to the [Building a 3ds max CMake projects](https://github.com/PredatorCZ/PreCore/wiki/Building-a-3ds-max-CMake-projects) wiki page. ## Installation + ### [Latest Release](https://github.com/PredatorCZ/HavokMax/releases/) + Move corresponding .dlu located in correct version folder into ***%3ds max installation directory%/plugins***. \ Versions must match!\ -Additionally plugin will require **Visual C++ Redistributable for Visual Studio 2015** to be installed in order to work. +Additionally plugin will require **Visual C++ Redistributable for Visual Studio 2017** to be installed in order to work. ## License + This plugin is available under GPL v3 license. (See LICENSE.md) This plugin uses following libraries: -* HavokLib, Copyright (c) 2016-2019 Lukas Cone +* HavokLib, Copyright (c) 2016-2020 Lukas Cone diff --git a/src/DllEntry.cpp b/src/DllEntry.cpp index 08d9889..f170f13 100644 --- a/src/DllEntry.cpp +++ b/src/DllEntry.cpp @@ -1,25 +1,27 @@ -/* Havok Tool for 3ds Max - Copyright(C) 2019 Lukas Cone - - This program is free software : you can redistribute it and / or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program.If not, see . - - Havok Tool uses HavokLib 2016-2019 Lukas Cone +/* Havok Tool for 3ds Max + Copyright(C) 2019-2020 Lukas Cone + + This program is free software : you can redistribute it and / or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program.If not, see . + + Havok Tool uses HavokLib 2016-2020 Lukas Cone */ #include "HavokMax.h" +#include "datas/master_printer.hpp" +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#define min(a, b) (((a) < (b)) ? (a) : (b)) #include -#include "datas/MasterPrinter.hpp" #if VERSION_3DSMAX_B == VERSION_3DSMAX_E(2010) #include @@ -27,103 +29,85 @@ #include #endif -extern ClassDesc2* GetHavokImportDesc(); +extern ClassDesc2 *GetHavokImportDesc(); extern ClassDesc2 *GetHavokExportDesc(); HINSTANCE hInstance; -int controlsInit = FALSE; Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; -// This function is called by Windows when the DLL is loaded. This +// This function is called by Windows when the DLL is loaded. This // function may also be called many times during time critical operations // like rendering. Therefore developers need to be careful what they // do inside this function. In the code below, note how after the DLL is // loaded the first time only a few statements are executed. -BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID /*lpvReserved*/) -{ - if( fdwReason == DLL_PROCESS_ATTACH ) - { - //MaxSDK::Util::UseLanguagePackLocale(); - // Hang on to this DLL's instance handle. - hInstance = hinstDLL; - DisableThreadLibraryCalls(hInstance); - // DO NOT do any initialization here. Use LibInitialize() instead. - } - return(TRUE); +BOOL WINAPI DllMain(HINSTANCE hinstDLL, ULONG fdwReason, + LPVOID /*lpvReserved*/) { + if (fdwReason == DLL_PROCESS_ATTACH) { + hInstance = hinstDLL; + DisableThreadLibraryCalls(hInstance); + // DO NOT do any initialization here. Use LibInitialize() instead. + } + return (TRUE); } +void PrintLog(const char *msg) { + if (!IsWindowVisible(the_listener_window) || IsIconic(the_listener_window)) + show_listener(); + + const auto cvted = ToTSTRING(msg); + + mprintf(cvted.data()); + mflush(); +} + +extern "C" { // This function returns a string that describes the DLL and where the user // could purchase the DLL if they don't have it. -__declspec( dllexport ) const TCHAR* LibDescription() -{ - return _T(""); +__declspec(dllexport) const TCHAR *LibDescription() { + return _T(HavokMax_DESC); } // This function returns the number of plug-in classes this DLL -//TODO: Must change this number when adding a new class -__declspec( dllexport ) int LibNumberClasses() -{ - return 2; -} +// TODO: Must change this number when adding a new class +__declspec(dllexport) int LibNumberClasses() { return 2; } // This function returns the number of plug-in classes this DLL -__declspec( dllexport ) ClassDesc* LibClassDesc(int i) -{ - switch(i) { - case 0: return GetHavokImportDesc(); - case 1: return GetHavokExportDesc(); - default: return 0; - } +__declspec(dllexport) ClassDesc *LibClassDesc(int i) { + switch (i) { + case 0: + return GetHavokImportDesc(); + case 1: + return GetHavokExportDesc(); + default: + return 0; + } } -// This function returns a pre-defined constant indicating the version of +// This function returns a pre-defined constant indicating the version of // the system under which it was compiled. It is used to allow the system // to catch obsolete DLLs. -__declspec( dllexport ) ULONG LibVersion() -{ - return VERSION_3DSMAX; +__declspec(dllexport) ULONG LibVersion() { return VERSION_3DSMAX; } + +// This function is called once, right after your plugin has been loaded by 3ds +// Max. Perform one-time plugin initialization in this method. Return TRUE if +// you deem your plugin successfully loaded, or FALSE otherwise. If the function +// returns FALSE, the system will NOT load the plugin, it will then call +// FreeLibrary on your DLL, and send you a message. +__declspec(dllexport) int LibInitialize(void) { + printer.AddPrinterFunction(PrintLog); + Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + BuildHavokResources(); + return TRUE; } -void PrintLog(TCHAR* msg) -{ - if (!IsWindowVisible(the_listener_window) || IsIconic(the_listener_window)) - show_listener(); - - mprintf(msg); - mflush(); -} - -// This function is called once, right after your plugin has been loaded by 3ds Max. -// Perform one-time plugin initialization in this method. -// Return TRUE if you deem your plugin successfully loaded, or FALSE otherwise. If -// the function returns FALSE, the system will NOT load the plugin, it will then call FreeLibrary -// on your DLL, and send you a message. -__declspec( dllexport ) int LibInitialize(void) -{ - printer.AddPrinterFunction(PrintLog); - Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); - BuildHavokResources(); - return TRUE; -} - -// This function is called once, just before the plugin is unloaded. +// This function is called once, just before the plugin is unloaded. // Perform one-time plugin un-initialization in this method." // The system doesn't pay attention to a return value. -__declspec( dllexport ) int LibShutdown(void) -{ - Gdiplus::GdiplusShutdown(gdiplusToken); - DestroyHavokResources(); - return TRUE; +__declspec(dllexport) int LibShutdown(void) { + Gdiplus::GdiplusShutdown(gdiplusToken); + DestroyHavokResources(); + return TRUE; } - -TCHAR *GetString(int id) -{ - static TCHAR buf[256]; - - if (hInstance) - return LoadString(hInstance, id, buf, _countof(buf)) ? buf : NULL; - return NULL; } - diff --git a/src/HavokExport.cpp b/src/HavokExport.cpp index 765c3eb..9811d04 100644 --- a/src/HavokExport.cpp +++ b/src/HavokExport.cpp @@ -1,433 +1,450 @@ -/* Havok Tool for 3ds Max - Copyright(C) 2019 Lukas Cone - - This program is free software : you can redistribute it and / or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program.If not, see . - - Havok Tool uses HavokLib 2016-2019 Lukas Cone +/* Havok Tool for 3ds Max + Copyright(C) 2019-2020 Lukas Cone + + This program is free software : you can redistribute it and / or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program.If not, see . + + Havok Tool uses HavokLib 2016-2020 Lukas Cone */ +#include "datas/fileinfo.hpp" +#include "datas/master_printer.hpp" +#include "havok_xml.hpp" #include "HavokMax.h" -#include "datas/esstring.h" -#include "datas/fileinfo.hpp" #include -#define HavokExport_CLASS_ID Class_ID(0x2b020aa4, 0x5c7f7d58) +#define HavokExport_CLASS_ID Class_ID(0x2b020aa4, 0x5c7f7d58) static const TCHAR _className[] = _T("HavokExport"); -class HavokExport : public SceneExport, public HavokMaxV2 -{ +class HavokExport : public SceneExport, public HavokMaxV2 { public: - //Constructor/Destructor - HavokExport(); - virtual ~HavokExport() {} - - int ExtCount(); // Number of extensions supported - const TCHAR * Ext(int n); // Extension #n (i.e. "3DS") - const TCHAR * LongDesc(); // Long ASCII description (i.e. "Autodesk 3D Studio File") - const TCHAR * ShortDesc(); // Short ASCII description (i.e. "3D Studio") - const TCHAR * AuthorName(); // ASCII Author name - const TCHAR * CopyrightMessage(); // ASCII Copyright message - const TCHAR * OtherMessage1(); // Other message #1 - const TCHAR * OtherMessage2(); // Other message #2 - unsigned int Version(); // Version number * 100 (i.e. v3.01 = 301) - 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); - - float inverseScale = 1.0f; - Matrix3 inverseCorMat = 1.0f; - - void ProcessAnimation(xmlSkeleton *skel, xmlAnimationBinding *binds, xmlInterleavedAnimation *anim); + // Constructor/Destructor + HavokExport(); + + int ExtCount() override; // Number of extensions supported + const TCHAR *Ext(int n) override; // Extension #n + const TCHAR *LongDesc() override; // Long ASCII description + const TCHAR *ShortDesc() override; // Short ASCII description + const TCHAR *AuthorName() override; // ASCII Author name + const TCHAR *CopyrightMessage() override; // ASCII Copyright message + const TCHAR *OtherMessage1() override; // Other message #1 + const TCHAR *OtherMessage2() override; // Other message #2 + unsigned int Version() override; // Version number * 100 (i.e. v3.01 = 301) + void ShowAbout(HWND hWnd) override; // Show DLL's "About..." box + BOOL SupportsOptions(int ext, DWORD options) override; + int DoExport(const TCHAR *name, ExpInterface *ei, Interface *i, + BOOL suppressPrompts = FALSE, DWORD options = 0) override; + + void DoExport(const std::string &fileName, bool selectedOnly, + bool suppressPrompts); + + float inverseScale = 1.0f; + Matrix3 inverseCorMat = true; + + void ProcessAnimation(xmlSkeleton *skel, xmlAnimationBinding *binds, + xmlInterleavedAnimation *anim); }; -class : public ClassDesc2 -{ +class : public ClassDesc2 { public: - int IsPublic() { return TRUE; } - void * Create(BOOL) { return new HavokExport(); } - const TCHAR * ClassName() { return _className; } - SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } - Class_ID ClassID() { return HavokExport_CLASS_ID; } - const TCHAR * Category() { return NULL; } - const TCHAR * InternalName() { return _className; } - HINSTANCE HInstance() { return hInstance; } -}HavokExportDesc; - -ClassDesc2* GetHavokExportDesc() { return &HavokExportDesc; } + int IsPublic() { return TRUE; } + void *Create(BOOL) { return new HavokExport(); } + const TCHAR *ClassName() { return _className; } + SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } + Class_ID ClassID() { return HavokExport_CLASS_ID; } + const TCHAR *Category() { return NULL; } + const TCHAR *InternalName() { return _className; } + HINSTANCE HInstance() { return hInstance; } +} HavokExportDesc; + +ClassDesc2 *GetHavokExportDesc() { return &HavokExportDesc; } //--- HavokImp ------------------------------------------------------- HavokExport::HavokExport() {} -int HavokExport::ExtCount() -{ - return static_cast(extensions.size()); -} +int HavokExport::ExtCount() { return static_cast(extensions.size()); } -const TCHAR *HavokExport::Ext(int n) -{ - return extensions[n].c_str(); +const TCHAR *HavokExport::Ext(int n) { + return std::next(extensions.begin(), n)->data(); } -const TCHAR *HavokExport::LongDesc() -{ - return _T("Havok Export"); -} +const TCHAR *HavokExport::LongDesc() { return _T("Havok Export"); } -const TCHAR *HavokExport::ShortDesc() -{ - return _T("Havok Export"); -} +const TCHAR *HavokExport::ShortDesc() { return _T("Havok Export"); } -const TCHAR *HavokExport::AuthorName() -{ - return _T("Lukas Cone"); -} +const TCHAR *HavokExport::AuthorName() { return _T("Lukas Cone"); } -const TCHAR *HavokExport::CopyrightMessage() -{ - return _T("Copyright (C) 2016-2019 Lukas Cone"); +const TCHAR *HavokExport::CopyrightMessage() { + return _T(HavokMax_COPYRIGHT "Lukas Cone"); } -const TCHAR *HavokExport::OtherMessage1() -{ - return _T(""); -} +const TCHAR *HavokExport::OtherMessage1() { return _T(""); } -const TCHAR *HavokExport::OtherMessage2() -{ - return _T(""); -} +const TCHAR *HavokExport::OtherMessage2() { return _T(""); } -unsigned int HavokExport::Version() -{ - return HAVOKMAX_VERSIONINT; -} +unsigned int HavokExport::Version() { return HAVOKMAX_VERSIONINT; } -void HavokExport::ShowAbout(HWND hWnd) -{ - ShowAboutDLG(hWnd); -} +void HavokExport::ShowAbout(HWND hWnd) { ShowAboutDLG(hWnd); } -BOOL HavokExport::SupportsOptions(int /*ext*/, DWORD /*options*/) -{ - return TRUE; +BOOL HavokExport::SupportsOptions(int /*ext*/, DWORD /*options*/) { + return TRUE; } -struct xmlBoneMAX : xmlBone -{ - void *ref; +struct xmlBoneMAX : xmlBone { + INode *ref; }; -thread_local static class : public ITreeEnumProc -{ - void AddBone(INode *nde) - { - TimeValue captureFrame = ex->IDC_EDIT_CAPTUREFRAME_index * GetTicksPerFrame(); - xmlBoneMAX *currentNode = new xmlBoneMAX(); - currentNode->ID = static_cast(skeleton->bones.size()); - currentNode->name = esString(nde->GetName()); - currentNode->ref = nde; - - INode *parentNode = nde->GetParentNode(); - Matrix3 nodeTM = nde->GetNodeTM(captureFrame); - - nodeTM.NoScale(); - - if (parentNode && !parentNode->IsRootNode()) - { - for (auto &b : skeleton->bones) - if (static_cast(b)->ref == parentNode) - { - currentNode->parent = b; - Matrix3 parentTM = static_cast(static_cast(b)->ref)->GetNodeTM(captureFrame); - parentTM.NoScale(); - parentTM.Invert(); - nodeTM *= parentTM; - break; - } - } - - if (!currentNode->parent) - nodeTM *= ex->inverseCorMat; - - currentNode->transform.position = reinterpret_cast(nodeTM.GetTrans() * ex->inverseScale); - currentNode->transform.position.W = 1.0f; - currentNode->transform.rotation = reinterpret_cast(static_cast(nodeTM).Conjugate()); - - skeleton->bones.push_back(currentNode); - } +thread_local static class : public ITreeEnumProc { + void AddBone(INode *nde) { + TimeValue captureFrame = ex->captureFrame * GetTicksPerFrame(); + xmlBoneMAX *currentNode = new xmlBoneMAX(); + currentNode->ID = static_cast(skeleton->bones.size()); + currentNode->name = std::to_string(nde->GetName()); + currentNode->ref = nde; + + INode *parentNode = nde->GetParentNode(); + Matrix3 nodeTM = nde->GetNodeTM(captureFrame); + + nodeTM.NoScale(); + + if (parentNode && !parentNode->IsRootNode()) { + for (auto &b : skeleton->bones) { + if (static_cast(b.get())->ref == parentNode) { + currentNode->parent = b.get(); + Matrix3 parentTM = + static_cast(b.get())->ref->GetNodeTM(captureFrame); + parentTM.NoScale(); + parentTM.Invert(); + nodeTM *= parentTM; + break; + } + } + } + + if (!currentNode->parent) { + nodeTM *= ex->inverseCorMat; + } + currentNode->transform.translation = + Vector4A16(reinterpret_cast(nodeTM.GetTrans())) * + ex->inverseScale; + currentNode->transform.translation.W = 1.0f; + currentNode->transform.rotation = + Vector4A16(reinterpret_cast(static_cast(nodeTM))) + .QConjugate(); + + skeleton->bones.emplace_back(currentNode); + } + public: - xmlSkeleton *skeleton; - HavokExport *ex; - int callback(INode * node) - { - AddBone(node); - - return TREE_CONTINUE; - } -}iSceneScanner; - - -bool CanSkipBone(INode *cNode) -{ - Control *ctr = cNode->GetTMController(); - Control *posCtrl = ctr->GetPositionController(); - - if (!posCtrl) - return false; - - IKeyControl *posCtr = GetKeyControlInterface(posCtrl); - - int numKeys = 0; - - if (posCtr) - numKeys |= posCtr->GetNumKeys(); - else - { - posCtr = GetKeyControlInterface(posCtrl->GetXController()); - numKeys |= posCtr->GetNumKeys(); - posCtr = GetKeyControlInterface(posCtrl->GetYController()); - numKeys |= posCtr->GetNumKeys(); - posCtr = GetKeyControlInterface(posCtrl->GetZController()); - numKeys |= posCtr->GetNumKeys(); - } - - Control *rotCtrl = ctr->GetRotationController(); - - if (!rotCtrl) - return false; - - IKeyControl *rotCtr = GetKeyControlInterface(rotCtrl); - - if (rotCtr) - numKeys |= rotCtr->GetNumKeys(); - else - { - rotCtr = GetKeyControlInterface(rotCtrl->GetXController()); - numKeys |= rotCtr->GetNumKeys(); - rotCtr = GetKeyControlInterface(rotCtrl->GetYController()); - numKeys |= rotCtr->GetNumKeys(); - rotCtr = GetKeyControlInterface(rotCtrl->GetZController()); - numKeys |= rotCtr->GetNumKeys(); - } - - Control *scaleCtrl = ctr->GetScaleController(); - - if (!scaleCtrl) - return false; - - IKeyControl *scaleCtr = GetKeyControlInterface(scaleCtrl); - - if (scaleCtr) - numKeys |= scaleCtr->GetNumKeys(); - else - { - scaleCtr = GetKeyControlInterface(scaleCtrl->GetXController()); - numKeys |= scaleCtr->GetNumKeys(); - scaleCtr = GetKeyControlInterface(scaleCtrl->GetYController()); - numKeys |= scaleCtr->GetNumKeys(); - scaleCtr = GetKeyControlInterface(scaleCtrl->GetZController()); - numKeys |= scaleCtr->GetNumKeys(); - } - - return numKeys < 2; + xmlSkeleton *skeleton; + HavokExport *ex; + bool selectedOnly; + + int callback(INode *node) { + if (!selectedOnly || (selectedOnly && node->Selected())) { + AddBone(node); + } + + return TREE_CONTINUE; + } +} iSceneScanner; + +bool CanSkipBone(INode *cNode) { + Control *ctr = cNode->GetTMController(); + Control *posCtrl = ctr->GetPositionController(); + + if (!posCtrl) { + return false; + } + + IKeyControl *posCtr = GetKeyControlInterface(posCtrl); + + int numKeys = 0; + + if (posCtr) { + numKeys |= posCtr->GetNumKeys(); + } else { + posCtr = GetKeyControlInterface(posCtrl->GetXController()); + numKeys |= posCtr->GetNumKeys(); + posCtr = GetKeyControlInterface(posCtrl->GetYController()); + numKeys |= posCtr->GetNumKeys(); + posCtr = GetKeyControlInterface(posCtrl->GetZController()); + numKeys |= posCtr->GetNumKeys(); + } + + Control *rotCtrl = ctr->GetRotationController(); + + if (!rotCtrl) { + return false; + } + + IKeyControl *rotCtr = GetKeyControlInterface(rotCtrl); + + if (rotCtr) { + numKeys |= rotCtr->GetNumKeys(); + } else { + rotCtr = GetKeyControlInterface(rotCtrl->GetXController()); + numKeys |= rotCtr->GetNumKeys(); + rotCtr = GetKeyControlInterface(rotCtrl->GetYController()); + numKeys |= rotCtr->GetNumKeys(); + rotCtr = GetKeyControlInterface(rotCtrl->GetZController()); + numKeys |= rotCtr->GetNumKeys(); + } + + Control *scaleCtrl = ctr->GetScaleController(); + + if (!scaleCtrl) { + return false; + } + + IKeyControl *scaleCtr = GetKeyControlInterface(scaleCtrl); + + if (scaleCtr) { + numKeys |= scaleCtr->GetNumKeys(); + } else { + scaleCtr = GetKeyControlInterface(scaleCtrl->GetXController()); + numKeys |= scaleCtr->GetNumKeys(); + scaleCtr = GetKeyControlInterface(scaleCtrl->GetYController()); + numKeys |= scaleCtr->GetNumKeys(); + scaleCtr = GetKeyControlInterface(scaleCtrl->GetZController()); + numKeys |= scaleCtr->GetNumKeys(); + } + + return numKeys < 2; } -void HavokExport::ProcessAnimation(xmlSkeleton *skel, xmlAnimationBinding *binds, xmlInterleavedAnimation *anim) -{ - anim->animType = HK_INTERLEAVED_ANIMATION; - - Interval captureIterval(IDC_EDIT_ANISTART_index * GetTicksPerFrame(), IDC_EDIT_ANIEND_index * GetTicksPerFrame()); - - anim->duration = TicksToSec(captureIterval.End() - captureIterval.Start()); - anim->annotations.reserve(skel->GetNumBones()); - - for (auto &b : skel->bones) - { - xmlBoneMAX *cBone = static_cast(b); - INode *cNode = static_cast(cBone->ref); - - if (flags[IDC_CH_ANIOPTIMIZE_checked] && flags[IDC_CH_ANIOPTIMIZE_enabled] && CanSkipBone(cNode)) - continue; - - binds->transformTrackToBoneIndices.push_back(cBone->ID); - - xmlInterleavedAnimation::Transform_Container *aCont = new xmlInterleavedAnimation::Transform_Container; - aCont->reserve(captureIterval.Duration() / GetTicksPerFrame()); - - Control *rotateControl = (Control *)CreateInstance(CTRL_ROTATION_CLASS_ID, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0)); - IKeyControl *kCon = GetKeyControlInterface(rotateControl); - - for (TimeValue t = captureIterval.Start(); t <= captureIterval.End(); t += GetTicksPerFrame()) - { - Matrix3 lMat = cNode->GetNodeTM(t); - Matrix3 pMat; - INode *parentNode = cNode->GetParentNode(); - - if (parentNode && !parentNode->IsRootNode()) - { - pMat = cNode->GetParentTM(t); - pMat.Invert(); - } - else - pMat = inverseCorMat; - - lMat *= pMat; - - hkQTransform cTransform; - - cTransform.scale = { lMat.GetRow(0).Length(), lMat.GetRow(1).Length(), lMat.GetRow(2).Length(), 0.0f }; - - if (parentNode && !parentNode->IsRootNode()) - { - pMat = cNode->GetParentTM(t); - pMat.NoScale(); - pMat.Invert(); - } - - lMat = cNode->GetNodeTM(t); - lMat.NoScale(); - lMat *= pMat; - - ILinRotKey cKey; - cKey.time = t; - cKey.val = Quat(lMat).Conjugate(); - kCon->AppendKey(&cKey); - - cTransform.position = reinterpret_cast(lMat.GetTrans()); - cTransform.position.W = 1.0f; - aCont->push_back(cTransform); - //TODO check scale - } - - Control *rotateControl2 = (Control *)CreateInstance(CTRL_ROTATION_CLASS_ID, Class_ID(LININTERP_ROTATION_CLASS_ID , 0)); - rotateControl2->Copy(rotateControl); - IKeyControl *kCon2 = GetKeyControlInterface(rotateControl2); - - for (int k = 0; k < kCon2->GetNumKeys(); k++) - { - ILinRotKey cKey; - kCon2->GetKey(k, &cKey); - - aCont->at(k).rotation = reinterpret_cast(cKey.val); - } - - if (aCont->size() == 1) - for (auto &t : *aCont) - aCont->push_back(aCont->at(0)); - - anim->transforms.push_back(aCont); - - xmlAnnotationTrack annot; - annot.name = cBone->name; - anim->annotations.push_back(annot); - } +void HavokExport::ProcessAnimation(xmlSkeleton *skel, + xmlAnimationBinding *binds, + xmlInterleavedAnimation *anim) { + anim->animType = HK_INTERLEAVED_ANIMATION; + + Interval captureIterval(animationStart * GetTicksPerFrame(), + animationEnd * GetTicksPerFrame()); + + anim->duration = TicksToSec(captureIterval.End() - captureIterval.Start()); + anim->annotations.reserve(skel->GetNumBones()); + + for (auto &b : skel->bones) { + xmlBoneMAX *cBone = static_cast(b.get()); + INode *cNode = cBone->ref; + + if (checked[Checked::CH_ANIOPTIMIZE] && visible[Visible::CH_ANIOPTIMIZE] && + CanSkipBone(cNode)) { + continue; + } + + binds->transformTrackToBoneIndices.push_back(cBone->ID); + + xmlInterleavedAnimation::transform_container *aCont = + new xmlInterleavedAnimation::transform_container; + aCont->reserve(captureIterval.Duration() / GetTicksPerFrame()); + + Control *rotateControl = (Control *)CreateInstance( + CTRL_ROTATION_CLASS_ID, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0)); + IKeyControl *kCon = GetKeyControlInterface(rotateControl); + + for (TimeValue t = captureIterval.Start(); t <= captureIterval.End(); + t += GetTicksPerFrame()) { + Matrix3 lMat = cNode->GetNodeTM(t); + Matrix3 pMat; + INode *parentNode = cBone->parent ? cNode->GetParentNode() : nullptr; + + if (parentNode && !parentNode->IsRootNode()) { + pMat = cNode->GetParentTM(t); + pMat.Invert(); + } else { + pMat = inverseCorMat; + } + + lMat *= pMat; + + hkQTransform cTransform; + + cTransform.scale = + Vector4A16(lMat.GetRow(0).Length(), lMat.GetRow(1).Length(), + lMat.GetRow(2).Length(), 0.0f); + + if (parentNode && !parentNode->IsRootNode()) { + pMat = cNode->GetParentTM(t); + pMat.NoScale(); + pMat.Invert(); + } + + lMat = cNode->GetNodeTM(t); + lMat.NoScale(); + lMat *= pMat; + + ILinRotKey cKey; + cKey.time = t; + cKey.val = Quat(lMat).Conjugate(); + kCon->AppendKey(&cKey); + + cTransform.translation = + Vector4A16(reinterpret_cast(lMat.GetTrans())); + cTransform.translation.W = 1.0f; + aCont->push_back(cTransform); + // TODO check scale + } + + Control *rotateControl2 = (Control *)CreateInstance( + CTRL_ROTATION_CLASS_ID, Class_ID(LININTERP_ROTATION_CLASS_ID, 0)); + rotateControl2->Copy(rotateControl); + IKeyControl *kCon2 = GetKeyControlInterface(rotateControl2); + + for (int k = 0; k < kCon2->GetNumKeys(); k++) { + ILinRotKey cKey; + kCon2->GetKey(k, &cKey); + + aCont->at(k).rotation = + Vector4A16(reinterpret_cast(cKey.val)); + } + + if (aCont->size() == 1) { + for (auto &t : *aCont) { + aCont->push_back(aCont->at(0)); + } + } + + anim->transforms.emplace_back(aCont); + + xmlAnnotationTrack annot; + annot.name = cBone->name; + anim->annotations.push_back(annot); + } } -void SaveEnvData(xmlEnvironment *env, const TCHAR *fileName) -{ - TFileInfo fleInfo(fileName); - MSTR *curMaxFile = &GetCOREInterface()->GetCurFilePath(); - - if (curMaxFile->length()) - { - TFileInfo maxInfo(curMaxFile->data()); - - xmlEnvironmentVariable asset; - asset.name = "asset"; - asset.value = esString(maxInfo.GetFileName()); - env->variables.push_back(asset); - - xmlEnvironmentVariable assetFolder; - assetFolder.name = "assetFolder"; - assetFolder.value = esString(maxInfo.GetPath()); - env->variables.push_back(assetFolder); - - xmlEnvironmentVariable assetPath; - assetPath.name = "assetPath"; - assetPath.value = esString(maxInfo.GetFullPath()); - env->variables.push_back(assetPath); - } - - xmlEnvironmentVariable out; - out.name = "out"; - out.value = esString(fleInfo.GetFileName()); - env->variables.push_back(out); - - xmlEnvironmentVariable outFolder; - outFolder.name = "outFolder"; - outFolder.value = esString(fleInfo.GetPath()); - env->variables.push_back(outFolder); - - xmlEnvironmentVariable outPath; - outPath.name = "outPath"; - outPath.value = esString(fleInfo.GetFullPath()); - env->variables.push_back(outPath); +void SaveEnvData(xmlEnvironment *env, const std::string &fileName) { + AFileInfo fleInfo(fileName); + MSTR *curMaxFile = &GetCOREInterface()->GetCurFilePath(); + + if (curMaxFile->length()) { + TFileInfo maxInfo(curMaxFile->data()); + + xmlEnvironmentVariable asset; + asset.name = "asset"; + asset.value = std::to_string(maxInfo.GetFilename()); + env->storage.push_back(asset); + + xmlEnvironmentVariable assetFolder; + assetFolder.name = "assetFolder"; + assetFolder.value = std::to_string(maxInfo.GetFolder()); + env->storage.push_back(assetFolder); + + xmlEnvironmentVariable assetPath; + assetPath.name = "assetPath"; + assetPath.value = std::to_string(maxInfo.GetFullPath()); + env->storage.push_back(assetPath); + } + + xmlEnvironmentVariable out; + out.name = "out"; + out.value = fleInfo.GetFilename(); + env->storage.push_back(out); + + xmlEnvironmentVariable outFolder; + outFolder.name = "outFolder"; + outFolder.value = fleInfo.GetFolder(); + env->storage.push_back(outFolder); + + xmlEnvironmentVariable outPath; + outPath.name = "outPath"; + outPath.value = fleInfo.GetFullPath(); + env->storage.push_back(outPath); } -int HavokExport::DoExport(const TCHAR *fileName, ExpInterface *ei, Interface * /*i*/, BOOL suppressPrompts, DWORD /*options*/) -{ - char *oldLocale = setlocale(LC_NUMERIC, NULL); - setlocale(LC_NUMERIC, "en-US"); - - if (!suppressPrompts) - if (!SpawnExportDialog()) - return TRUE; - - inverseScale = 1.0f / IDC_EDIT_SCALE_value; - inverseCorMat = corMat; - inverseCorMat.Invert(); - - xmlHavokFile hkFile = {}; - xmlRootLevelContainer *cont = hkFile.NewClass(); - xmlAnimationContainer *aniCont = hkFile.NewClass(); - xmlEnvironment *envData = hkFile.NewClass(); - const bool useSkeleton = (flags[IDC_CH_ANIMATION_checked] && flags[IDC_CH_ANISKELETON_checked]) || !flags[IDC_CH_ANIMATION_checked]; - xmlSkeleton *skel = useSkeleton ? hkFile.NewClass() : new xmlSkeleton; - - skel->name = "Reference"; - iSceneScanner.skeleton = skel; - iSceneScanner.ex = this; - ei->theScene->EnumTree(&iSceneScanner); - cont->AddVariant(aniCont); - cont->AddVariant(envData); - SaveEnvData(envData, fileName); - - if (useSkeleton) - aniCont->skeletons.push_back(skel); - - if (flags[IDC_CH_ANIMATION_checked]) - { - xmlAnimationBinding *binding = hkFile.NewClass(); - xmlInterleavedAnimation *anim = hkFile.NewClass(); - binding->animation = dynamic_cast(anim); - aniCont->animations.push_back(binding->animation); - aniCont->bindings.push_back(binding); - - if (flags[IDC_CH_ANISKELETON_checked]) - binding->skeletonName = skel->name; - - ProcessAnimation(skel, binding, anim); - } - - hkFile.ExportXML(fileName, static_cast(IDC_CB_TOOLSET_index + 1)); - - if (!useSkeleton) - delete skel; - - setlocale(LC_NUMERIC, oldLocale); +void SwapLocale(); + +void HavokExport::DoExport(const std::string &fileName, bool selectedOnly, + bool suppressPrompts) { + inverseScale = 1.0f / objectScale; + inverseCorMat = corMat; + inverseCorMat.Invert(); + + xmlHavokFile hkFile = {}; + xmlRootLevelContainer *cont = hkFile.NewClass(); + xmlAnimationContainer *aniCont = hkFile.NewClass(); + xmlEnvironment *envData = hkFile.NewClass(); + const bool useSkeleton = + (checked[Checked::CH_ANIMATION] && checked[Checked::CH_ANISKELETON]) || + !checked[Checked::CH_ANIMATION]; + xmlSkeleton *skel = + useSkeleton ? hkFile.NewClass() : new xmlSkeleton; + + skel->name = "Reference"; + iSceneScanner.skeleton = skel; + iSceneScanner.ex = this; + iSceneScanner.selectedOnly = selectedOnly; + GetCOREInterface7()->GetScene()->EnumTree(&iSceneScanner); + cont->AddVariant(aniCont); + cont->AddVariant(envData); + SaveEnvData(envData, fileName); + + if (useSkeleton) { + aniCont->skeletons.push_back(skel); + } + + if (checked[Checked::CH_ANIMATION]) { + xmlAnimationBinding *binding = hkFile.NewClass(); + xmlInterleavedAnimation *anim = hkFile.NewClass(); + binding->animation = anim; + aniCont->animations.push_back(binding->animation); + aniCont->bindings.push_back(binding); + + if (checked[Checked::CH_ANISKELETON]) { + binding->skeletonName = skel->name; + } + + ProcessAnimation(skel, binding, anim); + } + + hkFile.ToXML(std::to_string(fileName), toolset); + + if (!useSkeleton) { + delete skel; + } +} - return TRUE; +int HavokExport::DoExport(const TCHAR *fileName, ExpInterface *, Interface *, + BOOL suppressPrompts, DWORD options) { + SwapLocale(); + + if (!suppressPrompts) + if (!SpawnExportDialog()) + return TRUE; + + TSTRING filename_ = fileName; + + try { + DoExport(std::to_string(filename_), options & SCENE_EXPORT_SELECTED, + suppressPrompts); + } catch (const std::exception &e) { + if (suppressPrompts) { + printerror(e.what()); + } else { + auto msg = ToTSTRING(e.what()); + MessageBox(GetActiveWindow(), msg.data(), _T("Exception thrown!"), + MB_ICONERROR | MB_OK); + } + } catch (...) { + if (suppressPrompts) { + printerror("Unhandled exception has been thrown!"); + } else { + MessageBox(GetActiveWindow(), _T("Unhandled exception has been thrown!"), + _T("Exception thrown!"), MB_ICONERROR | MB_OK); + } + } + + SwapLocale(); + + return TRUE; } diff --git a/src/HavokImport.cpp b/src/HavokImport.cpp index feadbbe..9bbfe07 100644 --- a/src/HavokImport.cpp +++ b/src/HavokImport.cpp @@ -1,429 +1,501 @@ -/* Havok Tool for 3ds Max - Copyright(C) 2019 Lukas Cone - - This program is free software : you can redistribute it and / or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program.If not, see . - - Havok Tool uses HavokLib 2016-2019 Lukas Cone +/* Havok Tool for 3ds Max + Copyright(C) 2019-2020 Lukas Cone + + This program is free software : you can redistribute it and / or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program.If not, see . + + Havok Tool uses HavokLib 2016-2020 Lukas Cone */ +#include "datas/except.hpp" +#include "datas/master_printer.hpp" +#include "havok_api.hpp" #include "HavokMax.h" -#include "datas/esstring.h" -#include "datas/masterprinter.hpp" -#define HavokImport_CLASS_ID Class_ID(0xad115395, 0x924c02c0) +#define HavokImport_CLASS_ID Class_ID(0xad115395, 0x924c02c0) static const TCHAR _className[] = _T("HavokImport"); -class HavokImport : public SceneImport, HavokMaxV2 -{ +class HavokImport : public SceneImport, HavokMaxV2 { public: - //Constructor/Destructor - HavokImport(); - virtual ~HavokImport() {} - - virtual int ExtCount(); // Number of extensions supported - virtual const TCHAR * Ext(int n); // Extension #n (i.e. "3DS") - virtual const TCHAR * LongDesc(); // Long ASCII description (i.e. "Autodesk 3D Studio File") - virtual const TCHAR * ShortDesc(); // Short ASCII description (i.e. "3D Studio") - virtual const TCHAR * AuthorName(); // ASCII Author name - virtual const TCHAR * CopyrightMessage(); // ASCII Copyright message - virtual const TCHAR * OtherMessage1(); // Other message #1 - virtual const TCHAR * OtherMessage2(); // Other message #2 - virtual unsigned int Version(); // Version number * 100 (i.e. v3.01 = 301) - virtual void ShowAbout(HWND hWnd); // Show DLL's "About..." box - virtual int DoImport(const TCHAR *name,ImpInterface *i,Interface *gi, BOOL suppressPrompts=FALSE); // Import file - - void LoadSkeleton(const hkaSkeleton &skel); - void LoadAnimation(const hkaAnimation *ani, const hkaAnimationBinding *bind); + // Constructor/Destructor + HavokImport(); + + int ExtCount() override; // Number of extensions supported + const TCHAR *Ext(int n) override; // Extension #n + const TCHAR *LongDesc() override; // Long ASCII description + const TCHAR *ShortDesc() override; // Short ASCII description + const TCHAR *AuthorName() override; // ASCII Author name + const TCHAR *CopyrightMessage() override; // ASCII Copyright message + const TCHAR *OtherMessage1() override; // Other message #1 + const TCHAR *OtherMessage2() override; // Other message #2 + unsigned int Version() override; // Version number * 100 (i.e. v3.01 = 301) + void ShowAbout(HWND hWnd) override; // Show DLL's "About..." box + int DoImport(const TCHAR *name, ImpInterface *i, Interface *gi, + BOOL suppressPrompts = FALSE) override; + + void DoImport(const std::string &fileName, bool suppressPrompts); + + void LoadSkeleton(const hkaSkeleton *skel); + void LoadAnimation(const hkaAnimation *ani, const hkaAnimationBinding *bind); + void LoadRootMotion(const hkaAnimatedReferenceFrame *ani, + const std::vector ×); }; -class : public ClassDesc2 -{ +class : public ClassDesc2 { public: - virtual int IsPublic() { return TRUE; } - virtual void * Create(BOOL) { return new HavokImport(); } - virtual const TCHAR * ClassName() { return _className; } - virtual SClass_ID SuperClassID() { return SCENE_IMPORT_CLASS_ID; } - virtual Class_ID ClassID() { return HavokImport_CLASS_ID; } - virtual const TCHAR * Category() { return NULL; } - virtual const TCHAR * InternalName() { return _className; } - virtual HINSTANCE HInstance() { return hInstance; } -}HavokImportDesc; - -ClassDesc2* GetHavokImportDesc() { return &HavokImportDesc; } + virtual int IsPublic() { return TRUE; } + virtual void *Create(BOOL) { return new HavokImport(); } + virtual const TCHAR *ClassName() { return _className; } + virtual SClass_ID SuperClassID() { return SCENE_IMPORT_CLASS_ID; } + virtual Class_ID ClassID() { return HavokImport_CLASS_ID; } + virtual const TCHAR *Category() { return NULL; } + virtual const TCHAR *InternalName() { return _className; } + virtual HINSTANCE HInstance() { return hInstance; } +} HavokImportDesc; + +ClassDesc2 *GetHavokImportDesc() { return &HavokImportDesc; } //--- HavokImp ------------------------------------------------------- HavokImport::HavokImport() {} -int HavokImport::ExtCount() -{ - return static_cast(extensions.size()); -} +int HavokImport::ExtCount() { return static_cast(extensions.size()); } -const TCHAR *HavokImport::Ext(int n) -{ - return extensions[n].c_str(); +const TCHAR *HavokImport::Ext(int n) { + return std::next(extensions.begin(), n)->data(); } -const TCHAR *HavokImport::LongDesc() -{ - return _T("Havok Import"); -} +const TCHAR *HavokImport::LongDesc() { return _T("Havok Import"); } -const TCHAR *HavokImport::ShortDesc() -{ - return _T("Havok Import"); -} +const TCHAR *HavokImport::ShortDesc() { return _T("Havok Import"); } -const TCHAR *HavokImport::AuthorName() -{ - return _T("Lukas Cone"); -} +const TCHAR *HavokImport::AuthorName() { return _T("Lukas Cone"); } -const TCHAR *HavokImport::CopyrightMessage() -{ - return _T("Copyright (C) 2016-2019 Lukas Cone"); +const TCHAR *HavokImport::CopyrightMessage() { + return _T(HavokMax_COPYRIGHT "Lukas Cone"); } -const TCHAR *HavokImport::OtherMessage1() -{ - return _T(""); -} +const TCHAR *HavokImport::OtherMessage1() { return _T(""); } -const TCHAR *HavokImport::OtherMessage2() -{ - return _T(""); -} +const TCHAR *HavokImport::OtherMessage2() { return _T(""); } -unsigned int HavokImport::Version() -{ - return HAVOKMAX_VERSIONINT; -} +unsigned int HavokImport::Version() { return HAVOKMAX_VERSIONINT; } -void HavokImport::ShowAbout(HWND hWnd) -{ - ShowAboutDLG(hWnd); -} +void HavokImport::ShowAbout(HWND hWnd) { ShowAboutDLG(hWnd); } static const MSTR skelNameHint = _T("hkaSkeleton"); static const MSTR boneNameHint = _T("hkaBone"); -void HavokImport::LoadSkeleton(const hkaSkeleton &skel) -{ - std::vector nodes; - int currentBone = 0; - - for (auto b : skel.FullBones()) - { - TSTRING boneName = esString(b); - INode *node = GetCOREInterface()->GetINodeByName(boneName.c_str()); - - if (!node) - { - Object *obj = static_cast(CreateInstance(HELPER_CLASS_ID, Class_ID(DUMMY_CLASS_ID, 0))); - node = GetCOREInterface()->CreateObjectNode(obj); - node->ShowBone(2); - node->SetWireColor(0x80ff); - } - - if (b.transform) - { - Matrix3 nodeTM = {}; - nodeTM.SetRotate(reinterpret_cast(b.transform->rotation).Conjugate()); - nodeTM.SetTrans(reinterpret_cast(b.transform->position) * IDC_EDIT_SCALE_value); - - if (b.parentID > -1) - { - nodes[b.parentID]->AttachChild(node); - nodeTM *= nodes[b.parentID]->GetNodeTM(0); - } - else - nodeTM *= corMat; - - node->SetNodeTM(0, nodeTM); - } - - node->SetName(ToBoneName(boneName)); - nodes.push_back(node); - node->SetUserPropString(skelNameHint, static_cast(esString(skel)).c_str()); - node->SetUserPropString(boneNameHint, ToTSTRING(currentBone).c_str()); - - currentBone++; - } +void HavokImport::LoadSkeleton(const hkaSkeleton *skel) { + std::vector nodes; + int currentBone = 0; + + for (auto b : *skel->Bones()) { + TSTRING boneName = ToTSTRING(b->Name()); + INode *node = GetCOREInterface()->GetINodeByName(boneName.c_str()); + + if (!node) { + Object *obj = static_cast( + CreateInstance(HELPER_CLASS_ID, Class_ID(DUMMY_CLASS_ID, 0))); + node = GetCOREInterface()->CreateObjectNode(obj); + node->ShowBone(2); + node->SetWireColor(0x80ff); + } + + Matrix3 nodeTM = {}; + uni::RTSValue bneTM; + b->GetTM(bneTM); + nodeTM.SetRotate( + reinterpret_cast(bneTM.rotation.QConjugate())); + nodeTM.SetTrans( + reinterpret_cast(bneTM.translation * objectScale)); + const auto parentNode = b->Parent(); + if (parentNode) { + const auto bIndex = parentNode->Index(); + nodes[bIndex]->AttachChild(node); + nodeTM *= nodes[bIndex]->GetNodeTM(0); + } else + nodeTM *= corMat; + + node->SetNodeTM(0, nodeTM); + + node->SetName(ToBoneName(boneName)); + nodes.push_back(node); + node->SetUserPropString(skelNameHint, ToTSTRING(skel->Name()).data()); + node->SetUserPropString(boneNameHint, ToTSTRING(currentBone).c_str()); + + currentBone++; + } } -static class : public ITreeEnumProc -{ - const MSTR skelNameHint = _T("hkaSkeleton"); - const MSTR boneNameHint = _T("hkaBone"); - const MSTR skelNameExclude = _T("ragdoll"); +static class : public ITreeEnumProc { + const MSTR skelNameHint = _T("hkaSkeleton"); + const MSTR boneNameHint = _T("hkaBone"); + const MSTR skelNameExclude = _T("ragdoll"); + public: - std::vector bones; - - void RescanBones() - { - bones.clear(); - GetCOREInterface7()->GetScene()->EnumTree(this); - } - - INode *LookupNode(int ID) - { - for (auto &b : bones) - if (b->UserPropExists(boneNameHint)) - { - int _ID; - b->GetUserPropInt(boneNameHint, _ID); - - if (_ID == ID) - return b; - } - - return nullptr; - } - - int callback(INode *node) - { - Object *refobj = node->EvalWorldState(0).obj; - - if (node->UserPropExists(skelNameHint)) - { - MSTR skelName; - node->GetUserPropString(skelNameHint, skelName); - skelName.toLower(); - - const int skelNameExcludeSize = skelNameExclude.Length(); - bool found = true; - - for (int s = 0; s < skelNameExcludeSize; s++) - if (skelName[s] != skelNameExclude[s]) - { - found = false; - break; - } - - if (found) - return TREE_CONTINUE; - - bones.push_back(node); - } - - return TREE_CONTINUE; - } -}iBoneScanner; - -void HavokImport::LoadAnimation(const hkaAnimation *ani, const hkaAnimationBinding *bind) -{ - if (!ani) - { - printerror("[Havok] Unregistered animation format."); - return; - } - - if (!ani->IsDecoderSupported()) - { - printerror("[Havok] Usupported animation type: ", << ani->GetAnimationTypeName()); - return; - } - - iBoneScanner.RescanBones(); - - TimeValue numTicks = SecToTicks(ani->GetDuration()); - TimeValue ticksPerFrame = GetTicksPerFrame(); - TimeValue overlappingTicks = numTicks % ticksPerFrame; - - if (overlappingTicks > (ticksPerFrame / 2)) - numTicks += ticksPerFrame - overlappingTicks; - else - numTicks -= overlappingTicks; - - Interval aniRange(0, numTicks); - GetCOREInterface()->SetAnimRange(aniRange); - - std::vector frameTimes; - - for (TimeValue v = 0; v <= aniRange.End(); v += GetTicksPerFrame()) - frameTimes.push_back(TicksToSec(v)); - - ani->ComputeFrameRate(); - - const int numBones = ani->GetNumOfTransformTracks(); - const bool additiveAnimation = bind ? bind->GetBlendHint() != BlendHint::NORMAL : false; - - std::vector addTMs(numBones, { {1.0f, 0.0f, 0.0f} ,{0.0f, 1.0f, 0.0f} ,{0.0f, 0.0f, 1.0f} ,{0.0f, 0.0f, 0.0f} }); - - if (additiveAnimation) - for (int curBone = 0; curBone < numBones; curBone++) - { - INode *node = nullptr; - TSTRING boneName; - - if (ani->GetNumAnnotations()) - { - hkaAnnotationTrackPtr annot = ani->GetAnnotation(curBone); - boneName = esStringConvert(annot.get()->GetName()); - node = GetCOREInterface()->GetINodeByName(boneName.c_str()); - } - - if (!node && bind) - node = iBoneScanner.LookupNode(bind->GetTransformTrackToBoneIndex(curBone)); - - if (!node) - continue; - - Matrix3 inPacket = node->GetNodeTM(0); - - if (!node->GetParentNode()->IsRootNode()) - { - Matrix3 parentTM = node->GetParentTM(0); - parentTM.Invert(); - inPacket *= parentTM; - } - - addTMs[curBone] = inPacket; - } - - for (int curBone = 0; curBone < numBones; curBone++) - { - INode *node = nullptr; - TSTRING boneName; - - if (ani->GetNumAnnotations()) - { - hkaAnnotationTrackPtr annot = ani->GetAnnotation(curBone); - boneName = esStringConvert(annot.get()->GetName()); - node = GetCOREInterface()->GetINodeByName(boneName.c_str()); - } - - if (!node) - { - if (bind && bind->GetNumTransformTrackToBoneIndices()) - node = iBoneScanner.LookupNode(bind->GetTransformTrackToBoneIndex(curBone)); - else - node = iBoneScanner.LookupNode(curBone); - } - - if (!node) - { - if (boneName.length()) - { - printwarning("[Havok] Couldn't find bone: ", << boneName); - } - else if (bind && bind->GetNumTransformTrackToBoneIndices()) - { - printwarning("[Havok] Couldn't find hkaBone: ", << bind->GetTransformTrackToBoneIndex(curBone)); - } - else - { - printwarning("[Havok] Couldn't find hkaBone: ", << curBone); - } - - continue; - } - - Control *cnt = node->GetTMController(); - - if (cnt->GetPositionController()->ClassID() != Class_ID(LININTERP_POSITION_CLASS_ID, 0)) - cnt->SetPositionController((Control *)CreateInstance(CTRL_POSITION_CLASS_ID, Class_ID(LININTERP_POSITION_CLASS_ID, 0))); - - if (cnt->GetRotationController()->ClassID() != Class_ID(LININTERP_ROTATION_CLASS_ID, 0)) - cnt->SetRotationController((Control *)CreateInstance(CTRL_ROTATION_CLASS_ID, Class_ID(LININTERP_ROTATION_CLASS_ID, 0))); - - if (cnt->GetScaleController()->ClassID() != Class_ID(LININTERP_SCALE_CLASS_ID, 0)) - cnt->SetScaleController((Control *)CreateInstance(CTRL_SCALE_CLASS_ID, Class_ID(LININTERP_SCALE_CLASS_ID, 0))); - - SuspendAnimate(); - AnimateOn(); - - for (auto &t : frameTimes) - { - hkQTransform trans; - ani->GetTransform(curBone, t, trans); - - Matrix3 cMat; - Quat &rots = reinterpret_cast(trans.rotation); - cMat.SetRotate(rots.Conjugate()); - cMat.SetTrans(reinterpret_cast(trans.position) * IDC_EDIT_SCALE_value); - - if (!flags[IDC_CH_DISABLE_SCALE_checked]) - cMat.Scale(reinterpret_cast(trans.scale)); - - if (node->GetParentNode()->IsRootNode()) - cMat *= corMat; - else if (!flags[IDC_CH_DISABLE_SCALE_checked]) - { - Matrix3 pAbsMat = node->GetParentTM(SecToTicks(t)); - Point3 nScale = { pAbsMat.GetRow(0).Length(), pAbsMat.GetRow(1).Length(), pAbsMat.GetRow(2).Length() }; - - for (int s = 0; s < 3; s++) - if (!nScale[s]) - nScale[s] = FLT_EPSILON; - - Point3 fracPos = cMat.GetTrans() / nScale; - nScale = 1.f - nScale; - cMat.Translate(fracPos * nScale); - } - - if (additiveAnimation) - cMat *= addTMs[curBone]; - - SetXFormPacket packet(cMat); - - cnt->SetValue(SecToTicks(t), &packet); - } - - AnimateOff(); - - Control *rotControl = (Control *)CreateInstance(CTRL_ROTATION_CLASS_ID, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0)); - rotControl->Copy(cnt->GetRotationController()); - cnt->GetRotationController()->Copy(rotControl); - - } -} + std::vector bones; + + void RescanBones() { + bones.clear(); + GetCOREInterface7()->GetScene()->EnumTree(this); + } + + INode *LookupNode(int ID) { + for (auto &b : bones) + if (b->UserPropExists(boneNameHint)) { + int _ID; + b->GetUserPropInt(boneNameHint, _ID); + + if (_ID == ID) + return b; + } + + return nullptr; + } -int HavokImport::DoImport(const TCHAR*fileName, ImpInterface* /*importerInt*/, Interface* /*ip*/, BOOL suppressPrompts) -{ - char *oldLocale = setlocale(LC_NUMERIC, NULL); - setlocale(LC_NUMERIC, "en-US"); + int callback(INode *node) override { + Object *refobj = node->EvalWorldState(0).obj; - IhkPackFile *pFile = IhkPackFile::Create(fileName, true); + if (node->UserPropExists(skelNameHint)) { + MSTR skelName; + node->GetUserPropString(skelNameHint, skelName); + skelName.toLower(); - if (!pFile) - return FALSE; + const int skelNameExcludeSize = skelNameExclude.Length(); + bool found = true; - const hkRootLevelContainer *rootCont = pFile->GetRootLevelContainer(); + for (int s = 0; s < skelNameExcludeSize; s++) + if (skelName[s] != skelNameExclude[s]) { + found = false; + break; + } - for (auto &v : *rootCont) - if (v == hkaAnimationContainer::HASH) - { - const hkaAnimationContainer *_cont = v; + if (found) + return TREE_CONTINUE; - numAnimations = _cont->GetNumAnimations(); + bones.push_back(node); + } - if (!suppressPrompts) - if (!SpawnImportDialog()) - return TRUE; + return TREE_CONTINUE; + } +} iBoneScanner; - for (auto &s : _cont->Skeletons()) - LoadSkeleton(s); +void HavokImport::LoadRootMotion(const hkaAnimatedReferenceFrame *ani, + const std::vector ×) { + if (!ani) { + return; + } - if (numAnimations) - LoadAnimation(_cont->GetAnimation(IDC_EDIT_MOTIONID_index), _cont->GetBinding(IDC_EDIT_MOTIONID_index)); - } - - delete pFile; + std::vector rootNodes; + std::copy_if(iBoneScanner.bones.begin(), iBoneScanner.bones.end(), + std::back_inserter(rootNodes), [](INode *item) { + auto pNode = item->GetParentNode(); - setlocale(LC_NUMERIC, oldLocale); + if (!pNode || pNode->IsRootNode()) { + return true; + } + + return false; + }); + + for (auto r : rootNodes) { + std::vector cMats; + cMats.reserve(times.size()); + + for (auto t : times) { + cMats.emplace_back(r->GetNodeTM(SecToTicks(t))); + } + + size_t cTime = 0; + Control *cnt = r->GetTMController(); + AnimateOn(); + + for (auto t : times) { + hkQTransform trans; + ani->GetValue(trans, t); + + Matrix3 cMat(true); + Quat &cRotation = reinterpret_cast(trans.rotation.QConjugate()); + auto cTrans = reinterpret_cast(trans.translation * objectScale); + cMat.SetRotate(cRotation); + cMat.SetTrans(cTrans * corMat); + //cMat *= corMat; + + SetXFormPacket packet(cMats[cTime++] * cMat); + + cnt->SetValue(SecToTicks(t), &packet); + } + + AnimateOff(); + } +} + +void HavokImport::LoadAnimation(const hkaAnimation *ani, + const hkaAnimationBinding *bind) { + if (!ani) { + printerror("[Havok] Unregistered animation format."); + return; + } + + /*if (!ani->IsDecoderSupported()) { + printerror("[Havok] Usupported animation type: ", + << ani->GetAnimationTypeName()); + return; + }*/ + + iBoneScanner.RescanBones(); + + TimeValue numTicks = SecToTicks(ani->Duration()); + TimeValue ticksPerFrame = GetTicksPerFrame(); + TimeValue overlappingTicks = numTicks % ticksPerFrame; + + if (overlappingTicks > (ticksPerFrame / 2)) { + numTicks += ticksPerFrame - overlappingTicks; + } else { + numTicks -= overlappingTicks; + } + Interval aniRange(0, numTicks); + GetCOREInterface()->SetAnimRange(aniRange); + + std::vector frameTimes; + + for (TimeValue v = 0; v <= aniRange.End(); v += GetTicksPerFrame()) { + frameTimes.push_back(TicksToSec(v)); + } + + const auto numBones = ani->GetNumOfTransformTracks(); + const BlendHint blendType = bind ? bind->GetBlendHint() : BlendHint::NORMAL; + + std::vector addTMs(numBones, true); + + if (blendType != BlendHint::NORMAL) { + for (int curBone = 0; curBone < numBones; curBone++) { + INode *node = nullptr; + TSTRING boneName; + + if (ani->GetNumAnnotations()) { + hkaAnnotationTrackPtr annot = ani->GetAnnotation(curBone); + boneName = ToTSTRING(annot.get()->GetName()); + node = GetCOREInterface()->GetINodeByName(boneName.c_str()); + } + + if (!node && bind) { + node = iBoneScanner.LookupNode( + bind->GetTransformTrackToBoneIndex(curBone)); + } + + if (!node) { + continue; + } + + Matrix3 inPacket = node->GetNodeTM(0); + + if (!node->GetParentNode()->IsRootNode()) { + Matrix3 parentTM = node->GetParentTM(0); + parentTM.Invert(); + inPacket *= parentTM; + } + + addTMs[curBone] = inPacket; + } + } + + const auto tracks = ani->Tracks(); + + for (int curBone = 0; curBone < numBones; curBone++) { + INode *node = nullptr; + es::string_view bneNameRaw; + + if (ani->GetNumAnnotations()) { + hkaAnnotationTrackPtr annot = ani->GetAnnotation(curBone); + bneNameRaw = annot.get()->GetName(); + TSTRING boneName = ToTSTRING(bneNameRaw); + node = GetCOREInterface()->GetINodeByName(boneName.c_str()); + } + + if (!node) { + if (bind && bind->GetNumTransformTrackToBoneIndices()) { + node = iBoneScanner.LookupNode( + bind->GetTransformTrackToBoneIndex(curBone)); + } else { + node = iBoneScanner.LookupNode(curBone); + } + } + + if (!node) { + if (bneNameRaw.length()) { + printwarning("[Havok] Couldn't find bone: " << bneNameRaw); + } else if (bind && bind->GetNumTransformTrackToBoneIndices()) { + printwarning("[Havok] Couldn't find hkaBone: " + << bind->GetTransformTrackToBoneIndex(curBone)); + } else { + printwarning("[Havok] Couldn't find hkaBone: " << curBone); + } + + continue; + } + + Control *cnt = node->GetTMController(); + + if (cnt->GetPositionController()->ClassID() != + Class_ID(LININTERP_POSITION_CLASS_ID, 0)) { + cnt->SetPositionController((Control *)CreateInstance( + CTRL_POSITION_CLASS_ID, Class_ID(LININTERP_POSITION_CLASS_ID, 0))); + } + + if (cnt->GetRotationController()->ClassID() != + Class_ID(LININTERP_ROTATION_CLASS_ID, 0)) { + cnt->SetRotationController((Control *)CreateInstance( + CTRL_ROTATION_CLASS_ID, Class_ID(LININTERP_ROTATION_CLASS_ID, 0))); + } + + if (cnt->GetScaleController()->ClassID() != + Class_ID(LININTERP_SCALE_CLASS_ID, 0)) { + cnt->SetScaleController((Control *)CreateInstance( + CTRL_SCALE_CLASS_ID, Class_ID(LININTERP_SCALE_CLASS_ID, 0))); + } + + SuspendAnimate(); + AnimateOn(); + + const auto track = tracks->At(curBone); + + for (auto &t : frameTimes) { + hkQTransform trans; + track->GetValue(trans, t); + + Matrix3 cMat; + Quat &cRotation = reinterpret_cast(trans.rotation.QConjugate()); + auto cTrans = reinterpret_cast(trans.translation * objectScale); + + if (blendType == BlendHint::ADDITIVE_DEPRECATED) { + cMat.SetRotate(Quat(addTMs[curBone]) + cRotation); + cMat.SetTrans(cTrans + addTMs[curBone].GetTrans()); + } else if (blendType == BlendHint::ADDITIVE) { + cMat.SetRotate(cRotation + Quat(addTMs[curBone])); + cMat.SetTrans(cTrans + addTMs[curBone].GetTrans()); + } else { + cMat.SetRotate(cRotation); + cMat.SetTrans(cTrans); + } + + if (!checked[Checked::CH_DISABLE_SCALE]) { + cMat.Scale(reinterpret_cast(trans.scale)); + } + + if (node->GetParentNode()->IsRootNode()) { + cMat *= corMat; + } else if (!checked[Checked::CH_DISABLE_SCALE]) { + Matrix3 pAbsMat = node->GetParentTM(SecToTicks(t)); + Point3 nScale = {pAbsMat.GetRow(0).Length(), pAbsMat.GetRow(1).Length(), + pAbsMat.GetRow(2).Length()}; + + for (int s = 0; s < 3; s++) { + if (!nScale[s]) { + nScale[s] = FLT_EPSILON; + } + } + Point3 fracPos = cMat.GetTrans() / nScale; + nScale = 1.f - nScale; + cMat.Translate(fracPos * nScale); + } + + SetXFormPacket packet(cMat); + + cnt->SetValue(SecToTicks(t), &packet); + } + + AnimateOff(); + + Control *rotControl = (Control *)CreateInstance( + CTRL_ROTATION_CLASS_ID, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0)); + rotControl->Copy(cnt->GetRotationController()); + cnt->GetRotationController()->Copy(rotControl); + } + + LoadRootMotion(ani->GetExtractedMotion(), frameTimes); +} + +void HavokImport::DoImport(const std::string &fileName, bool suppressPrompts) { + auto pFile = IhkPackFile::Create(std::to_string(fileName)); + const hkRootLevelContainer *rootCont = pFile->GetRootLevelContainer(); + + for (auto &v : *rootCont) { + if (v == hkaAnimationContainer::GetHash()) { + const hkaAnimationContainer *aniCont = v; + + numAnimations = aniCont->GetNumAnimations(); + + if (!suppressPrompts) { + if (!SpawnImportDialog()) { + return; + } + } + + for (auto s : aniCont->Skeletons()) { + LoadSkeleton(s); + } + + if (numAnimations) { + LoadAnimation(aniCont->GetAnimation(motionIndex), + aniCont->GetNumBindings() + ? aniCont->GetBinding(motionIndex) + : nullptr); + } + } + } +} + +void SwapLocale() { + static std::string oldLocale; + + if (!oldLocale.empty()) { + setlocale(LC_NUMERIC, oldLocale.data()); + oldLocale.clear(); + } else { + oldLocale = setlocale(LC_NUMERIC, nullptr); + setlocale(LC_NUMERIC, "en-US"); + } +} - return TRUE; +int HavokImport::DoImport(const TCHAR *fileName, ImpInterface * /*importerInt*/, + Interface * /*ip*/, BOOL suppressPrompts) { + SwapLocale(); + TSTRING filename_ = fileName; + + try { + DoImport(std::to_string(filename_), suppressPrompts); + } catch (const es::InvalidHeaderError &) { + SwapLocale(); + return FALSE; + } catch (const std::exception &e) { + if (suppressPrompts) { + printerror(e.what()); + } else { + auto msg = ToTSTRING(e.what()); + MessageBox(GetActiveWindow(), msg.data(), _T("Exception thrown!"), + MB_ICONERROR | MB_OK); + } + } catch (...) { + if (suppressPrompts) { + printerror("Unhandled exception has been thrown!"); + } else { + MessageBox(GetActiveWindow(), _T("Unhandled exception has been thrown!"), + _T("Exception thrown!"), MB_ICONERROR | MB_OK); + } + } + + SwapLocale(); + + return TRUE; } - diff --git a/src/HavokMax.cpp b/src/HavokMax.cpp index e4abd74..a3fc6df 100644 --- a/src/HavokMax.cpp +++ b/src/HavokMax.cpp @@ -1,942 +1,1082 @@ -/* Havok Tool for 3ds Max - Copyright(C) 2019 Lukas Cone - - This program is free software : you can redistribute it and / or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program.If not, see . - - Havok Tool uses HavokLib 2016-2019 Lukas Cone +/* Havok Tool for 3ds Max + Copyright(C) 2019-2020 Lukas Cone + + This program is free software : you can redistribute it and / or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program.If not, see . + + Havok Tool uses HavokLib 2016-2020 Lukas Cone */ +#include "datas/directory_scanner.hpp" +#include "datas/pugiex.hpp" +#include "datas/reflector_xml.hpp" +#include #include "HavokMax.h" +#include "MAXex/hk_preset.hpp" +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#define min(a, b) (((a) < (b)) ? (a) : (b)) #include "resource.h" -#include -#include #include <3dsmaxport.h> -#include -#include "datas/DirectoryScanner.hpp" +#include #include +#include +#include + +const TCHAR _name[] = _T("Havok Tool"); +const TCHAR _info[] = + _T("\n" HavokMax_COPYRIGHT "Lukas Cone\nVersion " HavokMax_VERSION); +const TCHAR _license[] = + _T("Havok Tool uses HavokLib, Copyright(C) 2016-2020 Lukas Cone."); +const TCHAR _homePage[] = + _T("https://lukascone.wordpress.com/2019/03/21/havok-3ds-max-plugin"); + +#include "MAXex/win/AboutDlg.h" + +REFLECTOR_CREATE(HavokMax, 1, VARNAMES, checked, visible, motionIndex, toolset, + animationStart, animationEnd, captureFrame, currentPresetName); -static const std::array toolsetNames = -{ - _T("5.5.0"), - _T("6.6.0"), - _T("7.1.0"), - _T("2010.2"), - _T("2011.1"), - _T("2011.2"), - _T("2012.2"), - _T("2013.1"), - _T("2014.1"), +struct PresetData : ReflectorInterface { + float scale; + bool external; + Matrix3 corMat; + PresetData() : scale(1.0f), external(false) { corMat.IdentityMatrix(); } }; -struct PresetData -{ - TSTRING name; - float scale; - bool external; - Matrix3 corMat; - PresetData() : scale(1.0f), name(_T("Default")), external(false) { corMat.IdentityMatrix(); } - bool operator==(const PresetData &input) const { return !name.compare(input.name); } - bool operator==(const TSTRING &input) const { return !name.compare(input); } +REFLECTOR_CREATE(PresetData, 1, VARNAMES, scale); + +std::set extensions = {_T("hkx"), _T("hkt"), _T("hka")}; +static std::map presets{ + {"Default", PresetData{}}, }; -extern HINSTANCE hInstance; -static const TCHAR *categoryName = _T("HK_PRESET"); -static PresetData defaultPreset; -std::vector extensions = { _T("hkx"), _T("hkt"), _T("hka") }; -static std::vector presets = { &defaultPreset }; -static HBITMAP bitmapGreen, - bitmapRed, - bitmapGray; +void SavePresets(pugi::xml_node node) { + auto rootNode = node.append_child("presets"); + + for (auto &f : presets) { + if (!f.second.external) { + auto presetNode = rootNode.append_child("preset"); + presetNode.append_attribute("name").set_value(f.first.data()); + ReflectorWrapConst rWrap(f.second); + ReflectorXMLUtil::Save(rWrap, presetNode); + auto corMatNode = presetNode.append_child("matrix"); + auto corMatData = GetCorrectionMatrix(f.second.corMat); + corMatNode.append_buffer(corMatData.data(), corMatData.size()); + } + } +} -#define QUOTE(x) #x -#define CATTEDTEXT(...) StaticFor(QUOTE, __VA_ARGS__) +static void GetCorrectionMatrix(Matrix3 &value, es::string_view text) { + float sign = 1.0f; + uint32 curRow = 0; + + for (auto t : text) { + if (curRow > 2) { + return; + } + + switch (t) { + case '-': + sign = -1.f; + continue; + case 'X': + value.SetRow(curRow, Point3(sign, 0.0f, 0.0f)); + break; + case 'Y': + value.SetRow(curRow, Point3(0.0f, sign, 0.0f)); + break; + case 'Z': + value.SetRow(curRow, Point3(0.0f, 0.0f, sign)); + break; + } + + curRow++; + sign = 1.f; + } +} -const TCHAR _name[] = _T("Havok Tool"); -const TCHAR _info[] = _T(CATTEDTEXT(\nCopyright(C) 2016-2019 Lukas Cone\nVersion\x20, HAVOKMAX_VERSION)); -const TCHAR _license[] = _T("Havok Tool uses HavokLib, Copyright(C) 2016-2019 Lukas Cone."); -const TCHAR _homePage[] = _T("https://lukascone.wordpress.com/2019/03/21/havok-3ds-max-plugin"); +static auto &LoadPreset(pugi::xml_node node) { + auto prName = node.attribute("name").value(); + auto &prData = presets[prName]; + ReflectorWrap rWrap(prData); + ReflectorXMLUtil::Load(rWrap, node); + auto corMatNode = node.child("matrix"); -#include "MAXex/win/AboutDlg.h" + if (corMatNode.empty()) { + return prData; + } + + GetCorrectionMatrix(prData.corMat, corMatNode.text().get()); + return prData; +} + +static void LoadPresets(pugi::xml_node node) { + auto rootNode = node.child("presets"); + + if (rootNode.empty()) { + return; + } + + for (auto ch : rootNode) { + LoadPreset(ch); + } +} + +static void LoadExternalPresetLegacy(const TCHAR *filename) { + TSTRING prName_; + prName_.resize(0x102); + TCHAR legacyGroup[] = _T("HK_PRESET"); + auto numReadChars = + GetPrivateProfileString(legacyGroup, _T("Name"), _T(""), &prName_[0], + (DWORD)prName_.size(), filename); + prName_.resize(numReadChars); + + if (prName_.empty()) { + return; + } + + decltype(auto) prName = std::to_string(prName_); + prName.erase(0, 2); + auto &prData = presets[prName]; + prData.external = true; + prName_.resize(16); + + GetPrivateProfileString(legacyGroup, _T("Scale"), _T(""), &prName_[0], + (DWORD)prName_.size(), filename); + + prData.scale = std::stof(prName_); + + GetPrivateProfileString(legacyGroup, _T("Matrix"), _T(""), &prName_[0], + (DWORD)prName_.size(), filename); + + GetCorrectionMatrix(prData.corMat, std::to_string(prName_)); + + prName_.resize(0x102); + numReadChars = + GetPrivateProfileString(legacyGroup, _T("Extensions"), _T(""), + &prName_[0], (DWORD)prName_.size(), filename); + prName_.resize(numReadChars); + + if (prName_.empty()) { + return; + } + + es::basic_string_view extRef(prName_.data()); + extRef.remove_prefix(2); + + while (!extRef.empty()) { + const size_t found = extRef.find('|'); + + if (!found) { + extRef.remove_prefix(1); + } + + if (found == extRef.npos) { + extensions.emplace(extRef); + break; + } else { + es::basic_string_view subitem(extRef.begin(), found); + extensions.emplace(subitem); + extRef.remove_prefix(found + 1); + } + } +} + +void LoadExternalPreset(pugi::xml_node node) { + auto &prData = LoadPreset(node); + prData.external = true; + auto extNode = node.child("extensions"); + + if (extNode.empty()) { + return; + } + + for (auto c : extNode) { + extensions.emplace(ToTSTRING(c.name())); + } +} + +void ScanPresets() { + TSTRING cfgPath = IPathConfigMgr::GetPathConfigMgr()->GetDir(APP_PLUGCFG_DIR); + auto cfgPathS = std::to_string(cfgPath); + DirectoryScanner legacyScan; + legacyScan.AddFilter(".ini"); + legacyScan.Scan(cfgPathS); + + for (auto &s : legacyScan) { + auto cName = ToTSTRING(s); + LoadExternalPresetLegacy(cName.data()); + } + + DirectoryScanner scanner; + scanner.AddFilter(".xml"); + scanner.Scan(cfgPathS); + + for (auto &s : scanner) { + try { + pugi::xml_document doc = XMLFromFile(s); + auto prNode = doc.child("HavokPreset"); + + if (prNode.empty()) { + continue; + } + + LoadExternalPreset(prNode); + } catch (...) { + continue; + } + } +} + +static HBITMAP bitmapGreen, bitmapRed, bitmapGray; + +void BuildHavokResources() { + HBITMAP bitmapBulbs = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1)); + HIMAGELIST images = ImageList_Create(8, 8, 0, 0, 1); + ImageList_Add(images, bitmapBulbs, NULL); + DeleteObject(bitmapBulbs); + + HICON hIcon = ImageList_GetIcon(images, 0, 0); + ICONINFO iconInfo; + GetIconInfo(hIcon, &iconInfo); + bitmapGreen = iconInfo.hbmColor; + + hIcon = ImageList_GetIcon(images, 1, 0); + GetIconInfo(hIcon, &iconInfo); + bitmapRed = iconInfo.hbmColor; + + hIcon = ImageList_GetIcon(images, 2, 0); + GetIconInfo(hIcon, &iconInfo); + bitmapGray = iconInfo.hbmColor; + + ImageList_Destroy(images); +} + +void DestroyHavokResources() { + DeleteObject(bitmapGreen); + DeleteObject(bitmapRed); + DeleteObject(bitmapGray); +} + +HavokMax::HavokMax() + : hWnd(), comboHandle(), currentPresetName("Default"), objectScale(1.0f), + instanceDialogType(DLGTYPE_unknown), toolset(HK500), captureFrame(), + motionIndex() { + corMat.IdentityMatrix(); + + Interval aniRange = GetCOREInterface()->GetAnimRange(); + + animationStart = aniRange.Start() / GetTicksPerFrame(); + animationEnd = aniRange.End() / GetTicksPerFrame(); + + RegisterReflectedTypes(); + + ScanPresets(); + LoadCFG(); + + auto fndPres = presets.find(currentPresetName); + + if (!es::IsEnd(presets, fndPres)) { + corMat = fndPres->second.corMat; + objectScale = fndPres->second.scale; + } +} + +static auto GetConfig() { + TSTRING cfgpath = IPathConfigMgr::GetPathConfigMgr()->GetDir(APP_PLUGCFG_DIR); + return cfgpath + _T("/HavokMaxSettings.xml"); +} + +static void LoadLegacyConfig() { + TSTRING cfgpath = IPathConfigMgr::GetPathConfigMgr()->GetDir(APP_PLUGCFG_DIR); + cfgpath += _T("/HavokImpSettings.ini"); + TCHAR legacyGroup[] = _T("HK_PRESETS_HEADER"); + UINT numPresets = GetPrivateProfileInt(legacyGroup, _T("numUserPresets"), -1, + cfgpath.data()); + + auto Migrate = [&](auto &&prName_) { + TSTRING group; + group.resize(0x102); + + GetPrivateProfileString(prName_.data(), _T("Scale"), _T(""), &group[0], + (DWORD)group.size(), cfgpath.data()); + auto &prData = presets[to_string(prName_)]; + prData.scale = std::stof(group); + + GetPrivateProfileString(prName_.data(), _T("Matrix"), _T(""), &group[0], + (DWORD)group.size(), cfgpath.data()); + + GetCorrectionMatrix(prData.corMat, std::to_string(group)); + }; + + Migrate(TSTRING(_T("Default"))); + + if (numPresets == -1) { + return; + } -void AddExtension(TSTRING &ext) -{ - for (auto &e : extensions) - if (e == ext) - return; - - extensions.push_back(ext); -} + for (size_t p = 0; p < numPresets; p++) { + TSTRING genName = _T("PresetName"); + genName += ToTSTRING(std::to_string(p)); + TSTRING prName_; + prName_.resize(0x102); + auto numReadChars = + GetPrivateProfileString(legacyGroup, genName.data(), _T(""), &prName_[0], + (DWORD)prName_.size(), cfgpath.data()); -void Rescan() -{ - TSTRING CFGPath = IPathConfigMgr::GetPathConfigMgr()->GetDir(APP_PLUGCFG_DIR); - - DirectoryScanner scanner; - scanner.AddFilter(_T(".ini")); - scanner.Scan(CFGPath); - - TCHAR buffer[CFGBufferSize]; - - for (auto &cfg : scanner) - { - TSTRING extBuff; - - GetText(categoryName, extBuff, cfg.c_str(), buffer, _T("Name")); - - if (!extBuff.size()) - continue; - - PresetData *data = nullptr; - - for (auto &p : presets) - if (*p == extBuff) - { - data = p; - break; - } - - if (!data) - { - data = new PresetData(); - data->name = extBuff; - data->external = true; - presets.push_back(data); - } - - GetValue(categoryName, data->scale, cfg.c_str(), buffer, _T("Scale")); - GetCorrectionMatrix(data->corMat, cfg.c_str(), buffer); - extBuff.clear(); - GetText(categoryName, extBuff, cfg.c_str(), buffer, _T("Extensions")); - - if (extBuff.size()) - { - size_t foundSeparator = extBuff.find('|'); - size_t lastSeparator = 0; - - if (foundSeparator == TSTRING::npos) - { - AddExtension(extBuff); - } - else - { - do - { - TSTRING cExt = extBuff.substr(lastSeparator, foundSeparator - lastSeparator); - AddExtension(cExt); - lastSeparator = ++foundSeparator; - } while ((foundSeparator = extBuff.find('|', foundSeparator)) != TSTRING::npos); - - TSTRING cExt = extBuff.substr(lastSeparator); - AddExtension(cExt); - } - } - } -} - -void BuildHavokResources() -{ - HBITMAP bitmapBulbs = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1)); - HIMAGELIST images = ImageList_Create(8, 8, 0, 0, 1); - ImageList_Add(images, bitmapBulbs, NULL); - DeleteObject(bitmapBulbs); - - HICON hIcon = ImageList_GetIcon(images, 0, 0); - ICONINFO iconInfo; - GetIconInfo(hIcon, &iconInfo); - bitmapGreen = iconInfo.hbmColor; - - hIcon = ImageList_GetIcon(images, 1, 0); - GetIconInfo(hIcon, &iconInfo); - bitmapRed = iconInfo.hbmColor; - - hIcon = ImageList_GetIcon(images, 2, 0); - GetIconInfo(hIcon, &iconInfo); - bitmapGray = iconInfo.hbmColor; - - ImageList_Destroy(images); -} - -void DestroyHavokResources() -{ - for (auto &f : presets) - if (f != &defaultPreset) - delete f; - - DeleteObject(bitmapGreen); - DeleteObject(bitmapRed); - DeleteObject(bitmapGray); -} - -HavokMax::HavokMax() : - CFGFile(nullptr), hWnd(nullptr), comboHandle(nullptr), currentPresetName(_T("Default")), - IDConfigValue(IDC_EDIT_SCALE)(1.0f), instanceDialogType(DLGTYPE_unknown), IDConfigIndex(IDC_CB_TOOLSET)(0), IDConfigIndex(IDC_EDIT_CAPTUREFRAME)(0) -{ - corMat.IdentityMatrix(); - - Interval aniRange = GetCOREInterface()->GetAnimRange(); - - IDConfigIndex(IDC_EDIT_ANISTART) = aniRange.Start() / GetTicksPerFrame(); - IDConfigIndex(IDC_EDIT_ANIEND) = aniRange.End() / GetTicksPerFrame(); - - Rescan(); - LoadCFG(); - - for (auto &p : presets) - if (!p->name.compare(currentPresetName)) - { - corMat = p->corMat; - IDC_EDIT_SCALE_value = p->scale; - } -} - -void HavokMax::BuildCFG() -{ - cfgpath = IPathConfigMgr::GetPathConfigMgr()->GetDir(APP_PLUGCFG_DIR); - cfgpath.append(_T("\\HavokImpSettings.ini")); - CFGFile = cfgpath.c_str(); -} -static const TCHAR *hkPresetHeader = _T("HK_PRESETS_HEADER"); - -void HavokMax::LoadCFG() -{ - BuildCFG(); - TCHAR buffer[CFGBufferSize]; - int numUserPresets = 0; - - GetText(hkPresetHeader, currentPresetName, CFGFile, buffer, _T("currentPresetName")); - GetIndex(hkPresetHeader, numUserPresets, CFGFile, buffer, _T("numUserPresets")); - - for (int p = 0; p < numUserPresets; p++) - { - TSTRING prStr = _T("PresetName"); - prStr.append(ToTSTRING(p)); - - PresetData *data = nullptr; - TSTRING presetName; - bool pushNew = false; - - GetText(hkPresetHeader, presetName, CFGFile, buffer, prStr.c_str()); - - for (auto &p : presets) - if (!p->name.compare(presetName)) - data = p; - - if (!data) - { - data = new PresetData(); - data->name = presetName; - pushNew = true; - } - - GetValue(data->name.c_str(), data->scale, CFGFile, buffer, _T("Scale")); - GetCorrectionMatrix(data->corMat, CFGFile, buffer, data->name.c_str()); - - if (pushNew) - presets.push_back(data); - } - - GetValue(defaultPreset.name.c_str(), defaultPreset.scale, CFGFile, buffer, _T("Scale")); - GetCorrectionMatrix(defaultPreset.corMat, CFGFile, buffer, defaultPreset.name.c_str()); - - GetCFGIndex(IDC_CB_TOOLSET); - GetCFGIndex(IDC_EDIT_ANIEND); - GetCFGIndex(IDC_EDIT_ANISTART); - GetCFGIndex(IDC_EDIT_CAPTUREFRAME); - GetCFGIndex(IDC_EDIT_MOTIONID); - GetCFGChecked(IDC_CH_ANIMATION); - GetCFGChecked(IDC_CH_ANISKELETON); - GetCFGChecked(IDC_CH_ANIOPTIMIZE); - GetCFGChecked(IDC_CH_DISABLE_SCALE); - GetCFGEnabled(IDC_CH_ANISKELETON); - GetCFGEnabled(IDC_CH_ANIOPTIMIZE); - GetCFGEnabled(IDC_EDIT_ANIEND); - GetCFGEnabled(IDC_EDIT_ANISTART); - GetCFGEnabled(IDC_SPIN_ANIEND); - GetCFGEnabled(IDC_SPIN_ANISTART); -} - -void HavokMax::SaveCFG() -{ - BuildCFG(); - TCHAR buffer[CFGBufferSize]; - const LRESULT curSel = SendMessage(comboHandle, CB_GETCURSEL, 0, 0); - int numUserPresets = 0; - - currentPresetName = presets[curSel]->name.c_str(); - WriteText(hkPresetHeader, currentPresetName, CFGFile, _T("currentPresetName")); - - for (auto &p : presets) - if (!p->external) - { - if (p != &defaultPreset) - { - TSTRING prStr = _T("PresetName"); - prStr.append(ToTSTRING(numUserPresets)); - numUserPresets++; - WriteText(hkPresetHeader, p->name, CFGFile, prStr.c_str()); - } - - WriteValue(p->name.c_str(), p->scale, CFGFile, buffer, _T("Scale")); - WriteCorrectionMatrix(p->corMat, CFGFile, buffer, p->name.c_str()); - } - - WriteIndex(hkPresetHeader, numUserPresets, CFGFile, buffer, _T("numUserPresets")); - - SetCFGIndex(IDC_CB_TOOLSET); - SetCFGIndex(IDC_EDIT_ANIEND); - SetCFGIndex(IDC_EDIT_ANISTART); - SetCFGIndex(IDC_EDIT_CAPTUREFRAME); - SetCFGIndex(IDC_EDIT_MOTIONID); - SetCFGChecked(IDC_CH_ANIMATION); - SetCFGChecked(IDC_CH_ANISKELETON); - SetCFGChecked(IDC_CH_ANIOPTIMIZE); - SetCFGChecked(IDC_CH_DISABLE_SCALE); - SetCFGEnabled(IDC_CH_ANISKELETON); - SetCFGEnabled(IDC_CH_ANIOPTIMIZE); - SetCFGEnabled(IDC_EDIT_ANIEND); - SetCFGEnabled(IDC_EDIT_ANISTART); - SetCFGEnabled(IDC_SPIN_ANIEND); - SetCFGEnabled(IDC_SPIN_ANISTART); -} - -int HavokMax::SavePreset(const TCHAR* presetName) -{ - PresetData *data = nullptr; - - for (auto &p : presets) - if (*p == presetName) - { - data = p; - break; - } - - if (data) - { - MessageBox(hWnd, _T("Creation of duplicate presets is not allowed.\nPlease choose a diferent name."), _T("Cannot save preset"), MB_ICONSTOP); - return 1; - } - - if (!data) - { - data = new PresetData(); - data->name = presetName; - presets.push_back(data); - } - - return SavePreset(data); -} - -void HavokMax::UpdateData() -{ - const LRESULT curSel = SendMessage(comboHandle, CB_GETCURSEL, 0, 0); - - SavePreset(presets[curSel]); - - corMat = presets[curSel]->corMat; -} - -void HavokMax::SetupExportUI() -{ - SetWindowText(hWnd, _T(CATTEDTEXT(Havok Export v, HAVOKMAX_VERSION))); - - HWND comboItem = GetDlgItem(hWnd, IDC_CB_TOOLSET); - - for (auto &t : toolsetNames) - SendMessage(comboItem, CB_ADDSTRING, 0, (LPARAM)t); - - SendMessage(comboItem, CB_SETCURSEL, IDC_CB_TOOLSET_index, 0); - - SetupIntSpinner(hWnd, IDC_SPIN_ANIEND, IDC_EDIT_ANIEND, -10000, 10000, IDC_EDIT_ANIEND_index); - SetupIntSpinner(hWnd, IDC_SPIN_ANISTART, IDC_EDIT_ANISTART, -10000, 10000, IDC_EDIT_ANISTART_index); - SetupIntSpinner(hWnd, IDC_SPIN_CAPTUREFRAME, IDC_EDIT_CAPTUREFRAME, -10000, 10000, IDC_EDIT_CAPTUREFRAME_index); -} - -void HavokMax::Setup(HWND hwnd) -{ - hWnd = hwnd; - comboHandle = GetDlgItem(hwnd, IDC_CB_PRESET); -} - -INT_PTR CALLBACK NewPresetDLG(HWND hWnd, UINT message, WPARAM wParam, LPARAM) -{ - switch (message) - { - case WM_COMMAND: - switch (LOWORD(wParam)) - { - case IDOK: - { - HWND comboHandle = GetDlgItem(hWnd, IDC_CB_NEWPRESET); - const int textLen = GetWindowTextLength(comboHandle) + 1; - - if (textLen == 1) - { - EndDialog(hWnd, NULL); - return TRUE; - } - - TCHAR *textData = static_cast(malloc(sizeof(TCHAR) * textLen)); - GetWindowText(comboHandle, textData, textLen); - EndDialog(hWnd, reinterpret_cast(textData)); - return TRUE; - } - case IDCANCEL: - EndDialog(hWnd, NULL); - return TRUE; - } - } - return FALSE; -} - -INT_PTR CALLBACK DialogCallbacksMain(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - HavokMax *imp = reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); - - switch (message) - { - case WM_INITDIALOG: - { - CenterWindow(hWnd, GetParent(hWnd)); - SetWindowLongPtr(hWnd, GWLP_USERDATA, lParam); - imp = reinterpret_cast(lParam); - imp->Setup(hWnd); - imp->LoadCFG(); - - for (auto &p : presets) - SendMessage(imp->comboHandle, CB_ADDSTRING, 0, (LPARAM)p->name.c_str()); - - LRESULT reslt = SendMessage(imp->comboHandle, CB_SELECTSTRING, 0, (LPARAM)imp->currentPresetName.c_str()); - reslt = reslt > 0 ? reslt : 0; - - SendMessage(imp->comboHandle, CB_SETCURSEL, reslt, 0); - - imp->UpdatePresetUI(presets[reslt]); - - if (imp->instanceDialogType == HavokMax::DLGTYPE_import) - SetWindowText(hWnd, _T(CATTEDTEXT(Havok Import v, HAVOKMAX_VERSION))); - else if (imp->instanceDialogType == HavokMax::DLGTYPE_export) - imp->SetupExportUI(); - - return TRUE; - } - case WM_CLOSE: - imp->SaveCFG(); - EndDialog(hWnd, 0); - return TRUE; - case WM_COMMAND: - switch (LOWORD(wParam)) - { - case IDC_BT_DONE: - if (!imp->SanityBitcher()) - { - imp->SaveCFG(); - EndDialog(hWnd, 1); - } - return TRUE; - case IDC_BT_ABOUT: - ShowAboutDLG(hWnd); - return TRUE; - case IDC_BT_CANCEL: - EndDialog(hWnd, 0); - return TRUE; - - case IDC_BT_SAVEPRESET: - imp->SaveCFG(); - return TRUE; - - case IDC_BT_ADDPRESET: - { - if (!imp->SanityBitcher()) - { - TCHAR *textData = reinterpret_cast(DialogBox(hInstance, MAKEINTRESOURCE(IDD_NEWPRESET), hWnd, NewPresetDLG)); - - if (textData) - { - if (!imp->SavePreset(textData)) - { - const LRESULT relt = SendMessage(imp->comboHandle, CB_ADDSTRING, 0, (LPARAM)textData); - SendMessage(imp->comboHandle, CB_SETCURSEL, relt, 0); - imp->UpdatePresetUI(presets[relt]); - } - - free(textData); - } - imp->SaveCFG(); - } - return TRUE; - } - case IDC_BT_DELETEPRESET: - { - const LRESULT curSel = SendMessage(imp->comboHandle, CB_GETCURSEL, 0, 0); - - if (curSel > 0 && !presets[curSel]->external) - { - SendMessage(imp->comboHandle, CB_DELETESTRING, curSel, 0); - SendMessage(imp->comboHandle, CB_SETCURSEL, curSel - 1, 0); - imp->UpdatePresetUI(presets[curSel - 1]); - - WritePrivateProfileString(presets[curSel]->name.c_str(), NULL, NULL, imp->CFGFile); - delete presets[curSel]; - presets.erase(presets.begin() + curSel); - } - imp->SaveCFG(); - return TRUE; - } - - case IDC_CB_PRESET: - { - switch (HIWORD(wParam)) - { - case CBN_SELCHANGE: - { - const LRESULT curSel = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); - imp->UpdatePresetUI(presets[curSel]); - return TRUE; - } - break; - } - break; - } - - case IDC_CB_TOOLSET: - { - switch (HIWORD(wParam)) - { - case CBN_SELCHANGE: - { - const LRESULT curSel = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); - imp->IDC_CB_TOOLSET_index = curSel; - return TRUE; - } - break; - } - break; - } - - MSGCheckbox(IDC_CH_ANIMATION); - MSGEnable(IDC_CH_ANIMATION, IDC_CH_ANISKELETON); - MSGEnable(IDC_CH_ANIMATION, IDC_EDIT_ANIEND); - MSGEnable(IDC_CH_ANIMATION, IDC_EDIT_ANISTART); - MSGEnable(IDC_CH_ANIMATION, IDC_SPIN_ANIEND); - MSGEnable(IDC_CH_ANIMATION, IDC_SPIN_ANISTART); - MSGEnableEnabled(IDC_CH_ANISKELETON, IDC_CH_ANIOPTIMIZE); - break; - - MSGCheckbox(IDC_CH_ANIOPTIMIZE); break; - - MSGCheckbox(IDC_CH_DISABLE_SCALE); break; - - MSGCheckbox(IDC_CH_ANISKELETON); - MSGEnableEnabled(IDC_CH_ANISKELETON, IDC_CH_ANIOPTIMIZE); - break; - - default: - return imp ? imp->DlgCommandCallBack(wParam, lParam) : FALSE; - } - - case CC_SPINNER_CHANGE: - switch (LOWORD(wParam)) - { - case IDC_SPIN_SCALE: - imp->IDC_EDIT_SCALE_value = reinterpret_cast(lParam)->GetFVal(); - imp->UpdateData(); - break; - case IDC_SPIN_CAPTUREFRAME: - imp->IDC_EDIT_CAPTUREFRAME_index = reinterpret_cast(lParam)->GetIVal(); - break; - case IDC_SPIN_ANISTART: - imp->IDC_EDIT_ANISTART_index = reinterpret_cast(lParam)->GetIVal(); - break; - case IDC_SPIN_ANIEND: - imp->IDC_EDIT_ANIEND_index= reinterpret_cast(lParam)->GetIVal(); - break; - case IDC_SPIN_MOTIONID: - imp->IDC_EDIT_MOTIONID_index = reinterpret_cast(lParam)->GetIVal(); - break; - } - } - return (INT_PTR)FALSE; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// HavokMaxV1 + prName_.resize(numReadChars); + + if (prName_.empty()) { + return; + } + + prName_.erase(0, 2); + + Migrate(prName_); + } +} + +void HavokMax::LoadCFG() { + pugi::xml_document doc; + auto conf = GetConfig(); + + if (doc.load_file(conf.data())) { + ReflectorWrap rWrap(this); + ReflectorXMLUtil::Load(rWrap, doc); + LoadPresets(doc); + } else { + LoadLegacyConfig(); + } + + // clang-format off + CheckDlgButton(hWnd, IDC_CH_ANIMATION, checked[Checked::CH_ANIMATION]); + CheckDlgButton(hWnd, IDC_CH_ANIOPTIMIZE, checked[Checked::CH_ANIOPTIMIZE]); + CheckDlgButton(hWnd, IDC_CH_ANISKELETON, checked[Checked::CH_ANISKELETON]); + CheckDlgButton(hWnd, IDC_CH_DISABLE_SCALE, checked[Checked::CH_DISABLE_SCALE]); + EnableWindow(GetDlgItem(hWnd, IDC_CH_ANIOPTIMIZE), visible[Visible::CH_ANIOPTIMIZE]); + EnableWindow(GetDlgItem(hWnd, IDC_CH_ANISKELETON), visible[Visible::CH_ANISKELETON]); + EnableWindow(GetDlgItem(hWnd, IDC_EDIT_ANIEND), visible[Visible::SP_ANIEND]); + EnableWindow(GetDlgItem(hWnd, IDC_SPIN_ANIEND), visible[Visible::SP_ANIEND]); + EnableWindow(GetDlgItem(hWnd, IDC_EDIT_ANISTART), visible[Visible::SP_ANISTART]); + EnableWindow(GetDlgItem(hWnd, IDC_SPIN_ANISTART), visible[Visible::SP_ANISTART]); + // clang-format on +} + +void HavokMax::SaveCFG() { + pugi::xml_document doc; + ReflectorWrapConst rWrap(this); + ReflectorXMLUtil::Save(rWrap, doc); + SavePresets(doc); + auto conf = GetConfig(); + doc.save_file(conf.data()); +} + +int HavokMax::SavePreset(const std::string &presetName) { + auto fnd = presets.find(presetName); + + if (!es::IsEnd(presets, fnd)) { + MessageBox(hWnd, + _T("Creation of duplicate presets is not allowed.\nPlease ") + _T("choose a diferent name."), + _T("Cannot save preset"), MB_ICONSTOP); + return 1; + } + + return SavePreset(presets[presetName]); +} + +auto GetPreset(HWND hWnd) { + const int textLen = GetWindowTextLength(hWnd); + TSTRING wndText; + wndText.resize(textLen); + GetWindowText(hWnd, &wndText[0], textLen + 1); + decltype(auto) wndTextS = std::to_string(wndText); + return presets.find(wndTextS); +} + +void HavokMax::UpdateData() { + auto cPres = GetPreset(comboHandle); + SavePreset(cPres->second); + corMat = cPres->second.corMat; +} + +void HavokMax::SetupExportUI() { + SetWindowText(hWnd, _T("Havok Export V" HavokMax_VERSION)); + + HWND comboItem = GetDlgItem(hWnd, IDC_CB_TOOLSET); + auto ren = GetReflectedEnum(); + + for (auto t : ren) { + if (t == "HKUNKVER") { + continue; + } + + if (t == "HK2015") { + break; + } + + t.remove_prefix(2); + + if (t.size() > 3) { + const size_t fndUnder = t.find('_'); + auto cName = ToTSTRING(t.to_string()); + + if (fndUnder != t.npos) { + cName.replace(fndUnder, 1, 1, '.'); + } + + SendMessage(comboItem, CB_ADDSTRING, 0, (LPARAM)cName.data()); + } else { + const TCHAR verOld[] = {t[0], '.', t[1], '.', t[2], 0}; + SendMessage(comboItem, CB_ADDSTRING, 0, (LPARAM)verOld); + } + } + + SendMessage(comboItem, CB_SETCURSEL, toolset - 1, 0); + + SetupIntSpinner(hWnd, IDC_SPIN_ANIEND, IDC_EDIT_ANIEND, -10000, 10000, + animationEnd); + SetupIntSpinner(hWnd, IDC_SPIN_ANISTART, IDC_EDIT_ANISTART, -10000, 10000, + animationStart); + SetupIntSpinner(hWnd, IDC_SPIN_CAPTUREFRAME, IDC_EDIT_CAPTUREFRAME, -10000, + 10000, captureFrame); +} + +void HavokMax::Setup(HWND hwnd) { + hWnd = hwnd; + comboHandle = GetDlgItem(hwnd, IDC_CB_PRESET); +} + +INT_PTR CALLBACK NewPresetDLG(HWND hWnd, UINT message, WPARAM wParam, LPARAM) { + switch (message) { + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: { + HWND comboHandle = GetDlgItem(hWnd, IDC_CB_NEWPRESET); + const int textLen = GetWindowTextLength(comboHandle) + 1; + + if (textLen == 1) { + EndDialog(hWnd, NULL); + return TRUE; + } + + TCHAR *textData = static_cast(malloc(sizeof(TCHAR) * textLen)); + GetWindowText(comboHandle, textData, textLen); + EndDialog(hWnd, reinterpret_cast(textData)); + return TRUE; + } + case IDCANCEL: + EndDialog(hWnd, NULL); + return TRUE; + } + } + return FALSE; +} + +INT_PTR CALLBACK DialogCallbacksMain(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam) { + HavokMax *imp = + reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); + + switch (message) { + case WM_INITDIALOG: { + CenterWindow(hWnd, GetParent(hWnd)); + SetWindowLongPtr(hWnd, GWLP_USERDATA, lParam); + imp = reinterpret_cast(lParam); + imp->Setup(hWnd); + imp->LoadCFG(); + + for (auto &p : presets) { + auto cName = ToTSTRING(p.first); + SendMessage(imp->comboHandle, CB_ADDSTRING, 0, (LPARAM)cName.data()); + } + + auto cName = ToTSTRING(imp->currentPresetName); + const std::string defString = "Default"; + LRESULT reslt = + SendMessage(imp->comboHandle, CB_SELECTSTRING, 0, (LPARAM)cName.data()); + if (reslt < 0) { + cName = ToTSTRING(defString); + SendMessage(imp->comboHandle, CB_SELECTSTRING, 0, (LPARAM)cName.c_str()); + } + + decltype(auto) cNameS = reslt < 0 ? defString : imp->currentPresetName; + + imp->UpdatePresetUI(presets.at(cNameS)); + + if (imp->instanceDialogType == HavokMax::DLGTYPE_import) { + SetWindowText(hWnd, _T("Havok Import V" HavokMax_VERSION)); + } else if (imp->instanceDialogType == HavokMax::DLGTYPE_export) { + imp->SetupExportUI(); + } + + return TRUE; + } + case WM_CLOSE: + imp->SaveCFG(); + EndDialog(hWnd, 0); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_BT_DONE: + if (!imp->SanityBitcher()) { + imp->SaveCFG(); + EndDialog(hWnd, 1); + } + return TRUE; + case IDC_BT_ABOUT: + ShowAboutDLG(hWnd); + return TRUE; + case IDC_BT_CANCEL: + EndDialog(hWnd, 0); + return TRUE; + + case IDC_BT_SAVEPRESET: + imp->SaveCFG(); + return TRUE; + + case IDC_BT_ADDPRESET: { + if (!imp->SanityBitcher()) { + TCHAR *textData = reinterpret_cast(DialogBox( + hInstance, MAKEINTRESOURCE(IDD_NEWPRESET), hWnd, NewPresetDLG)); + + if (textData) { + std::string presetName = std::to_string(textData); + if (!imp->SavePreset(presetName)) { + const LRESULT relt = SendMessage(imp->comboHandle, CB_ADDSTRING, 0, + (LPARAM)textData); + SendMessage(imp->comboHandle, CB_SETCURSEL, relt, 0); + auto cPres = GetPreset(imp->comboHandle); + imp->UpdatePresetUI(cPres->second); + imp->currentPresetName = cPres->first; + } + + free(textData); + } + imp->SaveCFG(); + } + return TRUE; + } + case IDC_BT_DELETEPRESET: { + auto cPres = GetPreset(imp->comboHandle); + + if (cPres->first != "Default" && !cPres->second.external) { + const LRESULT curSel = + SendMessage(imp->comboHandle, CB_GETCURSEL, 0, 0); + SendMessage(imp->comboHandle, CB_SETCURSEL, curSel - 1, 0); + SendMessage(imp->comboHandle, CB_DELETESTRING, curSel, 0); + auto nPres = GetPreset(imp->comboHandle); + imp->UpdatePresetUI(nPres->second); + imp->currentPresetName = nPres->first; + presets.erase(cPres); + } + imp->SaveCFG(); + return TRUE; + } + + case IDC_CB_PRESET: { + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: { + auto cPres = GetPreset(imp->comboHandle); + imp->UpdatePresetUI(cPres->second); + imp->currentPresetName = cPres->first; + return TRUE; + } break; + } + break; + } + + case IDC_CB_TOOLSET: { + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: { + const LRESULT curSel = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); + imp->toolset = static_cast(curSel + 1); + return TRUE; + } break; + } + break; + } + + case IDC_CH_ANIMATION: { + const bool isChecked = IsDlgButtonChecked(hWnd, IDC_CH_ANIMATION) != 0; + imp->checked.Set(Checked::CH_ANIMATION, isChecked); + imp->visible.Set(Visible::CH_ANISKELETON, isChecked); + imp->visible.Set(Visible::SP_ANIEND, isChecked); + imp->visible.Set(Visible::SP_ANISTART, isChecked); + + EnableWindow(GetDlgItem(hWnd, IDC_CH_ANISKELETON), isChecked); + EnableWindow(GetDlgItem(hWnd, IDC_EDIT_ANIEND), isChecked); + EnableWindow(GetDlgItem(hWnd, IDC_EDIT_ANISTART), isChecked); + EnableWindow(GetDlgItem(hWnd, IDC_SPIN_ANIEND), isChecked); + EnableWindow(GetDlgItem(hWnd, IDC_SPIN_ANISTART), isChecked); + } + + case IDC_CH_ANISKELETON: { + const bool isChecked = IsDlgButtonChecked(hWnd, IDC_CH_ANISKELETON) != 0; + imp->checked.Set(Checked::CH_ANISKELETON, isChecked); + imp->visible.Set(Visible::CH_ANIOPTIMIZE, + isChecked && imp->visible[Visible::CH_ANISKELETON]); + EnableWindow(GetDlgItem(hWnd, IDC_CH_ANIOPTIMIZE), + imp->visible[Visible::CH_ANIOPTIMIZE]); + break; + } + + case IDC_CH_ANIOPTIMIZE: + imp->checked.Set(Checked::CH_ANIOPTIMIZE, + IsDlgButtonChecked(hWnd, IDC_CH_ANIOPTIMIZE) != 0); + break; + + case IDC_CH_DISABLE_SCALE: + imp->checked.Set(Checked::CH_DISABLE_SCALE, + IsDlgButtonChecked(hWnd, IDC_CH_DISABLE_SCALE) != 0); + break; + + default: + return imp ? imp->DlgCommandCallBack(wParam, lParam) : FALSE; + } + + case CC_SPINNER_CHANGE: + switch (LOWORD(wParam)) { + case IDC_SPIN_SCALE: + imp->objectScale = reinterpret_cast(lParam)->GetFVal(); + imp->UpdateData(); + break; + case IDC_SPIN_CAPTUREFRAME: + imp->captureFrame = + reinterpret_cast(lParam)->GetIVal(); + break; + case IDC_SPIN_ANISTART: + imp->animationStart = + reinterpret_cast(lParam)->GetIVal(); + break; + case IDC_SPIN_ANIEND: + imp->animationEnd = + reinterpret_cast(lParam)->GetIVal(); + break; + case IDC_SPIN_MOTIONID: + imp->motionIndex = reinterpret_cast(lParam)->GetIVal(); + break; + } + } + return (INT_PTR)FALSE; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// HavokMaxV1 static const int toEnableItemsV1[] = { - IDC_RB_XX, IDC_RB_XY, IDC_RB_XZ, IDC_RB_YX, IDC_RB_YY, IDC_RB_YZ, IDC_RB_ZX, IDC_RB_ZY, - IDC_RB_ZZ, IDC_CH_ROWX, IDC_CH_ROWY, IDC_CH_ROWZ, IDC_SPIN, IDC_SPIN_SCALE }; - -void HavokMaxV1::UpdatePresetUI(PresetData *data) -{ - corMat = data->corMat; - IDC_EDIT_SCALE_value = data->scale; - - SetupFloatSpinner(hWnd, IDC_SPIN_SCALE, IDC_EDIT_SCALE, 0, 5000, IDC_EDIT_SCALE_value); - - if (data->corMat.GetRow(0)[0]) - CheckRadioButton(hWnd, IDC_RB_XX, IDC_RB_XZ, IDC_RB_XX); - else if (data->corMat.GetRow(0)[1]) - CheckRadioButton(hWnd, IDC_RB_XX, IDC_RB_XZ, IDC_RB_XY); - else if (data->corMat.GetRow(0)[2]) - CheckRadioButton(hWnd, IDC_RB_XX, IDC_RB_XZ, IDC_RB_XZ); - - if (data->corMat.GetRow(1)[0]) - CheckRadioButton(hWnd, IDC_RB_YZ, IDC_RB_YX, IDC_RB_YX); - else if (data->corMat.GetRow(1)[1]) - CheckRadioButton(hWnd, IDC_RB_YZ, IDC_RB_YX, IDC_RB_YY); - else if (data->corMat.GetRow(1)[2]) - CheckRadioButton(hWnd, IDC_RB_YZ, IDC_RB_YX, IDC_RB_YZ); - - if (data->corMat.GetRow(2)[0]) - CheckRadioButton(hWnd, IDC_RB_ZX, IDC_RB_ZZ, IDC_RB_ZX); - else if (data->corMat.GetRow(2)[1]) - CheckRadioButton(hWnd, IDC_RB_ZX, IDC_RB_ZZ, IDC_RB_ZY); - else if (data->corMat.GetRow(2)[2]) - CheckRadioButton(hWnd, IDC_RB_ZX, IDC_RB_ZZ, IDC_RB_ZZ); - - CheckDlgButton(hWnd, IDC_CH_ROWX, (data->corMat.GetRow(0)[0] + data->corMat.GetRow(0)[1] + data->corMat.GetRow(0)[2]) < 0); - CheckDlgButton(hWnd, IDC_CH_ROWY, (data->corMat.GetRow(1)[0] + data->corMat.GetRow(1)[1] + data->corMat.GetRow(1)[2]) < 0); - CheckDlgButton(hWnd, IDC_CH_ROWZ, (data->corMat.GetRow(2)[0] + data->corMat.GetRow(2)[1] + data->corMat.GetRow(2)[2]) < 0); - - CollisionHandler(); - - const int bruh = sizeof(toEnableItemsV1) / 4; - - for (int it = 0; it < bruh; it++) - EnableWindow(GetDlgItem(hWnd, toEnableItemsV1[it]), !data->external); - - SendDlgItemMessage(hWnd, IDC_PC_INVERT, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)(!data->external ? (sanityCheck[0] ? bitmapRed : bitmapGreen) : bitmapGray)); - SendDlgItemMessage(hWnd, IDC_PC_ROWX, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)(!data->external ? (sanityCheck[1] ? bitmapRed : bitmapGreen) : bitmapGray)); - SendDlgItemMessage(hWnd, IDC_PC_ROWY, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)(!data->external ? (sanityCheck[2] ? bitmapRed : bitmapGreen) : bitmapGray)); - SendDlgItemMessage(hWnd, IDC_PC_ROWZ, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)(!data->external ? (sanityCheck[3] ? bitmapRed : bitmapGreen) : bitmapGray)); -} - -int HavokMaxV1::SpawnImportDialog() -{ - instanceDialogType = DLGTYPE_import; - return DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_IMPORT), GetActiveWindow(), DialogCallbacksMain, reinterpret_cast(this)); -} - -int HavokMaxV1::SpawnExportDialog() -{ - instanceDialogType = DLGTYPE_export; - return DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_EXPORT), GetActiveWindow(), DialogCallbacksMain, reinterpret_cast(this)); -} - -void HavokMaxV1::CollisionHandler() -{ - if ( - (IsDlgButtonChecked(hWnd, IDC_RB_XX) && (IsDlgButtonChecked(hWnd, IDC_RB_YX) || IsDlgButtonChecked(hWnd, IDC_RB_ZX))) || - (IsDlgButtonChecked(hWnd, IDC_RB_XY) && (IsDlgButtonChecked(hWnd, IDC_RB_YY) || IsDlgButtonChecked(hWnd, IDC_RB_ZY))) || - (IsDlgButtonChecked(hWnd, IDC_RB_XZ) && (IsDlgButtonChecked(hWnd, IDC_RB_YZ) || IsDlgButtonChecked(hWnd, IDC_RB_ZZ))) - ) - { - SendDlgItemMessage(hWnd, IDC_PC_ROWX, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmapRed); - sanityCheck(1, true); - } - else - { - SendDlgItemMessage(hWnd, IDC_PC_ROWX, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmapGreen); - sanityCheck(1, false); - } - - if ( - (IsDlgButtonChecked(hWnd, IDC_RB_YX) && (IsDlgButtonChecked(hWnd, IDC_RB_XX) || IsDlgButtonChecked(hWnd, IDC_RB_ZX))) || - (IsDlgButtonChecked(hWnd, IDC_RB_YY) && (IsDlgButtonChecked(hWnd, IDC_RB_XY) || IsDlgButtonChecked(hWnd, IDC_RB_ZY))) || - (IsDlgButtonChecked(hWnd, IDC_RB_YZ) && (IsDlgButtonChecked(hWnd, IDC_RB_XZ) || IsDlgButtonChecked(hWnd, IDC_RB_ZZ))) - ) - { - SendDlgItemMessage(hWnd, IDC_PC_ROWY, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmapRed); - sanityCheck(2, true); - } - else - { - SendDlgItemMessage(hWnd, IDC_PC_ROWY, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmapGreen); - sanityCheck(2, false); - } - - if ( - (IsDlgButtonChecked(hWnd, IDC_RB_ZX) && (IsDlgButtonChecked(hWnd, IDC_RB_YX) || IsDlgButtonChecked(hWnd, IDC_RB_XX))) || - (IsDlgButtonChecked(hWnd, IDC_RB_ZY) && (IsDlgButtonChecked(hWnd, IDC_RB_YY) || IsDlgButtonChecked(hWnd, IDC_RB_XY))) || - (IsDlgButtonChecked(hWnd, IDC_RB_ZZ) && (IsDlgButtonChecked(hWnd, IDC_RB_YZ) || IsDlgButtonChecked(hWnd, IDC_RB_XZ))) - ) - { - SendDlgItemMessage(hWnd, IDC_PC_ROWZ, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmapRed); - sanityCheck(3, true); - } - else - { - SendDlgItemMessage(hWnd, IDC_PC_ROWZ, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmapGreen); - sanityCheck(3, false); - } - - const bool checkedStatus = IsDlgButtonChecked(hWnd, IDC_CH_ROWX) && IsDlgButtonChecked(hWnd, IDC_CH_ROWY) && IsDlgButtonChecked(hWnd, IDC_CH_ROWZ); - sanityCheck(0, checkedStatus); - SendDlgItemMessage(hWnd, IDC_PC_INVERT, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)(checkedStatus ? bitmapRed : bitmapGreen)); -} - -int HavokMaxV1::SanityBitcher() -{ - const int insane = reinterpret_cast(sanityCheck); - - if (!insane) - return insane; - - TSTRING reslt = _T("Invalid matrix parameters.\n"); - - if (sanityCheck[1] || sanityCheck[2] || sanityCheck[3]) - { - reslt.append(_T("Following rows are colliding: ")); - - if (sanityCheck[1]) - reslt.append(_T("X, ")); - - if (sanityCheck[2]) - reslt.append(_T("Y, ")); - - if (sanityCheck[3]) - reslt.append(_T("Z, ")); - - reslt.pop_back(); - reslt.pop_back(); - reslt.push_back('\n'); - } - - if (sanityCheck[0]) - reslt.append(_T("All rows are inverted!\n")); - - MessageBox(hWnd, reslt.c_str(), _T("Invalid matrix settings!"), MB_ICONSTOP); - - return insane; -} - -INT_PTR HavokMaxV1::DlgCommandCallBack(WPARAM wParam, LPARAM lParam) -{ - switch (LOWORD(wParam)) - { - case IDC_RB_XX: - case IDC_RB_YX: - case IDC_RB_ZX: - case IDC_RB_XY: - case IDC_RB_YY: - case IDC_RB_ZY: - case IDC_RB_XZ: - case IDC_RB_YZ: - case IDC_RB_ZZ: - case IDC_CH_ROWX: - case IDC_CH_ROWY: - case IDC_CH_ROWZ: - { - CollisionHandler(); - UpdateData(); - return TRUE; - } - } - return (INT_PTR) FALSE; -} - -int HavokMaxV1::SavePreset(PresetData *data) -{ - if (data->external) - { - MessageBox(hWnd, _T("Cannot save into external preset!"), _T("Cannot save preset"), MB_ICONSTOP); - return 1; - } - - data->scale = IDC_EDIT_SCALE_value; - - float sign = IsDlgButtonChecked(hWnd, IDC_CH_ROWX) ? -1.0f : 1.0f; + IDC_RB_XX, IDC_RB_XY, IDC_RB_XZ, IDC_RB_YX, IDC_RB_YY, + IDC_RB_YZ, IDC_RB_ZX, IDC_RB_ZY, IDC_RB_ZZ, IDC_CH_ROWX, + IDC_CH_ROWY, IDC_CH_ROWZ, IDC_SPIN, IDC_SPIN_SCALE}; + +void HavokMaxV1::UpdatePresetUI(PresetData &data) { + corMat = data.corMat; + objectScale = data.scale; + + SetupFloatSpinner(hWnd, IDC_SPIN_SCALE, IDC_EDIT_SCALE, 0, 5000, objectScale); + + if (data.corMat.GetRow(0)[0]) + CheckRadioButton(hWnd, IDC_RB_XX, IDC_RB_XZ, IDC_RB_XX); + else if (data.corMat.GetRow(0)[1]) + CheckRadioButton(hWnd, IDC_RB_XX, IDC_RB_XZ, IDC_RB_XY); + else if (data.corMat.GetRow(0)[2]) + CheckRadioButton(hWnd, IDC_RB_XX, IDC_RB_XZ, IDC_RB_XZ); + + if (data.corMat.GetRow(1)[0]) + CheckRadioButton(hWnd, IDC_RB_YZ, IDC_RB_YX, IDC_RB_YX); + else if (data.corMat.GetRow(1)[1]) + CheckRadioButton(hWnd, IDC_RB_YZ, IDC_RB_YX, IDC_RB_YY); + else if (data.corMat.GetRow(1)[2]) + CheckRadioButton(hWnd, IDC_RB_YZ, IDC_RB_YX, IDC_RB_YZ); + + if (data.corMat.GetRow(2)[0]) + CheckRadioButton(hWnd, IDC_RB_ZX, IDC_RB_ZZ, IDC_RB_ZX); + else if (data.corMat.GetRow(2)[1]) + CheckRadioButton(hWnd, IDC_RB_ZX, IDC_RB_ZZ, IDC_RB_ZY); + else if (data.corMat.GetRow(2)[2]) + CheckRadioButton(hWnd, IDC_RB_ZX, IDC_RB_ZZ, IDC_RB_ZZ); + + CheckDlgButton(hWnd, IDC_CH_ROWX, + (data.corMat.GetRow(0)[0] + data.corMat.GetRow(0)[1] + + data.corMat.GetRow(0)[2]) < 0); + CheckDlgButton(hWnd, IDC_CH_ROWY, + (data.corMat.GetRow(1)[0] + data.corMat.GetRow(1)[1] + + data.corMat.GetRow(1)[2]) < 0); + CheckDlgButton(hWnd, IDC_CH_ROWZ, + (data.corMat.GetRow(2)[0] + data.corMat.GetRow(2)[1] + + data.corMat.GetRow(2)[2]) < 0); + + CollisionHandler(); + + const int bruh = sizeof(toEnableItemsV1) / 4; + + for (int it = 0; it < bruh; it++) + EnableWindow(GetDlgItem(hWnd, toEnableItemsV1[it]), !data.external); + + SendDlgItemMessage(hWnd, IDC_PC_INVERT, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)(!data.external + ? (sanityCheck[0] ? bitmapRed : bitmapGreen) + : bitmapGray)); + SendDlgItemMessage(hWnd, IDC_PC_ROWX, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)(!data.external + ? (sanityCheck[1] ? bitmapRed : bitmapGreen) + : bitmapGray)); + SendDlgItemMessage(hWnd, IDC_PC_ROWY, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)(!data.external + ? (sanityCheck[2] ? bitmapRed : bitmapGreen) + : bitmapGray)); + SendDlgItemMessage(hWnd, IDC_PC_ROWZ, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)(!data.external + ? (sanityCheck[3] ? bitmapRed : bitmapGreen) + : bitmapGray)); +} + +int HavokMaxV1::SpawnImportDialog() { + instanceDialogType = DLGTYPE_import; + return DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_IMPORT), + GetActiveWindow(), DialogCallbacksMain, + reinterpret_cast(this)); +} + +int HavokMaxV1::SpawnExportDialog() { + instanceDialogType = DLGTYPE_export; + return DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_EXPORT), + GetActiveWindow(), DialogCallbacksMain, + reinterpret_cast(this)); +} + +void HavokMaxV1::CollisionHandler() { + if ((IsDlgButtonChecked(hWnd, IDC_RB_XX) && + (IsDlgButtonChecked(hWnd, IDC_RB_YX) || + IsDlgButtonChecked(hWnd, IDC_RB_ZX))) || + (IsDlgButtonChecked(hWnd, IDC_RB_XY) && + (IsDlgButtonChecked(hWnd, IDC_RB_YY) || + IsDlgButtonChecked(hWnd, IDC_RB_ZY))) || + (IsDlgButtonChecked(hWnd, IDC_RB_XZ) && + (IsDlgButtonChecked(hWnd, IDC_RB_YZ) || + IsDlgButtonChecked(hWnd, IDC_RB_ZZ)))) { + SendDlgItemMessage(hWnd, IDC_PC_ROWX, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)bitmapRed); + sanityCheck += 1; + } else { + SendDlgItemMessage(hWnd, IDC_PC_ROWX, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)bitmapGreen); + sanityCheck -= 1; + } + + if ((IsDlgButtonChecked(hWnd, IDC_RB_YX) && + (IsDlgButtonChecked(hWnd, IDC_RB_XX) || + IsDlgButtonChecked(hWnd, IDC_RB_ZX))) || + (IsDlgButtonChecked(hWnd, IDC_RB_YY) && + (IsDlgButtonChecked(hWnd, IDC_RB_XY) || + IsDlgButtonChecked(hWnd, IDC_RB_ZY))) || + (IsDlgButtonChecked(hWnd, IDC_RB_YZ) && + (IsDlgButtonChecked(hWnd, IDC_RB_XZ) || + IsDlgButtonChecked(hWnd, IDC_RB_ZZ)))) { + SendDlgItemMessage(hWnd, IDC_PC_ROWY, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)bitmapRed); + sanityCheck += 2; + } else { + SendDlgItemMessage(hWnd, IDC_PC_ROWY, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)bitmapGreen); + sanityCheck -= 2; + } + + if ((IsDlgButtonChecked(hWnd, IDC_RB_ZX) && + (IsDlgButtonChecked(hWnd, IDC_RB_YX) || + IsDlgButtonChecked(hWnd, IDC_RB_XX))) || + (IsDlgButtonChecked(hWnd, IDC_RB_ZY) && + (IsDlgButtonChecked(hWnd, IDC_RB_YY) || + IsDlgButtonChecked(hWnd, IDC_RB_XY))) || + (IsDlgButtonChecked(hWnd, IDC_RB_ZZ) && + (IsDlgButtonChecked(hWnd, IDC_RB_YZ) || + IsDlgButtonChecked(hWnd, IDC_RB_XZ)))) { + SendDlgItemMessage(hWnd, IDC_PC_ROWZ, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)bitmapRed); + sanityCheck += 3; + } else { + SendDlgItemMessage(hWnd, IDC_PC_ROWZ, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)bitmapGreen); + sanityCheck -= 3; + } + + const bool checkedStatus = IsDlgButtonChecked(hWnd, IDC_CH_ROWX) && + IsDlgButtonChecked(hWnd, IDC_CH_ROWY) && + IsDlgButtonChecked(hWnd, IDC_CH_ROWZ); + sanityCheck.Set(0, checkedStatus); + SendDlgItemMessage(hWnd, IDC_PC_INVERT, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM)(checkedStatus ? bitmapRed : bitmapGreen)); +} + +int HavokMaxV1::SanityBitcher() { + const int insane = + reinterpret_cast(sanityCheck); + + if (!insane) + return insane; + + TSTRING reslt = _T("Invalid matrix parameters.\n"); + + if (sanityCheck[1] || sanityCheck[2] || sanityCheck[3]) { + reslt.append(_T("Following rows are colliding: ")); - data->corMat.SetRow(0, Point3( - IsDlgButtonChecked(hWnd, IDC_RB_XX) ? sign : 0.0f, - IsDlgButtonChecked(hWnd, IDC_RB_XY) ? sign : 0.0f, - IsDlgButtonChecked(hWnd, IDC_RB_XZ) ? sign : 0.0f - )); + if (sanityCheck[1]) + reslt.append(_T("X, ")); - sign = IsDlgButtonChecked(hWnd, IDC_CH_ROWY) ? -1.0f : 1.0f; + if (sanityCheck[2]) + reslt.append(_T("Y, ")); - data->corMat.SetRow(1, Point3( - IsDlgButtonChecked(hWnd, IDC_RB_YX) ? sign : 0.0f, - IsDlgButtonChecked(hWnd, IDC_RB_YY) ? sign : 0.0f, - IsDlgButtonChecked(hWnd, IDC_RB_YZ) ? sign : 0.0f - )); + if (sanityCheck[3]) + reslt.append(_T("Z, ")); - sign = IsDlgButtonChecked(hWnd, IDC_CH_ROWZ) ? -1.0f : 1.0f; + reslt.pop_back(); + reslt.pop_back(); + reslt.push_back('\n'); + } - data->corMat.SetRow(2, Point3( - IsDlgButtonChecked(hWnd, IDC_RB_ZX) ? sign : 0.0f, - IsDlgButtonChecked(hWnd, IDC_RB_ZY) ? sign : 0.0f, - IsDlgButtonChecked(hWnd, IDC_RB_ZZ) ? sign : 0.0f - )); + if (sanityCheck[0]) + reslt.append(_T("All rows are inverted!\n")); - return 0; + MessageBox(hWnd, reslt.c_str(), _T("Invalid matrix settings!"), MB_ICONSTOP); + + return insane; +} + +INT_PTR HavokMaxV1::DlgCommandCallBack(WPARAM wParam, LPARAM lParam) { + switch (LOWORD(wParam)) { + case IDC_RB_XX: + case IDC_RB_YX: + case IDC_RB_ZX: + case IDC_RB_XY: + case IDC_RB_YY: + case IDC_RB_ZY: + case IDC_RB_XZ: + case IDC_RB_YZ: + case IDC_RB_ZZ: + case IDC_CH_ROWX: + case IDC_CH_ROWY: + case IDC_CH_ROWZ: { + CollisionHandler(); + UpdateData(); + return TRUE; + } + } + return (INT_PTR)FALSE; +} + +int HavokMaxV1::SavePreset(PresetData &data) { + if (data.external) { + MessageBox(hWnd, _T("Cannot save into external preset!"), + _T("Cannot save preset"), MB_ICONSTOP); + return 1; + } + + data.scale = objectScale; + + float sign = IsDlgButtonChecked(hWnd, IDC_CH_ROWX) ? -1.0f : 1.0f; + + data.corMat.SetRow(0, + Point3(IsDlgButtonChecked(hWnd, IDC_RB_XX) ? sign : 0.0f, + IsDlgButtonChecked(hWnd, IDC_RB_XY) ? sign : 0.0f, + IsDlgButtonChecked(hWnd, IDC_RB_XZ) ? sign : 0.0f)); + + sign = IsDlgButtonChecked(hWnd, IDC_CH_ROWY) ? -1.0f : 1.0f; + + data.corMat.SetRow(1, + Point3(IsDlgButtonChecked(hWnd, IDC_RB_YX) ? sign : 0.0f, + IsDlgButtonChecked(hWnd, IDC_RB_YY) ? sign : 0.0f, + IsDlgButtonChecked(hWnd, IDC_RB_YZ) ? sign : 0.0f)); + + sign = IsDlgButtonChecked(hWnd, IDC_CH_ROWZ) ? -1.0f : 1.0f; + + data.corMat.SetRow(2, + Point3(IsDlgButtonChecked(hWnd, IDC_RB_ZX) ? sign : 0.0f, + IsDlgButtonChecked(hWnd, IDC_RB_ZY) ? sign : 0.0f, + IsDlgButtonChecked(hWnd, IDC_RB_ZZ) ? sign : 0.0f)); + + return 0; } -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// HavokMaxV2 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// HavokMaxV2 -int HavokMaxV2::SanityBitcher() -{ - const int insane = reinterpret_cast(sanityCheck); +int HavokMaxV2::SanityBitcher() { + const int insane = + reinterpret_cast(sanityCheck); - if (!insane) - return insane; + if (!insane) + return insane; - TSTRING reslt; + TSTRING reslt; - if (sanityCheck[1]) - reslt.append(_T("Back and Right axes cannot have same values!\n")); + if (sanityCheck[1]) + reslt.append(_T("Back and Right axes cannot have same values!\n")); - if (sanityCheck[0]) - reslt.append(_T("All axes are inverted!\n")); + if (sanityCheck[0]) + reslt.append(_T("All axes are inverted!\n")); - MessageBox(hWnd, reslt.c_str(), _T("Invalid coordsys settings!"), MB_ICONSTOP); + MessageBox(hWnd, reslt.c_str(), _T("Invalid coordsys settings!"), + MB_ICONSTOP); - return insane; + return insane; } -void HavokMaxV2::CollisionHandler() -{ - const LRESULT backSel = SendMessage(comboBack, CB_GETCURSEL, 0, 0); - const LRESULT rightSel = SendMessage(comboRight, CB_GETCURSEL, 0, 0); +void HavokMaxV2::CollisionHandler() { + const LRESULT backSel = SendMessage(comboBack, CB_GETCURSEL, 0, 0); + const LRESULT rightSel = SendMessage(comboRight, CB_GETCURSEL, 0, 0); - const bool checkedStatus = IsDlgButtonChecked(hWnd, IDC_CH_INVERT_BACK) && IsDlgButtonChecked(hWnd, IDC_CH_INVERT_RIGHT) && IsDlgButtonChecked(hWnd, IDC_CH_INVERT_TOP); - sanityCheck(0, checkedStatus); + const bool checkedStatus = IsDlgButtonChecked(hWnd, IDC_CH_INVERT_BACK) && + IsDlgButtonChecked(hWnd, IDC_CH_INVERT_RIGHT) && + IsDlgButtonChecked(hWnd, IDC_CH_INVERT_TOP); + sanityCheck.Set(0, checkedStatus); - ShowWindow(GetDlgItem(hWnd, IDC_PC_INVERT_ERROR), checkedStatus ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hWnd, IDC_PC_INVERT_ERROR), + checkedStatus ? SW_SHOW : SW_HIDE); - const bool colliding = backSel == rightSel; - sanityCheck(1, colliding); + const bool colliding = backSel == rightSel; + sanityCheck.Set(1, colliding); - ShowWindow(GetDlgItem(hWnd, IDC_PC_REMAP_ERROR1), colliding ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(hWnd, IDC_PC_REMAP_ERROR2), colliding ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hWnd, IDC_PC_REMAP_ERROR1), + colliding ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hWnd, IDC_PC_REMAP_ERROR2), + colliding ? SW_SHOW : SW_HIDE); } -INT_PTR HavokMaxV2::DlgCommandCallBack(WPARAM wParam, LPARAM lParam) -{ - switch (LOWORD(wParam)) - { - case IDC_CB_BACK: - case IDC_CB_RIGHT: - case IDC_CH_INVERT_TOP: - case IDC_CH_INVERT_RIGHT: - case IDC_CH_INVERT_BACK: - { - CollisionHandler(); - UpdateData(); - return TRUE; - } - } - return (INT_PTR)FALSE; +INT_PTR HavokMaxV2::DlgCommandCallBack(WPARAM wParam, LPARAM lParam) { + switch (LOWORD(wParam)) { + case IDC_CB_BACK: + case IDC_CB_RIGHT: + case IDC_CH_INVERT_TOP: + case IDC_CH_INVERT_RIGHT: + case IDC_CH_INVERT_BACK: { + CollisionHandler(); + UpdateData(); + return TRUE; + } + } + return (INT_PTR)FALSE; } -int HavokMaxV2::SpawnImportDialog() -{ - instanceDialogType = DLGTYPE_import; - return DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_IMPORT_NEW), GetActiveWindow(), DialogCallbacksMain, reinterpret_cast(this)); +int HavokMaxV2::SpawnImportDialog() { + instanceDialogType = DLGTYPE_import; + return DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_IMPORT_NEW), + GetActiveWindow(), DialogCallbacksMain, + reinterpret_cast(this)); } -int HavokMaxV2::SpawnExportDialog() -{ - instanceDialogType = DLGTYPE_export; - return DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_EXPORT_NEW), GetActiveWindow(), DialogCallbacksMain, reinterpret_cast(this)); +int HavokMaxV2::SpawnExportDialog() { + instanceDialogType = DLGTYPE_export; + return DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_EXPORT_NEW), + GetActiveWindow(), DialogCallbacksMain, + reinterpret_cast(this)); } -void HavokMaxV2::Setup(HWND hwnd) -{ - HavokMax::Setup(hwnd); - comboBack = GetDlgItem(hWnd, IDC_CB_BACK); - comboRight = GetDlgItem(hWnd, IDC_CB_RIGHT); +void HavokMaxV2::Setup(HWND hwnd) { + HavokMax::Setup(hwnd); + comboBack = GetDlgItem(hWnd, IDC_CB_BACK); + comboRight = GetDlgItem(hWnd, IDC_CB_RIGHT); - static const TCHAR *_items[] = { _T("Right"), _T("Back"), _T("Top") }; + static const TCHAR *_items[] = {_T("Right"), _T("Back"), _T("Top")}; - for (int c = 0; c < 3; c++) - { - SendMessage(comboBack, CB_ADDSTRING, 0, (LPARAM)_items[c]); - SendMessage(comboRight, CB_ADDSTRING, 0, (LPARAM)_items[c]); - } + for (int c = 0; c < 3; c++) { + SendMessage(comboBack, CB_ADDSTRING, 0, (LPARAM)_items[c]); + SendMessage(comboRight, CB_ADDSTRING, 0, (LPARAM)_items[c]); + } } static const int toEnableItemsV2[] = { - IDC_CB_BACK, IDC_CB_RIGHT, IDC_CH_INVERT_TOP, IDC_CH_INVERT_RIGHT, IDC_CH_INVERT_BACK, IDC_SPIN, IDC_SPIN_SCALE }; + IDC_CB_BACK, IDC_CB_RIGHT, IDC_CH_INVERT_TOP, IDC_CH_INVERT_RIGHT, + IDC_CH_INVERT_BACK, IDC_SPIN, IDC_SPIN_SCALE}; + +void HavokMaxV2::UpdatePresetUI(PresetData &data) { + corMat = data.corMat; + objectScale = data.scale; -void HavokMaxV2::UpdatePresetUI(PresetData *data) -{ - corMat = data->corMat; - IDC_EDIT_SCALE_value = data->scale; + if (numAnimations && motionIndex < 0) { + motionIndex = 0; + } - SetupFloatSpinner(hWnd, IDC_SPIN_SCALE, IDC_EDIT_SCALE, 0, 5000, IDC_EDIT_SCALE_value); - SetupIntSpinner(hWnd, IDC_SPIN_MOTIONID, IDC_EDIT_MOTIONID, 0, numAnimations - 1, IDC_EDIT_MOTIONID_index); + SetupFloatSpinner(hWnd, IDC_SPIN_SCALE, IDC_EDIT_SCALE, 0, 5000, objectScale); + SetupIntSpinner(hWnd, IDC_SPIN_MOTIONID, IDC_EDIT_MOTIONID, 0, + numAnimations - 1, motionIndex); - int curIndex = 0; + int curIndex = 0; - if (data->corMat.GetRow(0)[1]) - curIndex = 1; - else if (data->corMat.GetRow(0)[2]) - curIndex = 2; + if (data.corMat.GetRow(0)[1]) + curIndex = 1; + else if (data.corMat.GetRow(0)[2]) + curIndex = 2; - SendMessage(comboRight, CB_SETCURSEL, curIndex, 0); - curIndex = 0; + SendMessage(comboRight, CB_SETCURSEL, curIndex, 0); + curIndex = 0; - if (data->corMat.GetRow(1)[1]) - curIndex = 1; - else if (data->corMat.GetRow(1)[2]) - curIndex = 2; + if (data.corMat.GetRow(1)[1]) + curIndex = 1; + else if (data.corMat.GetRow(1)[2]) + curIndex = 2; - SendMessage(comboBack, CB_SETCURSEL, curIndex, 0); + SendMessage(comboBack, CB_SETCURSEL, curIndex, 0); - CheckDlgButton(hWnd, IDC_CH_INVERT_RIGHT, (data->corMat.GetRow(0)[0] + data->corMat.GetRow(0)[1] + data->corMat.GetRow(0)[2]) < 0); - CheckDlgButton(hWnd, IDC_CH_INVERT_BACK, (data->corMat.GetRow(1)[0] + data->corMat.GetRow(1)[1] + data->corMat.GetRow(1)[2]) < 0); - CheckDlgButton(hWnd, IDC_CH_INVERT_TOP, (data->corMat.GetRow(2)[0] + data->corMat.GetRow(2)[1] + data->corMat.GetRow(2)[2]) < 0); + CheckDlgButton(hWnd, IDC_CH_INVERT_RIGHT, + (data.corMat.GetRow(0)[0] + data.corMat.GetRow(0)[1] + + data.corMat.GetRow(0)[2]) < 0); + CheckDlgButton(hWnd, IDC_CH_INVERT_BACK, + (data.corMat.GetRow(1)[0] + data.corMat.GetRow(1)[1] + + data.corMat.GetRow(1)[2]) < 0); + CheckDlgButton(hWnd, IDC_CH_INVERT_TOP, + (data.corMat.GetRow(2)[0] + data.corMat.GetRow(2)[1] + + data.corMat.GetRow(2)[2]) < 0); - CollisionHandler(); + CollisionHandler(); - const int bruh = sizeof(toEnableItemsV2) / 4; + const int bruh = sizeof(toEnableItemsV2) / 4; - for (int it = 0; it < bruh; it++) - EnableWindow(GetDlgItem(hWnd, toEnableItemsV2[it]), !data->external); + for (int it = 0; it < bruh; it++) + EnableWindow(GetDlgItem(hWnd, toEnableItemsV2[it]), !data.external); } -int HavokMaxV2::SavePreset(PresetData *data) -{ - if (data->external) - { - MessageBox(hWnd, _T("Cannot save into external preset!"), _T("Cannot save preset"), MB_ICONSTOP); - return 1; - } +int HavokMaxV2::SavePreset(PresetData &data) { + if (data.external) { + MessageBox(hWnd, _T("Cannot save into external preset!"), + _T("Cannot save preset"), MB_ICONSTOP); + return 1; + } - data->scale = IDC_EDIT_SCALE_value; + data.scale = objectScale; - float sign = IsDlgButtonChecked(hWnd, IDC_CH_INVERT_RIGHT) ? -1.0f : 1.0f; - Point3 value = {}; - int XAxisSelection = static_cast(SendMessage(comboRight, CB_GETCURSEL, 0, 0)); + float sign = IsDlgButtonChecked(hWnd, IDC_CH_INVERT_RIGHT) ? -1.0f : 1.0f; + Point3 value = {}; + int XAxisSelection = + static_cast(SendMessage(comboRight, CB_GETCURSEL, 0, 0)); - value[XAxisSelection] = sign; - data->corMat.SetRow(0, value); - value[XAxisSelection] = 0.0f; + value[XAxisSelection] = sign; + data.corMat.SetRow(0, value); + value[XAxisSelection] = 0.0f; - sign = IsDlgButtonChecked(hWnd, IDC_CH_INVERT_BACK) ? -1.0f : 1.0f; + sign = IsDlgButtonChecked(hWnd, IDC_CH_INVERT_BACK) ? -1.0f : 1.0f; - int YAxisSelection = static_cast(SendMessage(comboBack, CB_GETCURSEL, 0, 0)); + int YAxisSelection = + static_cast(SendMessage(comboBack, CB_GETCURSEL, 0, 0)); - value[YAxisSelection] = sign; - data->corMat.SetRow(1, value); - value[YAxisSelection] = 0.0f; + value[YAxisSelection] = sign; + data.corMat.SetRow(1, value); + value[YAxisSelection] = 0.0f; - sign = IsDlgButtonChecked(hWnd, IDC_CH_INVERT_TOP) ? -1.0f : 1.0f; + sign = IsDlgButtonChecked(hWnd, IDC_CH_INVERT_TOP) ? -1.0f : 1.0f; - int ZAxisSelection = 3 - (YAxisSelection + XAxisSelection); + int ZAxisSelection = 3 - (YAxisSelection + XAxisSelection); - value[ZAxisSelection] = sign; - data->corMat.SetRow(2, value); + value[ZAxisSelection] = sign; + data.corMat.SetRow(2, value); - return 0; + return 0; } diff --git a/src/HavokMax.def b/src/HavokMax.def deleted file mode 100644 index 1705038..0000000 --- a/src/HavokMax.def +++ /dev/null @@ -1,10 +0,0 @@ -LIBRARY HavokMax.dlu -EXPORTS - LibDescription @1 PRIVATE - LibNumberClasses @2 PRIVATE - LibClassDesc @3 PRIVATE - LibVersion @4 PRIVATE - LibInitialize @6 PRIVATE - LibShutdown @7 PRIVATE -SECTIONS - .data READ WRITE diff --git a/src/HavokMax.h b/src/HavokMax.h index 00cb3d4..0efcc31 100644 --- a/src/HavokMax.h +++ b/src/HavokMax.h @@ -1,139 +1,123 @@ -/* Havok Tool for 3ds Max - Copyright(C) 2019 Lukas Cone - - This program is free software : you can redistribute it and / or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program.If not, see . - - Havok Tool uses HavokLib 2016-2019 Lukas Cone +/* Havok Tool for 3ds Max + Copyright(C) 2019-2020 Lukas Cone + + This program is free software : you can redistribute it and / or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program.If not, see . + + Havok Tool uses HavokLib 2016-2020 Lukas Cone */ #pragma once #include "MAXex/3DSMaxSDKCompat.h" -#include +#include "datas/reflector.hpp" +#include "hklib/hk_base.hpp" +#include "project.h" + #include #include +#include #include -//SIMPLE TYPE +// SIMPLE TYPE -#include -#include #include - -#include "MAXex/win/CFGMacros.h" +#include +#include +#undef min +#undef max +#include "datas/flags.hpp" +#include #include -#include "HavokXMLApi.hpp" -#define HAVOKMAX_VERSION 1.10 -#define HAVOKMAX_VERSIONINT 110 +static constexpr int HAVOKMAX_VERSIONINT = + HavokMax_VERSION_MAJOR * 100 + HavokMax_VERSION_MINOR; -extern TCHAR *GetString(int id); +struct PresetData; extern HINSTANCE hInstance; -struct PresetData; +REFLECTOR_CREATE(Checked, ENUM, 2, CLASS, 8, CH_ANIMATION, CH_ANISKELETON, + CH_ANIOPTIMIZE, CH_DISABLE_SCALE); +REFLECTOR_CREATE(Visible, ENUM, 2, CLASS, 8, CH_ANISKELETON, CH_ANIOPTIMIZE, + SP_ANIEND, SP_ANISTART); -class HavokMax -{ +class HavokMax : public ReflectorInterface { public: - - enum DLGTYPE_e - { - DLGTYPE_unknown, - DLGTYPE_import, - DLGTYPE_export - }; - - enum ConfigBoolean - { - IDConfigBool(IDC_CH_ANIMATION), - IDConfigBool(IDC_CH_ANISKELETON), - IDConfigBool(IDC_CH_ANIOPTIMIZE), - IDConfigBool(IDC_CH_DISABLE_SCALE), - IDConfigVisible(IDC_CH_ANISKELETON), - IDConfigVisible(IDC_CH_ANIOPTIMIZE), - IDConfigVisible(IDC_EDIT_ANIEND), - IDConfigVisible(IDC_EDIT_ANISTART), - IDConfigVisible(IDC_SPIN_ANIEND), - IDConfigVisible(IDC_SPIN_ANISTART), - }; - - DLGTYPE_e instanceDialogType; - TSTRING currentPresetName; - HWND comboHandle; - HWND hWnd; - TSTRING cfgpath; - esFlags sanityCheck; - const TCHAR *CFGFile; - int numAnimations; - NewIDConfigIndex(IDC_EDIT_MOTIONID); - NewIDConfigValue(IDC_EDIT_SCALE); - NewIDConfigIndex(IDC_CB_TOOLSET); - NewIDConfigIndex(IDC_EDIT_ANIEND); - NewIDConfigIndex(IDC_EDIT_ANISTART); - NewIDConfigIndex(IDC_EDIT_CAPTUREFRAME); - - esFlags flags; - Matrix3 corMat; - - void LoadCFG(); - void BuildCFG(); - void SaveCFG(); - void UpdateData(); - void SetupExportUI(); - - int SavePreset(const TCHAR *presetName); - virtual int SavePreset(PresetData *presetData) = 0; - virtual void Setup(HWND hwnd); - virtual void CollisionHandler() = 0; - virtual void UpdatePresetUI(PresetData *presetData) = 0; - virtual int SanityBitcher() = 0; - virtual int SpawnImportDialog() = 0; - virtual int SpawnExportDialog() = 0; - virtual INT_PTR DlgCommandCallBack(WPARAM wParam, LPARAM lParam) = 0; - - HavokMax(); - virtual ~HavokMax() {} + enum DLGTYPE_e { DLGTYPE_unknown, DLGTYPE_import, DLGTYPE_export }; + + // config + es::Flags checked; + es::Flags visible; + int32 motionIndex; + hkToolset toolset; + TimeValue animationStart, animationEnd, captureFrame; + std::string currentPresetName; + + // preset data + float objectScale; + Matrix3 corMat; + + DLGTYPE_e instanceDialogType; + HWND comboHandle; + HWND hWnd; + es::Flags sanityCheck; + int32 numAnimations; + + void LoadCFG(); + void BuildCFG(); + void SaveCFG(); + void UpdateData(); + void SetupExportUI(); + + int SavePreset(const std::string &presetName); + virtual int SavePreset(PresetData &presetData) = 0; + virtual void Setup(HWND hwnd); + virtual void CollisionHandler() = 0; + virtual void UpdatePresetUI(PresetData &presetData) = 0; + virtual int SanityBitcher() = 0; + virtual int SpawnImportDialog() = 0; + virtual int SpawnExportDialog() = 0; + virtual INT_PTR DlgCommandCallBack(WPARAM wParam, LPARAM lParam) = 0; + + HavokMax(); + virtual ~HavokMax() {} }; -class HavokMaxV1 : public HavokMax -{ +class HavokMaxV1 : public HavokMax { public: - void CollisionHandler(); - void UpdatePresetUI(PresetData *presetData); - int SanityBitcher(); - int SpawnImportDialog(); - int SpawnExportDialog(); - INT_PTR DlgCommandCallBack(WPARAM wParam, LPARAM lParam); - int SavePreset(PresetData *presetData); + void CollisionHandler(); + void UpdatePresetUI(PresetData &presetData); + int SanityBitcher(); + int SpawnImportDialog(); + int SpawnExportDialog(); + INT_PTR DlgCommandCallBack(WPARAM wParam, LPARAM lParam); + int SavePreset(PresetData &presetData); }; -class HavokMaxV2 : public HavokMax -{ +class HavokMaxV2 : public HavokMax { public: - HWND comboRight; - HWND comboBack; - - void CollisionHandler(); - void UpdatePresetUI(PresetData *presetData); - int SanityBitcher(); - int SpawnImportDialog(); - int SpawnExportDialog(); - void Setup(HWND hwnd); - INT_PTR DlgCommandCallBack(WPARAM wParam, LPARAM lParam); - int SavePreset(PresetData *presetData); + HWND comboRight; + HWND comboBack; + + void CollisionHandler(); + void UpdatePresetUI(PresetData &presetData); + int SanityBitcher(); + int SpawnImportDialog(); + int SpawnExportDialog(); + void Setup(HWND hwnd); + INT_PTR DlgCommandCallBack(WPARAM wParam, LPARAM lParam); + int SavePreset(PresetData &presetData); }; void BuildHavokResources(); void DestroyHavokResources(); -void Rescan(); void ShowAboutDLG(HWND hWnd); -extern std::vector extensions; +extern std::set extensions; diff --git a/src/HavokMax.rc b/src/HavokMax.rc index 2731f4c..bd4b76f 100644 --- a/src/HavokMax.rc +++ b/src/HavokMax.rc @@ -7,7 +7,7 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" +#include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS