├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.md ├── cmake ├── version.rc.in └── x64-windows-skse.cmake ├── include ├── AnimMotionHandler.h ├── Hooks.h ├── Motion.h ├── RE │ ├── B │ │ ├── bhkCharacterController.h │ │ ├── bhkCharacterMoveInfo.h │ │ ├── bhkCharacterState.h │ │ ├── bhkCharacterStateClimbing.h │ │ ├── bhkCharacterStateFlying.h │ │ ├── bhkCharacterStateInAir.h │ │ ├── bhkCharacterStateJumping.h │ │ ├── bhkCharacterStateOnGround.h │ │ ├── bhkCharacterStateSwimming.h │ │ └── bhkWorld.h │ ├── H │ │ ├── hkClass.h │ │ ├── hkQsTransform.h │ │ ├── hkVector4.h │ │ ├── hkaAnimatedReferenceFrame.h │ │ ├── hkaAnimation.h │ │ ├── hkaAnimationBinding.h │ │ ├── hkaAnimationControl.h │ │ ├── hkaAnnotationTrack.h │ │ ├── hkaDefaultAnimationControl.h │ │ ├── hkaSplineCompressedAnimation.h │ │ ├── hkbBehaviorGraph.h │ │ ├── hkbClipGenerator.h │ │ ├── hkbContext.h │ │ ├── hkbGenerator.h │ │ ├── hkbNode.h │ │ ├── hkbStateMachine.h │ │ └── hkpCharacterMovementUtil.h │ ├── M │ │ └── MotionDataContainer.h │ └── RTTI.h ├── Settings.h ├── pch.h └── utils │ ├── INISettingCollection.h │ ├── Logger.h │ ├── Setting.h │ ├── Trampoline.h │ └── half.h ├── source ├── Hooks.cpp ├── Settings.cpp ├── main.cpp ├── pch.cpp └── utils │ └── INISettingCollection.cpp ├── vcpkg-configuration.json ├── vcpkg.json └── version.rc.in /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: -4 3 | AlignAfterOpenBracket: DontAlign 4 | AlignConsecutiveAssignments: 'false' 5 | AlignConsecutiveDeclarations: 'false' 6 | AlignConsecutiveMacros: 'false' 7 | AlignEscapedNewlines: Left 8 | AlignOperands: 'true' 9 | AlignTrailingComments: 'true' 10 | AllowAllArgumentsOnNextLine: 'false' 11 | AllowAllConstructorInitializersOnNextLine: 'false' 12 | AllowAllParametersOfDeclarationOnNextLine: 'false' 13 | AllowShortBlocksOnASingleLine: Empty 14 | AllowShortCaseLabelsOnASingleLine: 'false' 15 | AllowShortFunctionsOnASingleLine: All 16 | AllowShortIfStatementsOnASingleLine: Never 17 | AllowShortLambdasOnASingleLine: All 18 | AllowShortLoopsOnASingleLine: 'true' 19 | AlwaysBreakAfterReturnType: None 20 | AlwaysBreakBeforeMultilineStrings: 'true' 21 | AlwaysBreakTemplateDeclarations: 'Yes' 22 | BinPackArguments: 'true' 23 | BinPackParameters: 'true' 24 | BraceWrapping: 25 | AfterCaseLabel: 'true' 26 | AfterClass: 'true' 27 | AfterControlStatement: 'false' 28 | AfterEnum: 'true' 29 | AfterExternBlock: 'true' 30 | AfterFunction: 'true' 31 | AfterNamespace: 'true' 32 | AfterStruct: 'true' 33 | AfterUnion: 'true' 34 | BeforeCatch: 'false' 35 | BeforeElse: 'false' 36 | IndentBraces: 'false' 37 | SplitEmptyFunction: 'false' 38 | SplitEmptyNamespace: 'false' 39 | SplitEmptyRecord: 'false' 40 | BreakBeforeBinaryOperators: None 41 | BreakBeforeBraces: Custom 42 | BreakBeforeTernaryOperators: 'false' 43 | BreakConstructorInitializers: AfterColon 44 | BreakInheritanceList: AfterColon 45 | BreakStringLiterals: 'true' 46 | ColumnLimit: 0 47 | CompactNamespaces: 'false' 48 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 49 | ConstructorInitializerIndentWidth: 4 50 | ContinuationIndentWidth: 4 51 | Cpp11BracedListStyle: 'false' 52 | DeriveLineEnding: 'true' 53 | DerivePointerAlignment: 'false' 54 | DisableFormat: 'false' 55 | FixNamespaceComments: 'false' 56 | IncludeBlocks: Preserve 57 | IndentCaseBlocks: 'true' 58 | IndentCaseLabels: 'false' 59 | IndentGotoLabels: 'false' 60 | IndentPPDirectives: None 61 | IndentWidth: 4 62 | IndentWrappedFunctionNames: 'true' 63 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 64 | Language: Cpp 65 | MaxEmptyLinesToKeep: 2 66 | NamespaceIndentation: All 67 | PointerAlignment: Left 68 | ReflowComments : 'false' 69 | SortIncludes: 'true' 70 | SortUsingDeclarations: 'true' 71 | SpaceAfterCStyleCast: 'false' 72 | SpaceAfterLogicalNot: 'false' 73 | SpaceAfterTemplateKeyword: 'true' 74 | SpaceBeforeAssignmentOperators: 'true' 75 | SpaceBeforeCpp11BracedList: 'false' 76 | SpaceBeforeCtorInitializerColon: 'true' 77 | SpaceBeforeInheritanceColon: 'true' 78 | SpaceBeforeParens: ControlStatements 79 | SpaceBeforeRangeBasedForLoopColon: 'true' 80 | SpaceBeforeSquareBrackets: 'false' 81 | SpaceInEmptyBlock: 'false' 82 | SpaceInEmptyParentheses: 'false' 83 | SpacesBeforeTrailingComments: 2 84 | SpacesInAngles: 'false' 85 | SpacesInCStyleCastParentheses: 'false' 86 | SpacesInConditionalStatement: 'false' 87 | SpacesInContainerLiterals: 'true' 88 | SpacesInParentheses: 'false' 89 | SpacesInSquareBrackets: 'false' 90 | Standard: c++17 91 | TabWidth: 4 92 | UseCRLF: 'true' 93 | UseTab: Always 94 | 95 | ... 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vs/ 3 | x64/ 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/CommonLibSSE"] 2 | path = extern/CommonLibSSE 3 | url = https://github.com/alexsylex/CommonLibSSE 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | ######################################################################################################################## 4 | ## Define project 5 | ######################################################################################################################## 6 | 7 | project( 8 | AnimationMotionRevolution 9 | VERSION 1.5.3 10 | LANGUAGES CXX 11 | ) 12 | 13 | set(CMAKE_CXX_STANDARD 23) 14 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 15 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) 16 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DEBUG OFF) 17 | 18 | configure_file( 19 | ${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in 20 | ${CMAKE_CURRENT_BINARY_DIR}/version.rc 21 | @ONLY 22 | ) 23 | 24 | set(headers 25 | include/RE/B/bhkCharacterController.h 26 | include/RE/B/bhkCharacterMoveInfo.h 27 | include/RE/B/bhkCharacterState.h 28 | include/RE/B/bhkCharacterStateClimbing.h 29 | include/RE/B/bhkCharacterStateFlying.h 30 | include/RE/B/bhkCharacterStateInAir.h 31 | include/RE/B/bhkCharacterStateJumping.h 32 | include/RE/B/bhkCharacterStateOnGround.h 33 | include/RE/B/bhkCharacterStateSwimming.h 34 | include/RE/B/bhkWorld.h 35 | include/RE/H/hkaAnimatedReferenceFrame.h 36 | include/RE/H/hkaAnimation.h 37 | include/RE/H/hkaAnimationBinding.h 38 | include/RE/H/hkaAnimationControl.h 39 | include/RE/H/hkaAnnotationTrack.h 40 | include/RE/H/hkaDefaultAnimationControl.h 41 | include/RE/H/hkaSplineCompressedAnimation.h 42 | include/RE/H/hkbBehaviorGraph.h 43 | include/RE/H/hkbContext.h 44 | include/RE/H/hkbClipGenerator.h 45 | include/RE/H/hkbGenerator.h 46 | include/RE/H/hkbNode.h 47 | include/RE/H/hkbStateMachine.h 48 | include/RE/H/hkClass.h 49 | include/RE/H/hkpCharacterMovementUtil.h 50 | include/RE/H/hkQsTransform.h 51 | include/RE/H/hkVector4.h 52 | include/RE/M/MotionDataContainer.h 53 | include/RE/RTTI.h 54 | include/utils/Trampoline.h 55 | include/utils/INISettingCollection.h 56 | include/utils/half.h 57 | include/utils/Logger.h 58 | include/utils/Setting.h 59 | include/AnimMotionHandler.h 60 | include/Hooks.h 61 | include/Motion.h 62 | include/pch.h 63 | include/Settings.h 64 | ) 65 | 66 | set(sources 67 | source/utils/INISettingCollection.cpp 68 | source/Hooks.cpp 69 | source/main.cpp 70 | source/pch.cpp 71 | source/Settings.cpp 72 | ) 73 | 74 | source_group( 75 | TREE 76 | ${CMAKE_CURRENT_SOURCE_DIR} 77 | FILES 78 | ${headers} 79 | ${sources} 80 | ) 81 | 82 | ######################################################################################################################## 83 | ## Configure target DLL 84 | ######################################################################################################################## 85 | 86 | find_package(CommonLibSSE CONFIG REQUIRED) 87 | find_package(binary_io REQUIRED CONFIG) 88 | find_package(spdlog REQUIRED CONFIG) 89 | 90 | add_commonlibsse_plugin(${PROJECT_NAME} SOURCES ${headers} ${sources}) 91 | add_library("${PROJECT_NAME}::${PROJECT_NAME}" ALIAS "${PROJECT_NAME}") 92 | 93 | target_include_directories( 94 | ${PROJECT_NAME} 95 | PRIVATE 96 | ${CMAKE_CURRENT_SOURCE_DIR}/include 97 | ) 98 | 99 | target_link_libraries( 100 | ${PROJECT_NAME} 101 | PRIVATE 102 | CommonLibSSE::CommonLibSSE 103 | spdlog::spdlog_header_only 104 | ) 105 | 106 | if (MSVC) 107 | target_link_options( 108 | ${PROJECT_NAME} 109 | PRIVATE 110 | "$<$:/INCREMENTAL;/OPT:NOREF;/OPT:NOICF>" 111 | "$<$:/INCREMENTAL:NO;/OPT:REF;/OPT:ICF>" 112 | ) 113 | endif() 114 | 115 | target_precompile_headers( 116 | ${PROJECT_NAME} 117 | PRIVATE 118 | include/PCH.h 119 | ) 120 | 121 | ######################################################################################################################## 122 | ## Automatic plugin deployment 123 | ######################################################################################################################## 124 | 125 | set(SKYRIM_SE_DIR "C:/Program Files (x86)/Steam/steamapps/common/Skyrim Special Edition") 126 | set(SKYRIM_VR_DIR "C:/Program Files (x86)/Steam/steamapps/common/Skyrim VR") 127 | set(SKYRIM_AE_1_6_353_DIR "C:/Program Files (x86)/Steam/steamapps/common/Skyrim Anniversary Edition 1.6.353") 128 | set(SKYRIM_AE_1_6_640_DIR "C:/Program Files (x86)/Steam/steamapps/common/Skyrim Anniversary Edition 1.6.640") 129 | 130 | if ("$ENV{RUNTIME_DISABLE_FLAGS}" STREQUAL "") 131 | set(COPY_SE_AE YES) 132 | set(COPY_VR YES) 133 | elseif ("$ENV{RUNTIME_DISABLE_FLAGS}" STREQUAL "-UENABLE_SKYRIM_VR") 134 | set(COPY_SE_AE YES) 135 | set(COPY_VR NO) 136 | elseif ("$ENV{RUNTIME_DISABLE_FLAGS}" STREQUAL "-UENABLE_SKYRIM_SE -UENABLE_SKYRIM_AE") 137 | set(COPY_SE_AE NO) 138 | set(COPY_VR YES) 139 | endif() 140 | 141 | if (COPY_SE_AE) 142 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 143 | COMMAND ${CMAKE_COMMAND} -E copy $ 144 | "${SKYRIM_SE_DIR}/Data/SKSE/Plugins/$" 145 | COMMAND ${CMAKE_COMMAND} -E copy $ 146 | "${SKYRIM_AE_1_6_353_DIR}/Data/SKSE/Plugins/$" 147 | COMMAND ${CMAKE_COMMAND} -E copy $ 148 | "${SKYRIM_AE_1_6_640_DIR}/Data/SKSE/Plugins/$" 149 | ) 150 | endif() 151 | if (COPY_VR) 152 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 153 | COMMAND ${CMAKE_COMMAND} -E copy $ 154 | "${SKYRIM_VR_DIR}/Data/SKSE/Plugins/$" 155 | ) 156 | endif() 157 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 21, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "base", 11 | "hidden": true, 12 | "cacheVariables": { 13 | "CMAKE_CXX_FLAGS": "$env{COMMONLIBSSE_COMPILER} $env{COMMONLIBSSE_PLATFORM} $env{COMMONLIBSSE_TEXT} $env{RUNTIME_DISABLE_FLAGS}" 14 | } 15 | }, 16 | { 17 | "name": "vcpkg", 18 | "hidden": true, 19 | "cacheVariables": { 20 | "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", 21 | "VCPKG_TARGET_TRIPLET": "x64-windows-skse", 22 | "VCPKG_HOST_TRIPLET": "x64-windows-skse", 23 | "VCPKG_OVERLAY_TRIPLETS": "${sourceDir}/cmake", 24 | "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>DLL" 25 | } 26 | }, 27 | { 28 | "name": "win32", 29 | "hidden": true, 30 | "environment": { 31 | "COMMONLIBSSE_PLATFORM": "-DWIN32_LEAN_AND_MEAN -DNOMINMAX" 32 | } 33 | }, 34 | { 35 | "name": "win32-unicode", 36 | "hidden": true, 37 | "inherits": "win32", 38 | "environment": { 39 | "COMMONLIBSSE_TEXT": "-DUNICODE -D_UNICODE" 40 | } 41 | }, 42 | { 43 | "name": "x64", 44 | "hidden": true, 45 | "architecture": { 46 | "value": "x64", 47 | "strategy": "external" 48 | } 49 | }, 50 | { 51 | "name": "msvc", 52 | "hidden": true, 53 | "vendor": { 54 | "microsoft.com/VisualStudioSettings/CMake/1.0": { 55 | "intelliSenseMode": "windows-msvc-x64", 56 | "enableMicrosoftCodeAnalysis": true, 57 | "enableClangTidyCodeAnalysis": true 58 | } 59 | } 60 | }, 61 | { 62 | "name": "build-debug", 63 | "hidden": true, 64 | "inherits": [ 65 | "base", 66 | "vcpkg", 67 | "win32-unicode", 68 | "x64", 69 | "msvc" 70 | ], 71 | "generator": "Ninja", 72 | "cacheVariables": { 73 | "CMAKE_BUILD_TYPE": { 74 | "type": "STRING", 75 | "value": "Debug" 76 | } 77 | } 78 | }, 79 | { 80 | "name": "build-relwithdebinfo", 81 | "hidden": true, 82 | "inherits": [ 83 | "base", 84 | "vcpkg", 85 | "win32-unicode", 86 | "x64", 87 | "msvc" 88 | ], 89 | "generator": "Ninja", 90 | "cacheVariables": { 91 | "CMAKE_BUILD_TYPE": { 92 | "type": "STRING", 93 | "value": "RelWithDebInfo" 94 | } 95 | } 96 | }, 97 | { 98 | "name": "all", 99 | "hidden": true, 100 | "environment": { 101 | "RUNTIME_DISABLE_FLAGS": "", 102 | "COMMONLIBSSE_COMPILER": "/permissive- /Zc:preprocessor /EHsc $penv{CXXFLAGS} -DENABLE_COMMONLIBSSE_TESTING $env{RUNTIME_DISABLE_FLAGS}" 103 | } 104 | }, 105 | { 106 | "name": "se-ae", 107 | "hidden": true, 108 | "environment": { 109 | "RUNTIME_DISABLE_FLAGS": "-UENABLE_SKYRIM_VR", 110 | "COMMONLIBSSE_COMPILER": "/permissive- /Zc:preprocessor /EHsc $penv{CXXFLAGS} -DENABLE_COMMONLIBSSE_TESTING $env{RUNTIME_DISABLE_FLAGS}" 111 | } 112 | }, 113 | { 114 | "name": "vr", 115 | "hidden": true, 116 | "environment": { 117 | "RUNTIME_DISABLE_FLAGS": "-UENABLE_SKYRIM_SE -UENABLE_SKYRIM_AE", 118 | "COMMONLIBSSE_COMPILER": "/permissive- /Zc:preprocessor /EHsc $penv{CXXFLAGS} -DENABLE_COMMONLIBSSE_TESTING $env{RUNTIME_DISABLE_FLAGS}" 119 | } 120 | }, 121 | { 122 | "name": "build-debug-all", 123 | "inherits": [ 124 | "build-debug", 125 | "all" 126 | ], 127 | "displayName": "Debug", 128 | "binaryDir": "${sourceDir}/build/debug" 129 | }, 130 | { 131 | "name": "build-relwithdebinfo-all", 132 | "inherits": [ 133 | "build-relwithdebinfo", 134 | "all" 135 | ], 136 | "displayName": "RelWithDebInfo", 137 | "binaryDir": "${sourceDir}/build/relwithdebinfo" 138 | }, 139 | { 140 | "name": "build-debug-se-ae", 141 | "inherits": [ 142 | "build-debug", 143 | "se-ae" 144 | ], 145 | "displayName": "Debug (SE-AE only)", 146 | "binaryDir": "${sourceDir}/build/debug-se-ae-only" 147 | }, 148 | { 149 | "name": "build-relwithdebinfo-se-ae", 150 | "inherits": [ 151 | "build-relwithdebinfo", 152 | "se-ae" 153 | ], 154 | "displayName": "RelWithDebInfo (SE-AE only)", 155 | "binaryDir": "${sourceDir}/build/relwithdebinfo-se-ae-only" 156 | }, 157 | { 158 | "name": "build-debug-vr", 159 | "inherits": [ 160 | "build-debug", 161 | "vr" 162 | ], 163 | "displayName": "Debug (VR only)", 164 | "binaryDir": "${sourceDir}/build/debug-vr-only" 165 | }, 166 | { 167 | "name": "build-relwithdebinfo-vr", 168 | "inherits": [ 169 | "build-relwithdebinfo", 170 | "vr" 171 | ], 172 | "displayName": "RelWithDebInfo (VR only)", 173 | "binaryDir": "${sourceDir}/build/relwithdebinfo-vr-only" 174 | } 175 | ], 176 | "buildPresets": [ 177 | { 178 | "name": "debug-all", 179 | "displayName": "Debug", 180 | "configurePreset": "build-debug-all" 181 | }, 182 | { 183 | "name": "relwithdebinfo-all", 184 | "displayName": "RelWithDebInfo", 185 | "configurePreset": "build-relwithdebinfo-all" 186 | }, 187 | { 188 | "name": "debug-se-ae", 189 | "displayName": "Debug (SE-AE only)", 190 | "configurePreset": "build-debug-se-ae" 191 | }, 192 | { 193 | "name": "relwithdebinfo-se-ae", 194 | "displayName": "RelWithDebInfo (SE-AE only)", 195 | "configurePreset": "build-relwithdebinfo-se-ae" 196 | }, 197 | { 198 | "name": "debug-vr", 199 | "displayName": "Debug (VR only)", 200 | "configurePreset": "build-debug-vr" 201 | }, 202 | { 203 | "name": "relwithdebinfo-vr", 204 | "displayName": "RelWithDebInfo (VR only)", 205 | "configurePreset": "build-relwithdebinfo-vr" 206 | } 207 | ] 208 | } 209 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Alejandro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Animation Motion Revolution 2 | 3 | https://www.nexusmods.com/skyrimspecialedition/mods/50258 4 | -------------------------------------------------------------------------------- /cmake/version.rc.in: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 1 VERSIONINFO 4 | FILEVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 5 | PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 6 | FILEFLAGSMASK 0x17L 7 | #ifdef _DEBUG 8 | FILEFLAGS 0x1L 9 | #else 10 | FILEFLAGS 0x0L 11 | #endif 12 | FILEOS 0x4L 13 | FILETYPE 0x1L 14 | FILESUBTYPE 0x0L 15 | BEGIN 16 | BLOCK "StringFileInfo" 17 | BEGIN 18 | BLOCK "040904b0" 19 | BEGIN 20 | VALUE "FileDescription", "@PROJECT_NAME@" 21 | VALUE "FileVersion", "@PROJECT_VERSION@.0" 22 | VALUE "InternalName", "@PROJECT_NAME@" 23 | VALUE "LegalCopyright", "MIT License" 24 | VALUE "ProductName", "@PROJECT_NAME@" 25 | VALUE "ProductVersion", "@PROJECT_VERSION@.0" 26 | END 27 | END 28 | BLOCK "VarFileInfo" 29 | BEGIN 30 | VALUE "Translation", 0x409, 1200 31 | END 32 | END 33 | -------------------------------------------------------------------------------- /cmake/x64-windows-skse.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | 4 | if (${PORT} MATCHES "fully-dynamic-game-engine|skse|qt*") 5 | set(VCPKG_LIBRARY_LINKAGE dynamic) 6 | else () 7 | set(VCPKG_LIBRARY_LINKAGE static) 8 | endif () 9 | -------------------------------------------------------------------------------- /include/AnimMotionHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Motion.h" 4 | 5 | class AnimMotionData 6 | { 7 | public: 8 | 9 | AnimMotionData() = default; 10 | 11 | AnimMotionData(const RE::hkaAnimation* a_animation, const Translation* a_translation) : 12 | animation{ a_animation }, translationList{ *a_translation } 13 | { } 14 | 15 | AnimMotionData(const RE::hkaAnimation* a_animation, const Rotation* a_rotation) : 16 | animation{ a_animation }, rotationList{ *a_rotation } 17 | { } 18 | 19 | void Add(const Translation* a_translation) 20 | { 21 | translationList.push_back(*a_translation); 22 | } 23 | 24 | void Add(const Rotation* a_rotation) 25 | { 26 | rotationList.push_back(*a_rotation); 27 | } 28 | 29 | void SortListsByTime() 30 | { 31 | if (!translationList.empty()) 32 | { 33 | std::sort(translationList.begin(), translationList.end(), 34 | [](const Translation& a_lhs, const Translation& a_rhs) -> bool 35 | { 36 | return a_lhs.time < a_rhs.time; 37 | }); 38 | } 39 | 40 | if (!rotationList.empty()) 41 | { 42 | std::sort(rotationList.begin(), rotationList.end(), 43 | [](const Rotation& a_lhs, const Rotation& a_rhs) -> bool 44 | { 45 | return a_lhs.time < a_rhs.time; 46 | }); 47 | } 48 | } 49 | 50 | const RE::hkaAnimation* animation = nullptr; 51 | std::vector translationList; 52 | std::vector rotationList; 53 | std::int32_t activeCount = 1; 54 | }; -------------------------------------------------------------------------------- /include/Hooks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/Trampoline.h" 4 | 5 | #include "RE/B/bhkCharacterMoveInfo.h" 6 | #include "RE/B/BShkbAnimationGraph.h" 7 | #include "RE/H/hkbClipGenerator.h" 8 | #include "RE/H/hkbContext.h" 9 | 10 | #include "RE/M/MotionDataContainer.h" 11 | 12 | namespace hooks 13 | { 14 | class hkbClipGenerator 15 | { 16 | static constexpr REL::RelocationID ActivateId{ 58602, 59252 }; 17 | static constexpr REL::RelocationID DeactivateId{ 58604, 59254 }; 18 | 19 | public: 20 | 21 | static inline REL::Relocation Activate{ ActivateId }; 22 | static inline REL::Relocation ComputeStartTime; 23 | static std::uint32_t ComputeStartTime_Hook(const RE::hkbClipGenerator* a_this, const RE::hkbContext* a_context); 24 | 25 | static inline REL::Relocation Deactivate{ DeactivateId }; 26 | static inline REL::Relocation ResetIgnoreStartTime; 27 | static void ResetIgnoreStartTime_Hook(const RE::hkbClipGenerator* a_this, const RE::hkbContext* a_hkbContext); 28 | 29 | private: 30 | 31 | static const RE::hkaAnimation* GetBoundAnimation(const RE::hkbClipGenerator* a_this) 32 | { 33 | return a_this->binding? (a_this->binding->animation? a_this->binding->animation.get() : nullptr) : nullptr; 34 | } 35 | }; 36 | 37 | class MotionDataContainer 38 | { 39 | static constexpr REL::RelocationID ProcessTranslationDataId{ 31812, 32582 }; 40 | static constexpr REL::RelocationID ProcessRotationDataId{ 31813, 32583 }; 41 | static constexpr REL::RelocationID InterpolateRotationId{ 69459, 70836 }; 42 | 43 | public: 44 | 45 | // Translation 46 | static inline REL::Relocation ProcessTranslationData{ ProcessTranslationDataId }; 47 | static void ProcessTranslationData_Hook(RE::MotionDataContainer* a_this, float a_motionTime, RE::NiPoint3& a_translation, 48 | const RE::BSFixedString* a_clipName, RE::Character* a_character); 49 | 50 | // Rotation 51 | static inline REL::Relocation ProcessRotationData{ ProcessRotationDataId }; 52 | static inline REL::Relocation InterpolateRotation{ InterpolateRotationId }; 53 | static void ProcessRotationData_Hook(RE::MotionDataContainer* a_this, float a_motionTime, RE::NiQuaternion& a_rotation, 54 | const RE::BSFixedString* a_clipName, RE::Character* a_character); 55 | 56 | private: 57 | 58 | static RE::hkbCharacter* GethkbCharacter(RE::Character* a_character) 59 | { 60 | RE::BSAnimationGraphManagerPtr animGraph; 61 | 62 | a_character->GetAnimationGraphManager(animGraph); 63 | 64 | if (animGraph) 65 | { 66 | RE::BShkbAnimationGraphPtr activeGraph = animGraph->graphs[animGraph->GetRuntimeData().activeGraph]; 67 | 68 | if (activeGraph) 69 | { 70 | return &activeGraph->characterInstance; 71 | } 72 | } 73 | 74 | return nullptr; 75 | }; 76 | }; 77 | 78 | class Character 79 | { 80 | static constexpr REL::RelocationID ProcessMotionDataId{ 31949, 32703 }; 81 | 82 | public: 83 | 84 | static inline REL::Relocation ProcessMotionData{ ProcessMotionDataId }; 85 | }; 86 | 87 | class bhkCharacterStateOnGround 88 | { 89 | public: 90 | 91 | static inline REL::Relocation vTable{ RE::VTABLE_bhkCharacterStateOnGround[0] }; 92 | 93 | static inline REL::Relocation SimulateStatePhysics; 94 | }; 95 | 96 | class bhkCharacterController 97 | { 98 | static constexpr REL::RelocationID UpdateStateAndMovementId{ 76436, 32703 }; 99 | 100 | public: 101 | 102 | static inline REL::Relocation 103 | UpdateStateAndMovement{ UpdateStateAndMovementId }; 104 | }; 105 | 106 | class hkpCharacterContext 107 | { 108 | public: 109 | 110 | static RE::hkpCharacterStateType GetCharacterState_Hook(RE::hkpCharacterContext* a_this); 111 | }; 112 | 113 | void SimulateStatePhysics(RE::bhkCharacterStateOnGround* a_this, RE::bhkCharacterController* a_characterController); 114 | 115 | static inline void Install() 116 | { 117 | bhkCharacterStateOnGround::SimulateStatePhysics = bhkCharacterStateOnGround::vTable.write_vfunc(8, SimulateStatePhysics); 118 | 119 | // bhkCharacterController::UpdateStateAndMovement 120 | { 121 | static std::uintptr_t hookedAddress = bhkCharacterController::UpdateStateAndMovement.address() + 0x7D1; 122 | 123 | utils::AllocExactSizeTrampoline<5>(); 124 | SKSE::GetTrampoline().write_call<5>(hookedAddress, &hkpCharacterContext::GetCharacterState_Hook); 125 | } 126 | 127 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 128 | 129 | // My guess is that Bethesda used compiled Havok libraries, that is why neither registers 130 | // nor offsets changed between SE-AE for the hkbClipGenerator hooks 131 | 132 | // hkbClipGenerator::Activate 133 | { 134 | static std::uintptr_t hookedAddress = hkbClipGenerator::Activate.address() + 0x66E; 135 | 136 | struct Hook : Xbyak::CodeGenerator 137 | { 138 | Hook() 139 | { 140 | Xbyak::Label hookLabel; 141 | Xbyak::Label retnLabel; 142 | 143 | mov(rdx, r15); // r15 = hkbContext* 144 | call(ptr[rip + hookLabel]); 145 | 146 | jmp(ptr[rip + retnLabel]); 147 | 148 | L(hookLabel), dq(reinterpret_cast(&hkbClipGenerator::ComputeStartTime_Hook)); 149 | L(retnLabel), dq(hookedAddress + 5); 150 | } 151 | }; 152 | 153 | hkbClipGenerator::ComputeStartTime = utils::WriteBranchTrampoline<5>(hookedAddress, Hook()); 154 | } 155 | 156 | // hkbClipGenerator::Deactivate 157 | { 158 | static std::uintptr_t hookedAddress = hkbClipGenerator::Deactivate.address() + 0x1A; 159 | 160 | struct Hook : Xbyak::CodeGenerator 161 | { 162 | Hook() 163 | { 164 | Xbyak::Label hookLabel; 165 | Xbyak::Label retnLabel; 166 | 167 | mov(rdx, r14); // r14 = hkbContext* 168 | call(ptr[rip + hookLabel]); 169 | 170 | jmp(ptr[rip + retnLabel]); 171 | 172 | L(hookLabel), dq(reinterpret_cast(&hkbClipGenerator::ResetIgnoreStartTime_Hook)); 173 | L(retnLabel), dq(hookedAddress + 5); 174 | } 175 | }; 176 | 177 | hkbClipGenerator::ResetIgnoreStartTime = utils::WriteBranchTrampoline<5>(hookedAddress, Hook()); 178 | } 179 | 180 | // Character::ProcessMotionData 181 | { 182 | static std::uintptr_t translation1HookedAddress = Character::ProcessMotionData.address() + (!REL::Module::IsAE() ? 0x28D : 0x298); 183 | static std::uintptr_t translation2HookedAddress = Character::ProcessMotionData.address() + (!REL::Module::IsAE() ? 0x2A1 : 0x2AA); 184 | static std::uintptr_t rotation1HookedAddress = Character::ProcessMotionData.address() + (!REL::Module::IsAE() ? 0x355 : 0x35C); 185 | static std::uintptr_t rotation2HookedAddress = Character::ProcessMotionData.address() + (!REL::Module::IsAE() ? 0x368 : 0x36D); 186 | 187 | struct Hook : Xbyak::CodeGenerator 188 | { 189 | Hook(void* a_func) 190 | { 191 | Xbyak::Label hookLabel; 192 | 193 | sub(rsp, 0x28); 194 | 195 | mov(ptr[rsp + 0x20], r13); // r13 = Character* 196 | mov(r9, rbx); // SE: rbx = BSFixedString* 197 | if (REL::Module::IsAE()) 198 | { 199 | sub(r9, 0x14); // AE: rbx - 0x14 = BSFixedString* 200 | } 201 | call(ptr[rip + hookLabel]); 202 | 203 | add(rsp, 0x28); 204 | 205 | ret(); 206 | 207 | L(hookLabel), dq(reinterpret_cast(a_func)); 208 | } 209 | }; 210 | 211 | // Translation 212 | utils::WriteCallTrampoline<5>(translation1HookedAddress, Hook(&MotionDataContainer::ProcessTranslationData_Hook)); 213 | utils::WriteCallTrampoline<5>(translation2HookedAddress, Hook(&MotionDataContainer::ProcessTranslationData_Hook)); 214 | 215 | //// Rotation 216 | utils::WriteCallTrampoline<5>(rotation1HookedAddress, Hook(&MotionDataContainer::ProcessRotationData_Hook)); 217 | utils::WriteCallTrampoline<5>(rotation2HookedAddress, Hook(&MotionDataContainer::ProcessRotationData_Hook)); 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /include/Motion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | struct Motion 9 | { 10 | float time; 11 | T delta; 12 | }; 13 | 14 | using Translation = Motion; 15 | using Rotation = Motion; 16 | 17 | // Annotations we are looking for contain translation/rotation data 18 | static std::variant 19 | ParseAnnotation(const RE::hkaAnnotationTrack::Annotation& a_annotation) 20 | { 21 | constexpr std::string_view animmotionPrefix = "animmotion "; 22 | constexpr std::string_view animrotationPrefix = "animrotation "; 23 | 24 | std::string_view text{ a_annotation.text.c_str() }; 25 | 26 | if (text.starts_with(animmotionPrefix)) 27 | { 28 | std::string_view start = text.substr(animmotionPrefix.size()); 29 | std::string_view end = start.substr(start.find(' ') + 1); 30 | 31 | RE::NiPoint3 translation; 32 | 33 | std::from_chars(start.data(), end.data(), translation.x); 34 | 35 | start = end; 36 | end = start.substr(start.find(' ') + 1); 37 | 38 | std::from_chars(start.data(), end.data(), translation.y); 39 | 40 | start = end; 41 | end = start.substr(start.size()); 42 | 43 | std::from_chars(start.data(), end.data(), translation.z); 44 | 45 | return Translation{ a_annotation.time, translation }; 46 | } 47 | else if (text.starts_with(animrotationPrefix)) 48 | { 49 | std::string_view start = text.substr(animrotationPrefix.size()); 50 | std::string_view end = start.substr(start.size()); 51 | 52 | float yawDegrees; 53 | std::from_chars(start.data(), end.data(), yawDegrees); 54 | 55 | float roll = 0.0f; 56 | float pitch = 0.0f; 57 | float yaw = yawDegrees * std::numbers::pi_v / 180.0f; 58 | 59 | RE::NiQuaternion rotation 60 | { 61 | .w = std::cos(roll / 2) * std::cos(pitch / 2) * std::cos(yaw / 2) + std::sin(roll / 2) * std::sin(pitch / 2) * std::sin(yaw / 2), 62 | .x = std::sin(roll / 2) * std::cos(pitch / 2) * std::cos(yaw / 2) - std::cos(roll / 2) * std::sin(pitch / 2) * std::sin(yaw / 2), 63 | .y = std::cos(roll / 2) * std::sin(pitch / 2) * std::cos(yaw / 2) + std::sin(roll / 2) * std::cos(pitch / 2) * std::sin(yaw / 2), 64 | .z = std::cos(roll / 2) * std::cos(pitch / 2) * std::sin(yaw / 2) - std::sin(roll / 2) * std::sin(pitch / 2) * std::cos(yaw / 2) 65 | }; 66 | 67 | return Rotation{ a_annotation.time, rotation }; 68 | } 69 | 70 | return { }; 71 | } -------------------------------------------------------------------------------- /include/RE/B/bhkCharacterController.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/B/BSBound.h" 4 | #include "RE/B/BSTEvent.h" 5 | #include "RE/B/BSTHashMap.h" 6 | #include "RE/H/hkRefPtr.h" 7 | #include "RE/H/hkStepInfo.h" 8 | #include "RE/H/hkVector4.h" 9 | #include "RE/H/hkpCharacterContext.h" 10 | #include "RE/H/hkpCharacterControl.h" 11 | #include "RE/H/hkpCharacterState.h" 12 | #include "RE/N/NiPoint3.h" 13 | #include "RE/N/NiRefObject.h" 14 | #include "RE/N/NiSmartPointer.h" 15 | 16 | namespace RE 17 | { 18 | class bhkCharacterMoveFinishEvent; 19 | class bhkCharacterState; 20 | class bhkICharOrientationController; 21 | class bhkShape; 22 | class hkpRigidBody; 23 | class hkTransform; 24 | class hkVector4; 25 | 26 | enum class CHARACTER_FLAGS 27 | { 28 | kNone = 0, 29 | kQuadruped = 1 << 0, 30 | kNoGravityOnGround = 1 << 1, 31 | kTryStep = 1 << 2, 32 | kNoFriction = 1 << 3, 33 | kAllowJumpNoContact = 1 << 4, 34 | kStuckQuad = 1 << 5, 35 | kAnimAngleMod = 1 << 6, 36 | kHitDamage = 1 << 7, 37 | kHitFlags = 1 << 7, 38 | kSupport = 1 << 8, 39 | kHasPotentialSupportManifold = 1 << 9, 40 | kCanJump = 1 << 10, 41 | kChaseBip = 1 << 11, 42 | kFollowRagdoll = 1 << 12, 43 | kJumping = 1 << 13, 44 | kNotPushable = 1 << 14, 45 | kFloatLand = 1 << 15, 46 | kCheckSupport = 1 << 16, 47 | kNoSim = 1 << 17, 48 | kFarAway = 1 << 18, 49 | kOnStilts = 1 << 19, 50 | kQuickSimulate = 1 << 20, 51 | kRecordHits = 1 << 21, 52 | kComputeTiltPreIntegrate = 1 << 22, 53 | kShouldersUnderWater = 1 << 23, 54 | kOnStairs = 1 << 24, 55 | kCanPitch = 1 << 25, 56 | kCanRoll = 1 << 26, 57 | kNoCharacterCollisions = 1 << 27, 58 | kNotPushablePermanent = 1 << 28, 59 | kPossiblePathObstacle = 1 << 29, 60 | kShapeRequiresZRot = 1 << 30, 61 | kSwimAtWaterSurface = 1 << 31, 62 | }; 63 | 64 | class bhkCharacterController : 65 | public NiRefObject, // 000 66 | public BSTEventSource // 010 67 | { 68 | public: 69 | inline static constexpr auto RTTI = RTTI_bhkCharacterController; 70 | 71 | ~bhkCharacterController() override; // 00 72 | 73 | // add 74 | virtual void GetPositionImpl(hkVector4& a_pos, bool a_applyCenterOffset) const = 0; // 02 75 | virtual void SetPositionImpl(const hkVector4& a_pos, bool a_applyCenterOffset, bool a_forceWarp) = 0; // 03 76 | virtual void GetTransformImpl(hkTransform& a_tranform) const = 0; // 04 77 | virtual void SetTransformImpl(const hkTransform& a_tranform) = 0; // 05 78 | virtual void GetLinearVelocityImpl(hkVector4& a_velocity) const = 0; // 06 79 | virtual void SetLinearVelocityImpl(const hkVector4& a_velocity) = 0; // 07 80 | virtual void GetCollisionFilterInfo(std::uint32_t& a_collisionFilterInfo) const = 0; // 08 81 | virtual void Unk_09(void) = 0; // 09 82 | virtual void Unk_0A(void) = 0; // 0A 83 | virtual void Unk_0B(void) = 0; // 0B 84 | virtual void Unk_0C(void) = 0; // 0C 85 | virtual void CheckSupportImpl() = 0; // 0D 86 | virtual void Unk_0E(void) = 0; // 0E 87 | virtual bhkWorld* GetHavokWorld() = 0; // 0F 88 | virtual hkpRigidBody* GetRigidBody() const = 0; // 10 89 | virtual float GetVDBAlpha() const = 0; // 11 90 | virtual void Unk_12(void) = 0; // 12 91 | virtual void Unk_13(void) = 0; // 13 92 | 93 | inline void GetPosition(hkVector4& a_pos, bool a_applyCenterOffset) const { return GetPositionImpl(a_pos, a_applyCenterOffset); } 94 | 95 | inline hkVector4 GetPosition(bool a_applyCenterOffset = true) const 96 | { 97 | hkVector4 pos; 98 | GetPositionImpl(pos, a_applyCenterOffset); 99 | return pos; 100 | } 101 | 102 | void SetWantedState() 103 | { 104 | if (wantState != hkpCharacterStateType::kTotal) 105 | { 106 | context.currentState = wantState; 107 | wantState = hkpCharacterStateType::kTotal; 108 | } 109 | } 110 | 111 | // members 112 | hkVector4 forwardVec; // 070 113 | hkStepInfo stepInfo; // 080 114 | hkVector4 outVelocity; // 090 115 | hkVector4 initialVelocity; // 0A0 116 | hkVector4 velocityMod; // 0B0 117 | hkVector4 direction; // 0C0 118 | hkVector4 rotCenter; // 0D0 119 | hkVector4 pushDelta; // 0E0 120 | hkVector4 fakeSupportStart; // 0F0 121 | hkVector4 up; // 100 122 | hkVector4 supportNorm; // 110 123 | BSBound collisionBound; // 120 124 | BSBound bumperCollisionBound; // 150 125 | hkVector4 deltaPos; // 180 126 | bhkICharOrientationController* orientationCtrl; // 190 127 | std::uint64_t pad198; // 198 128 | hkpSurfaceInfo surfaceInfo; // 1A0 129 | hkpCharacterContext context; // 1E0 130 | stl::enumeration flags; // 218 131 | hkpCharacterStateType wantState; // 218 132 | float velocityTime; // 220 133 | float rotMod; // 224 134 | float rotModTime; // 228 135 | float calculatePitchTimer; // 22C 136 | float acrobatics; // 230 137 | float center; // 234 138 | float waterHeight; // 238 139 | float jumpHeight; // 23C 140 | float fallStartHeight; // 240 141 | float fallTime; // 244 142 | float gravity; // 248 143 | float pitchAngle; // 24C 144 | float rollAngle; // 250 145 | float pitchMult; // 254 146 | float scale; // 258 147 | float swimFloatHeight; // 25C 148 | float actorHeight; // 260 149 | float speedPct; // 264 150 | std::uint32_t pushCount; // 268 151 | std::uint32_t unk26C; // 26C 152 | std::uint64_t unk270; // 270 153 | std::uint64_t unk278; // 278 154 | NiPointer shapes[2]; // 280 155 | float radius; // 290 156 | float height; // 294 157 | float destRadius; // 298 158 | float lodDistance; // 29C 159 | std::uint32_t size; // 2A0 160 | std::uint32_t priority; // 2A4 161 | std::int32_t supportCount; // 2A8 162 | std::uint32_t pad2AC; // 2AC 163 | hkRefPtr supportBody; // 2B0 164 | float bumpedForce; // 2B8 165 | std::uint32_t pad2BC; // 2BC 166 | hkRefPtr bumpedBody; // 2C0 167 | hkRefPtr bumpedCharCollisionObject; // 2C8 168 | BSTHashMap unk2D0; // 2D0 169 | float unk300; // 300 170 | std::uint32_t unk304; // 304 171 | std::uint32_t unk308; // 308 172 | float unk30C; // 30C 173 | float unk310; // 310 174 | std::uint32_t unk314; // 314 175 | std::uint32_t unk318; // 318 176 | bool unk31C; // 31C 177 | std::uint8_t unk31D; // 31D 178 | std::uint8_t unk31E; // 31E 179 | std::uint8_t unk31F; // 31F 180 | std::uint16_t unk320; // 320 181 | std::uint16_t unk322; // 322 182 | std::uint32_t unk324; // 324 183 | std::uint64_t unk328; // 328 184 | }; 185 | static_assert(sizeof(bhkCharacterController) == 0x330); 186 | } 187 | -------------------------------------------------------------------------------- /include/RE/B/bhkCharacterMoveInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/N/NiPoint3.h" 4 | 5 | namespace RE 6 | { 7 | struct bhkCharacterMoveInfo 8 | { 9 | // members 10 | float secondsSinceLastFrame; // 00 11 | NiPoint3 deltaRotation; // 04 12 | NiPoint3 deltaPosition; // 10 13 | float deltaSpeed; // 1C 14 | bool unk20; // 20 15 | std::uint8_t pad21; // 21 16 | std::uint16_t pad22; // 22 17 | }; 18 | static_assert(sizeof(bhkCharacterMoveInfo) == 0x24); 19 | } -------------------------------------------------------------------------------- /include/RE/B/bhkCharacterState.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/B/bhkCharacterController.h" 4 | #include "RE/H/hkpCharacterState.h" 5 | 6 | namespace RE 7 | { 8 | class bhkCharacterState : public hkpCharacterState 9 | { 10 | public: 11 | inline static constexpr auto RTTI = RTTI_bhkCharacterState; 12 | 13 | ~bhkCharacterState() override; // 00 14 | 15 | // override (hkpCharacterState) 16 | void Update(hkpCharacterContext& a_context, const hkpCharacterInput& a_input, hkpCharacterOutput& a_output) override; // 06 17 | void Change(hkpCharacterContext& a_context, const hkpCharacterInput& a_input, hkpCharacterOutput& a_output) override; // 07 18 | 19 | // add 20 | virtual void SimulateStatePhysics(bhkCharacterController* a_controller) = 0; // 08 21 | }; 22 | static_assert(sizeof(bhkCharacterState) == 0x10); 23 | } 24 | -------------------------------------------------------------------------------- /include/RE/B/bhkCharacterStateClimbing.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/B/bhkCharacterState.h" 4 | 5 | namespace RE 6 | { 7 | class bhkCharacterStateClimbing : public bhkCharacterState 8 | { 9 | public: 10 | inline static constexpr auto RTTI = RTTI_bhkCharacterStateClimbing; 11 | 12 | ~bhkCharacterStateClimbing() override; // 00 13 | 14 | // override (bhkCharacterState) 15 | hkpCharacterStateType GetType() const override; // 03 - { return kClimbing; } 16 | void SimulateStatePhysics(bhkCharacterController* a_controller) override; // 08 17 | }; 18 | static_assert(sizeof(bhkCharacterStateClimbing) == 0x10); 19 | } 20 | -------------------------------------------------------------------------------- /include/RE/B/bhkCharacterStateFlying.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/B/bhkCharacterState.h" 4 | 5 | namespace RE 6 | { 7 | class bhkCharacterStateFlying : public bhkCharacterState 8 | { 9 | public: 10 | inline static constexpr auto RTTI = RTTI_bhkCharacterStateFlying; 11 | 12 | ~bhkCharacterStateFlying() override; // 00 13 | 14 | // override (bhkCharacterState) 15 | hkpCharacterStateType GetType() const override; // 03 - { return kFlying; } 16 | void SimulateStatePhysics(bhkCharacterController* a_controller) override; // 08 17 | }; 18 | static_assert(sizeof(bhkCharacterStateFlying) == 0x10); 19 | } 20 | -------------------------------------------------------------------------------- /include/RE/B/bhkCharacterStateInAir.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/B/bhkCharacterState.h" 4 | 5 | namespace RE 6 | { 7 | class bhkCharacterStateInAir : public bhkCharacterState 8 | { 9 | public: 10 | inline static constexpr auto RTTI = RTTI_bhkCharacterStateInAir; 11 | 12 | ~bhkCharacterStateInAir() override; // 00 13 | 14 | // override (bhkCharacterState) 15 | hkpCharacterStateType GetType() const override; // 03 - { return kInAir; } 16 | void SimulateStatePhysics(bhkCharacterController* a_controller) override; // 08 17 | 18 | // members 19 | float maxVelocityDelta; // 10 20 | std::uint32_t pad14; // 14 21 | 22 | }; 23 | static_assert(sizeof(bhkCharacterStateInAir) == 0x18); 24 | } 25 | -------------------------------------------------------------------------------- /include/RE/B/bhkCharacterStateJumping.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/B/bhkCharacterState.h" 4 | 5 | namespace RE 6 | { 7 | class bhkCharacterStateJumping : public bhkCharacterState 8 | { 9 | public: 10 | inline static constexpr auto RTTI = RTTI_bhkCharacterStateJumping; 11 | 12 | ~bhkCharacterStateJumping() override; // 00 13 | 14 | // override (bhkCharacterState) 15 | hkpCharacterStateType GetType() const override; // 03 - { return kJumping; } 16 | void SimulateStatePhysics(bhkCharacterController* a_controller) override; // 08 17 | }; 18 | static_assert(sizeof(bhkCharacterStateJumping) == 0x10); 19 | } 20 | -------------------------------------------------------------------------------- /include/RE/B/bhkCharacterStateOnGround.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/B/bhkCharacterState.h" 4 | 5 | namespace RE 6 | { 7 | class bhkCharacterStateOnGround : public bhkCharacterState 8 | { 9 | public: 10 | inline static constexpr auto RTTI = RTTI_bhkCharacterStateOnGround; 11 | 12 | ~bhkCharacterStateOnGround() override; // 00 13 | 14 | // override (bhkCharacterState) 15 | hkpCharacterStateType GetType() const override; // 03 - { return kOnGround; } 16 | void SimulateStatePhysics(bhkCharacterController* a_controller) override; // 08 17 | 18 | // members 19 | std::uint64_t unk10; // 10 20 | }; 21 | static_assert(sizeof(bhkCharacterStateOnGround) == 0x18); 22 | } 23 | -------------------------------------------------------------------------------- /include/RE/B/bhkCharacterStateSwimming.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/B/bhkCharacterState.h" 4 | 5 | namespace RE 6 | { 7 | class bhkCharacterStateSwimming : public bhkCharacterState 8 | { 9 | public: 10 | inline static constexpr auto RTTI = RTTI_bhkCharacterStateSwimming; 11 | 12 | ~bhkCharacterStateSwimming() override; // 00 13 | 14 | // override (bhkCharacterState) 15 | hkpCharacterStateType GetType() const override; // 03 - { return kSwimming; } 16 | void SimulateStatePhysics(bhkCharacterController* a_controller) override; // 08 17 | }; 18 | static_assert(sizeof(bhkCharacterStateSwimming) == 0x10); 19 | } 20 | -------------------------------------------------------------------------------- /include/RE/B/bhkWorld.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/B/BSAtomic.h" 4 | #include "RE/B/bhkSerializable.h" 5 | #include "RE/H/hkVector4.h" 6 | 7 | namespace RE 8 | { 9 | struct bhkPickData; 10 | class BGSAcousticSpaceListener; 11 | class hkpSuspendInactiveAgentsUtil; 12 | class NiAVObject; 13 | 14 | class bhkWorld : public bhkSerializable 15 | { 16 | public: 17 | inline static constexpr auto RTTI = RTTI_bhkWorld; 18 | inline static auto Ni_RTTI = NiRTTI_bhkWorld; 19 | inline static constexpr auto VTABLE = VTABLE_bhkWorld; 20 | 21 | class bhkConstraintProjector; 22 | 23 | ~bhkWorld() override; // 00 24 | 25 | // override (bhkSerializable) 26 | const NiRTTI* GetRTTI() const override; // 02 27 | void SetReferencedObject(hkReferencedObject* a_object) override; // 25 28 | void AdjustRefCount(bool a_increment) override; // 26 29 | hkpWorld* GetWorld1() override; // 27 - { return referencedObject.ptr; } 30 | ahkpWorld* GetWorld2() override; // 28 - { return referencedObject.ptr; } 31 | void Unk_2B(void) override; // 2B 32 | void Unk_2C(void) override; // 2C - { return 1; } 33 | void Unk_2E(void) override; // 2E 34 | void Unk_2F(void) override; // 2F 35 | 36 | // add 37 | virtual void Unk_32(void); // 32 38 | virtual bool PickObject(bhkPickData& a_pickData); // 33 39 | virtual void Unk_34(void); // 34 40 | virtual void Unk_35(void); // 35 41 | virtual void InitHavok(NiAVObject* a_sceneObject, NiAVObject* a_root); // 36 42 | 43 | hkVector4 GetGravity() 44 | { 45 | using func_t = void (bhkWorld::*)(hkVector4&); 46 | REL::Relocation func{ RELOCATION_ID(76005, 77838) }; 47 | 48 | hkVector4 retVal; 49 | func(this, retVal); 50 | return retVal; 51 | } 52 | 53 | static float GetWorldScale() 54 | { 55 | REL::Relocation worldScale{ RELOCATION_ID(231896, 188105) }; 56 | return *worldScale; 57 | } 58 | 59 | static float GetWorldScaleInverse() 60 | { 61 | REL::Relocation worldScaleInverse{ RELOCATION_ID(230692, 187407) }; 62 | return *worldScaleInverse; 63 | } 64 | 65 | // members 66 | std::uint8_t unk0020[0x320]; // 0020 67 | std::uint8_t unk0340[0x6400]; // 0340 68 | std::uint8_t unk6740[0x5DC0]; // 6740 69 | BSTArray unkC500; // C500 70 | BSTArray unkC518; // C518 71 | BSTArray unkC530; // C530 72 | BSTArray unkC548; // C548 73 | std::uint64_t unkC560; // C560 74 | std::uint32_t unkC568; // C568 75 | float unkC56C; // C56C 76 | bhkConstraintProjector* constraintProjector; // C570 77 | std::uint64_t unkC578; // C578 78 | std::uint32_t unkC580; // C580 79 | float unkC584; // C584 80 | std::uint64_t unkC588; // C588 81 | std::uint64_t unkC590; // C590 82 | mutable BSReadWriteLock worldLock; // C598 83 | mutable BSReadWriteLock unkC5A0; // C5A0 84 | std::uint64_t unkC5A8; // C5A8 85 | hkVector4 unkC5B0; // C5B0 86 | std::uint64_t unkC5C0; // C5C0 87 | BGSAcousticSpaceListener* acousticSpaceListener; // C5C8 88 | hkpSuspendInactiveAgentsUtil* suspendInactiveAgentsUtil; // C5D0 89 | std::uint32_t unkC5D8; // C5D8 - incremented per frame 90 | std::uint32_t unkC5DC; // C5DC 91 | std::uint32_t unkC5E0; // C5E0 92 | std::uint32_t unkC5E4; // C5E4 93 | std::uint32_t unkC5E8; // C5E8 94 | std::uint32_t unkC5EC; // C5EC 95 | float tau; // C5F0 96 | float damping; // C5F4 97 | std::uint8_t unkC5F8; // C5F8 98 | bool toggleCollision; // C5F9 99 | std::uint16_t unkC5FA; // C5FA 100 | std::uint16_t unkC5FC; // C5FC 101 | std::uint16_t unkC5FE; // C5FE 102 | }; 103 | static_assert(sizeof(bhkWorld) == 0xC600); 104 | } 105 | -------------------------------------------------------------------------------- /include/RE/H/hkClass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace RE 4 | { 5 | class hkClassEnum; 6 | class hkClassMember; 7 | class hkCustomAttributes; 8 | 9 | class hkClass 10 | { 11 | enum FlagValues 12 | { 13 | kFlagsNone = 0, 14 | kFlagsNotSerializable = 1 15 | }; 16 | using Flags = stl::enumeration; 17 | 18 | const char* name; // 00 19 | const hkClass* parent; // 08 20 | std::int32_t objectSize; // 10 21 | std::int32_t numImplementedInterfaces; // 14 22 | const class hkClassEnum* declaredEnums; // 18 23 | std::int32_t numDeclaredEnums; // 20 24 | std::uint32_t pad24; // 24 25 | const class hkClassMember* declaredMembers; // 28 26 | std::int32_t numDeclaredMembers; // 30 27 | std::uint32_t pad34; // 34 28 | const void* defaults; // 38 29 | const hkCustomAttributes* attributes; // 40 30 | Flags flags; // 48 31 | std::int32_t describedVersion; // 4C 32 | }; 33 | static_assert(sizeof(hkClass) == 0x50); 34 | } -------------------------------------------------------------------------------- /include/RE/H/hkQsTransform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkQuaternion.h" 4 | #include "RE/H/hkVector4.h" 5 | 6 | namespace RE 7 | { 8 | class hkQsTransform 9 | { 10 | public: 11 | // members 12 | hkVector4 translation; // 00 13 | hkQuaternion rotation; // 10 14 | hkVector4 scale; // 20 15 | }; 16 | static_assert(sizeof(hkQsTransform) == 0x30); 17 | } 18 | -------------------------------------------------------------------------------- /include/RE/H/hkVector4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkSseMathTypes.h" 4 | #include "RE/N/NiPoint3.h" 5 | 6 | namespace RE 7 | { 8 | class hkVector4 9 | { 10 | public: 11 | hkVector4() : 12 | quad(_mm_set1_ps(0.0f)) 13 | {} 14 | 15 | hkVector4(const float& a_x) : 16 | quad(_mm_set1_ps(a_x)) 17 | {} 18 | 19 | hkVector4(const float& a_x, const float& a_y, const float& a_z, const float& a_w) : 20 | quad(_mm_setr_ps(a_x, a_y, a_z, a_w)) 21 | {} 22 | 23 | hkVector4(const hkVector4& a_rhs) : 24 | quad(a_rhs.quad) 25 | {} 26 | 27 | hkVector4(const hkQuadReal& a_rhs) : 28 | quad(a_rhs) 29 | {} 30 | 31 | hkVector4(const NiPoint3& a_point) : 32 | quad(_mm_setr_ps(a_point.x, a_point.y, a_point.z, 0.0f)) 33 | {} 34 | 35 | hkVector4& operator=(const hkVector4& a_rhs); 36 | hkVector4 operator+(const hkVector4& a_rhs) const; 37 | hkVector4 operator-(const hkVector4& a_rhs) const; 38 | hkVector4 operator*(const hkVector4& a_rhs) const; 39 | hkVector4 operator/(const hkVector4& a_rhs) const; 40 | 41 | hkVector4 operator+=(const hkVector4& a_rhs) const { return *this + a_rhs; } 42 | hkVector4 operator-=(const hkVector4& a_rhs) const { return *this - a_rhs; } 43 | hkVector4 operator*=(const hkVector4& a_rhs) const { return *this * a_rhs; } 44 | hkVector4 operator/=(const hkVector4& a_rhs) const { return *this / a_rhs; } 45 | 46 | [[nodiscard]] bool IsEqual(const hkVector4& a_pt, float a_epsilon = 1e-3f) const; 47 | [[nodiscard]] hkVector4 Cross(const hkVector4& a_pt) const; 48 | [[nodiscard]] float Dot3(const hkVector4& a_pt) const; 49 | [[nodiscard]] float Dot4(const hkVector4& a_pt) const; 50 | [[nodiscard]] float GetDistance3(const hkVector4& a_pt) const noexcept; 51 | [[nodiscard]] float GetSquaredDistance3(const hkVector4& a_pt) const noexcept; 52 | [[nodiscard]] float Length3() const; 53 | [[nodiscard]] float SqrLength3() const; 54 | [[nodiscard]] float Length4() const; 55 | [[nodiscard]] float SqrLength4() const; 56 | 57 | // members 58 | union 59 | { 60 | hkQuadReal quad{}; // 00 61 | struct 62 | { 63 | float x; 64 | float y; 65 | float z; 66 | float w; 67 | }; 68 | }; 69 | }; 70 | static_assert(sizeof(hkVector4) == 0x10); 71 | } 72 | -------------------------------------------------------------------------------- /include/RE/H/hkaAnimatedReferenceFrame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkQsTransform.h" 4 | #include "RE/H/hkReferencedObject.h" 5 | 6 | namespace RE 7 | { 8 | class hkaAnimatedReferenceFrame : public hkReferencedObject 9 | { 10 | public: 11 | inline static constexpr auto RTTI = RTTI_hkaAnimatedReferenceFrame; 12 | 13 | // add 14 | virtual void GetReferenceFrame(float a_time, hkQsTransform& a_motionOut) const = 0; // 03 15 | virtual void GetDeltaReferenceFrame(float a_time, float a_nextTime, int a_loops, hkQsTransform& a_deltaMotionOut, float a_cropStartAmount, float a_cropEndAmount) const = 0; // 04 16 | virtual float GetDuration() const = 0; // 05 17 | }; 18 | static_assert(sizeof(hkaAnimatedReferenceFrame) == 0x10); 19 | } 20 | -------------------------------------------------------------------------------- /include/RE/H/hkaAnimation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkRefPtr.h" 4 | #include "RE/H/hkReferencedObject.h" 5 | #include "RE/H/hkaAnimatedReferenceFrame.h" 6 | #include "RE/H/hkaAnnotationTrack.h" 7 | 8 | namespace RE 9 | { 10 | class hkaChunkCache; 11 | 12 | class hkaAnimation : public hkReferencedObject 13 | { 14 | public: 15 | inline static constexpr auto RTTI = RTTI_hkaAnimation; 16 | 17 | enum class AnimationType 18 | { 19 | kUnknownAnimation = 0, 20 | kInterleavedAnimation, 21 | kDeltaCompressedAnimation, 22 | kWaveletCompressedAnimation, 23 | kMirroredAnimation, 24 | kSplineCompressedAnimation, 25 | kQuantizedCompressedAnimation, 26 | }; 27 | 28 | struct DataChunk 29 | { 30 | const void* data; // 00 31 | std::uint32_t size; // 08 32 | std::uint8_t offset; // 0C 33 | }; 34 | 35 | struct TrackAnnotation 36 | { 37 | // The bone ID which is annotated 38 | std::uint16_t trackID; 39 | hkaAnnotationTrack::Annotation annotation; 40 | }; 41 | 42 | // add 43 | virtual void SampleTracks(float a_time, hkQsTransform* a_transformTracksOut, float* a_floatTracksOut, hkaChunkCache* cache) const = 0; // 03 44 | virtual void SamplePartialTracks(float a_time, std::uint32_t a_maxNumTransformTracks, hkQsTransform* a_transformTracksOut, std::uint32_t a_maxNumFloatTracks, float* a_floatTracksOut, hkaChunkCache* a_cache) const; // 04 45 | virtual void ClearAllCacheKeys(hkaChunkCache* a_cache) const; // 05 46 | virtual void SampleIndividualTransformTracks(float a_time, const std::uint16_t* a_tracks, std::uint32_t a_numTracks, hkQsTransform* a_transformOut) const = 0; // 06 47 | virtual void SampleIndividualFloatTracks(float a_time, const std::uint16_t* a_tracks, std::uint32_t a_numTracks, float* a_out) const = 0; // 07 48 | virtual std::int32_t GetNumOriginalFrames() const = 0; // 08 49 | virtual std::int32_t GetNumDataChunks(std::uint32_t a_frame, float a_delta) const; // 09 50 | virtual void GetDataChunks(std::uint32_t a_frame, float a_delta, DataChunk* a_dataChunks, std::int32_t a_numDataChunks) const; // 0A 51 | virtual std::int32_t GetMaxSizeOfCombinedDataChunks() const; // 0B 52 | virtual void GetExtractedMotionReferenceFrame(float a_time, hkQsTransform& a_motionOut) const; // 0C 53 | virtual void GetExtractedMotionDeltaReferenceFrame(float a_time, float a_nextTime, std::int32_t a_loops, hkQsTransform& a_deltaMotionOut, float a_cropStartAmount, float a_cropEndAmount) const; // 0D 54 | virtual std::uint32_t GetNumAnnotations(float a_startTime, float a_deltaTime) const; // 0E 55 | virtual std::uint32_t GetAnnotations(float a_startTime, float a_deltaTime, TrackAnnotation* a_annotationsOut, std::uint32_t a_maxAnnotations) const; // 0F 56 | 57 | // members 58 | stl::enumeration type; // 10 59 | float duration; // 14 60 | std::int32_t numberOfTransformTracks; // 18 61 | std::int32_t numberOfFloatTracks; // 1C 62 | hkRefPtr extractedMotion; // 20 63 | hkArray annotationTracks; // 28 64 | }; 65 | static_assert(sizeof(hkaAnimation) == 0x38); 66 | } 67 | -------------------------------------------------------------------------------- /include/RE/H/hkaAnimationBinding.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkArray.h" 4 | #include "RE/H/hkRefPtr.h" 5 | #include "RE/H/hkReferencedObject.h" 6 | #include "RE/H/hkStringPtr.h" 7 | #include "RE/H/hkaAnimation.h" 8 | 9 | namespace RE 10 | { 11 | class hkaAnimationBinding : public hkReferencedObject 12 | { 13 | public: 14 | inline static constexpr auto RTTI = RTTI_hkaAnimationBinding; 15 | 16 | enum class BlendHint 17 | { 18 | kNormal = 0, 19 | kAdditive 20 | }; 21 | 22 | // members 23 | hkStringPtr originalSkeletonName; // 10 24 | hkRefPtr animation; // 18 25 | hkArray transformTrackToBoneIndices; // 20 26 | hkArray floatTrackToFloatSlotIndices; // 30 27 | stl::enumeration blendHint; // 40 28 | char pad41[7]; // 41 29 | }; 30 | static_assert(sizeof(hkaAnimationBinding) == 0x48); 31 | } 32 | -------------------------------------------------------------------------------- /include/RE/H/hkaAnimationControl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkArray.h" 4 | #include "RE/H/hkReferencedObject.h" 5 | #include "RE/H/hkaAnimationBinding.h" 6 | 7 | namespace RE 8 | { 9 | class hkaAnimationControlListener; 10 | class hkaChunkCache; 11 | 12 | class hkaAnimationControl : public hkReferencedObject 13 | { 14 | public: 15 | inline static constexpr auto RTTI = RTTI_hkaAnimationControl; 16 | 17 | ~hkaAnimationControl() override; // 00 18 | 19 | // add 20 | virtual void Update(float a_stepDelta) = 0; // 03 21 | virtual void GetFutureTime(float a_stepDelta, float& a_localTimeOut, std::int32_t& a_loopsOut) const = 0; // 04 22 | virtual void SampleTracks(hkQsTransform* a_transformTracksOut, float* a_floatTracksOut, hkaChunkCache* a_cache) const; // 05 23 | virtual void SamplePartialTracks(std::uint32_t a_maxNumTransformTracks, hkQsTransform* a_transformTracksOut, std::uint32_t a_maxNumFloatTracks, float* a_floatTracksOut, hkaChunkCache* a_cache) const; // 06 24 | virtual void GetExtractedMotionDeltaReferenceFrame(float a_deltaTime, hkQsTransform& a_deltaMotionOut) const; // 07 25 | virtual std::int32_t GetNumberOfTransformTracks() const; // 08 26 | virtual std::int32_t GetNumberOfFloatTracks() const; // 09 27 | virtual std::int32_t GetNumTransformTrackToBoneIndices() const; // 0A 28 | virtual const std::int16_t* GetTransformTrackToBoneIndices() const; // 0B 29 | virtual std::int32_t GetNumFloatTrackToFloatSlotIndices() const; // 0C 30 | virtual const std::int16_t* GetFloatTrackToFloatSlotIndices() const; // 0D 31 | 32 | // members 33 | float localTime; // 10 34 | float weight; // 14 35 | hkArray transformTrackWeights; // 18 36 | hkArray floatTrackWeights; // 28 37 | hkaAnimationBinding* binding; // 38 38 | hkArray listeners; // 40 39 | float motionTrackWeight; // 50 40 | std::uint32_t pad54; // 54 41 | }; 42 | static_assert(sizeof(hkaAnimationControl) == 0x58); 43 | } 44 | -------------------------------------------------------------------------------- /include/RE/H/hkaAnnotationTrack.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkArray.h" 4 | #include "RE/H/hkStringPtr.h" 5 | 6 | namespace RE 7 | { 8 | class hkaAnnotationTrack 9 | { 10 | public: 11 | struct Annotation 12 | { 13 | float time; // 00 14 | std::uint32_t pad04; // 04 15 | hkStringPtr text; // 08 16 | }; 17 | 18 | hkStringPtr trackName; // 00 19 | hkArray annotations; // 08 20 | }; 21 | static_assert(sizeof(hkaAnnotationTrack) == 0x18); 22 | } 23 | -------------------------------------------------------------------------------- /include/RE/H/hkaDefaultAnimationControl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkVector4.h" 4 | #include "RE/H/hkaAnimationControl.h" 5 | 6 | namespace RE 7 | { 8 | class hkaDefaultAnimationControlMapperData; 9 | class hkaDefaultAnimationControlListener; 10 | 11 | class hkaDefaultAnimationControl : public hkaAnimationControl 12 | { 13 | public: 14 | inline static constexpr auto RTTI = RTTI_hkaDefaultAnimationControl; 15 | 16 | enum class EaseStatus 17 | { 18 | kEasingIn = 0, 19 | kEasedIn, 20 | kEasingOut, 21 | kEasedOut 22 | }; 23 | 24 | ~hkaDefaultAnimationControl() override; // 00 25 | 26 | // override (hkaAnimationControl) 27 | void Update(float a_stepDelta) override; // 03 28 | void GetFutureTime(float a_stepDelta, float& a_localTimeOut, std::int32_t& a_loopsOut) const override; // 04 29 | void SampleTracks(hkQsTransform* a_transformTracksOut, float* a_floatTracksOut, hkaChunkCache* a_cache) const override; // 05 30 | void SamplePartialTracks(std::uint32_t a_maxNumTransformTracks, hkQsTransform* a_transformTracksOut, std::uint32_t a_maxNumFloatTracks, float* a_floatTracksOut, hkaChunkCache* a_cache) const override; // 06 31 | void GetExtractedMotionDeltaReferenceFrame(float a_deltaTime, hkQsTransform& a_deltaMotionOut) const override; // 07 32 | std::int32_t GetNumberOfTransformTracks() const override; // 08 33 | std::int32_t GetNumberOfFloatTracks() const override; // 09 34 | std::int32_t GetNumTransformTrackToBoneIndices() const override; // 0A 35 | const std::int16_t* GetTransformTrackToBoneIndices() const override; // 0B 36 | 37 | // members 38 | float masterWeight; // 58 39 | float playbackSpeed; // 5C 40 | std::uint32_t overflowCount; // 60 41 | std::uint32_t underflowCount; // 64 42 | std::int32_t maxCycles; // 68 43 | std::uint32_t pad6C; // 6C 44 | hkVector4 easeInCurve; // 70 45 | hkVector4 easeOutCurve; // 80 46 | float easeInvDuration; // 90 47 | float easeT; // 94 48 | EaseStatus easeStatus; // 98 49 | float cropStartAmountLocalTime; // 9C 50 | float cropEndAmountLocalTime; // A0 51 | std::uint32_t padA4; // A4 52 | hkArray defaultListeners; // A8 53 | hkaDefaultAnimationControlMapperData* mapper; // B8 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /include/RE/H/hkaSplineCompressedAnimation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkaAnimation.h" 4 | 5 | namespace RE 6 | { 7 | class hkaSplineCompressedAnimation : public hkaAnimation 8 | { 9 | public: 10 | inline static constexpr auto RTTI = RTTI_hkaSplineCompressedAnimation; 11 | 12 | // override (hkaAnimation) 13 | void SampleTracks(float a_time, hkQsTransform* a_transformTracksOut, float* a_floatTracksOut, hkaChunkCache* cache) const override; // 03 14 | void SamplePartialTracks(float a_time, std::uint32_t a_maxNumTransformTracks, hkQsTransform* a_transformTracksOut, std::uint32_t a_maxNumFloatTracks, float* a_floatTracksOut, hkaChunkCache* a_cache) const override; // 04 15 | void SampleIndividualTransformTracks(float a_time, const std::uint16_t* a_tracks, std::uint32_t a_numTracks, hkQsTransform* a_transformOut) const override; // 06 16 | void SampleIndividualFloatTracks(float a_time, const std::uint16_t* a_tracks, std::uint32_t a_numTracks, float* a_out) const override; // 07 17 | std::int32_t GetNumOriginalFrames() const override; // 08 18 | std::int32_t GetNumDataChunks(std::uint32_t a_frame, float a_delta) const override; // 09 19 | void GetDataChunks(std::uint32_t a_frame, float a_delta, DataChunk* a_dataChunks, std::int32_t a_numDataChunks) const override; // 0A 20 | std::int32_t GetMaxSizeOfCombinedDataChunks() const override; // 0B 21 | 22 | // members 23 | std::int32_t numFrames; // 38 24 | std::int32_t numBlocks; // 3C 25 | std::int32_t maxFramesPerBlock; // 40 26 | std::int32_t maskAndQuantizationSize; // 44 27 | float blockDuration; // 48 28 | float blockInverseDuration; // 4C 29 | float frameDuration; // 50 30 | std::uint32_t pad54; // 54 31 | hkArray blockOffsets; // 58 32 | hkArray floatBlockOffsets; // 68 33 | hkArray transformOffsets; // 78 34 | hkArray floatOffsets; // 88 35 | hkArray data; // 98 36 | std::int32_t endian; // A8 37 | std::uint32_t padAC; // AC 38 | }; 39 | static_assert(sizeof(hkaSplineCompressedAnimation) == 0xB0); 40 | } 41 | -------------------------------------------------------------------------------- /include/RE/H/hkbBehaviorGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkArray.h" 4 | #include "RE/H/hkRefVariant.h" 5 | #include "RE/H/hkbGenerator.h" 6 | 7 | namespace RE 8 | { 9 | class hkbBehaviorGraphData; 10 | 11 | class hkbBehaviorGraph : public hkbGenerator 12 | { 13 | public: 14 | inline static constexpr auto RTTI = RTTI_hkbBehaviorGraph; 15 | 16 | enum class VariableMode 17 | { 18 | kDiscardWhenActive = 0, 19 | kMaintainValuesWhenInactive = 1, 20 | }; 21 | 22 | ~hkbBehaviorGraph() override; // 00 23 | 24 | // override (hkbGenerator) 25 | hkClass* GetClassType() const override; // 01 26 | void CalcContentStatistics(hkStatisticsCollector* a_collector, const hkClass* a_class) const override; // 02 27 | void Activate(const hkbContext& a_context) override; // 04 28 | void Update(const hkbContext& a_context, float a_timestep) override; // 05 29 | void Unk_06(void) override; // 06 30 | void Deactivate(const hkbContext& a_context) override; // 07 31 | void Unk_09(void) override; // 09 32 | void Unk_0C(void) override; // 0C 33 | void Unk_16(void) override; // 16 - { return 1; } 34 | void Generate(const hkbContext& a_context) override; // 17 35 | void Unk_18(void) override; // 18 - { return 1; } 36 | void UpdateSync(const hkbContext& a_context) override; // 19 37 | 38 | // members 39 | stl::enumeration variableMode; // 048 40 | std::uint8_t pad49; // 049 41 | std::uint16_t pad4A; // 04A 42 | std::uint32_t pad4C; // 04C 43 | hkArray uniqueIDPool; // 050 44 | hkRefVariant idToStateMachineTemplateMap; // 060 45 | hkArray mirroredExternalIDMap; // 068 46 | hkRefVariant pseudoRandomGenerator; // 078 47 | hkRefPtr rootGenerator; // 080 48 | hkRefPtr data; // 088 49 | hkRefVariant rootGeneratorClone; // 090 50 | hkRefVariant activeNodes; // 098 51 | hkRefVariant activeNodeTemplateToIndexMap; // 0A0 52 | hkRefVariant activeNodesChildrenIndices; // 0A8 53 | hkRefVariant globalTransitionData; // 0B0 54 | hkRefVariant eventIDMap; // 0B8 55 | hkRefVariant attributeIDMap; // 0C0 56 | hkRefVariant variableIDMap; // 0C8 57 | hkRefVariant characterPropertyIDMap; // 0D0 58 | hkRefVariant variableValueSet; // 0D8 59 | hkRefVariant nodeTemplateToCloneMap; // 0E0 60 | hkRefVariant nodeCloneToTemplateMap; // 0E8 61 | hkRefVariant stateListenerTemplateToCloneMap; // 0F0 62 | hkRefVariant nodePartitionInfo; // 0F8 63 | std::int32_t numIntermediateOutputs; // 100 64 | std::uint32_t pad104; // 104 65 | hkArray jobs; // 108 66 | hkArray allPartitionMemory; // 118 67 | std::int16_t numStaticNodes; // 128 68 | std::int16_t nextUniqueID; // 12A 69 | bool isActive; // 12C 70 | bool isLinked; // 12D 71 | bool updateActiveNodes; // 12E 72 | bool stateOrTransitionChanged; // 12F 73 | }; 74 | static_assert(sizeof(hkbBehaviorGraph) == 0x130); 75 | } 76 | -------------------------------------------------------------------------------- /include/RE/H/hkbClipGenerator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkQsTransform.h" 4 | #include "RE/H/hkaDefaultAnimationControl.h" 5 | #include "RE/H/hkbGenerator.h" 6 | #include "RE/H/hkbContext.h" 7 | 8 | namespace RE 9 | { 10 | class hkbClipTriggerArray; 11 | 12 | class hkbClipGenerator : public hkbGenerator 13 | { 14 | public: 15 | inline static constexpr auto RTTI = RTTI_hkbClipGenerator; 16 | 17 | enum PlaybackMode 18 | { 19 | kModeSinglePlay = 0, 20 | kModeLooping = 1, 21 | kModeUserControlled = 2, 22 | kModePingPong = 3, 23 | kModeCount = 4 24 | }; 25 | 26 | ~hkbClipGenerator() override; // 00 27 | 28 | // override (hkbNode) 29 | void Activate(const hkbContext& a_context) override; // 04 30 | void Update(const hkbContext& a_context, float a_timestep) override; // 05 31 | void Deactivate(const hkbContext& a_context) override; // 07 32 | 33 | // override (hkbGenerator) 34 | void Generate(const hkbContext& a_context) override; // 17 35 | void UpdateSync(const hkbContext& a_context) override; // 19 36 | 37 | // members 38 | hkStringPtr animationName; // 048 39 | hkRefPtr triggers; // 050 40 | float cropEndAmountLocalTime; // 058 41 | float startTime; // 05C 42 | float playbackSpeed; // 060 43 | float enforcedDuration; // 064 44 | float userControlledTimeFraction; // 068 45 | std::uint16_t animationBindingIndex; // 06C 46 | stl::enumeration mode; // 06E 47 | std::uint8_t flags; // 06F 48 | std::uint64_t unk70; // 070 49 | hkArray animDatas; // 078 50 | hkRefPtr animationControl; // 088 51 | hkRefPtr originalTriggers; // 090 52 | hkaDefaultAnimationControlMapperData* mapperData; // 098 53 | hkaAnimationBinding* binding; // 0A0 54 | hkRefVariant mirroredAnimation; // 0A8 55 | hkQsTransform extractedMotion; // 0B0 56 | hkArray echos; // 0E0 57 | float localTime; // 0F0 58 | float time; // 0F4 59 | float previousUserControlledTimeFraction; // 0F8 60 | std::int32_t bufferSize; // 0FC 61 | std::int32_t echoBufferSize; // 100 62 | bool atEnd; // 104 63 | bool ignoreStartTime; // 105 64 | bool pingPongBackward; // 106 65 | std::uint8_t pad107[9]; // 107 66 | }; 67 | static_assert(sizeof(hkbClipGenerator) == 0x110); 68 | } 69 | -------------------------------------------------------------------------------- /include/RE/H/hkbContext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkbCharacter.h" 4 | 5 | namespace RE 6 | { 7 | class hkaDefaultAnimationControlMapperData; 8 | class hkbGeneratorOutputListener; 9 | class hkbEventQueue; 10 | class hkbAttachmentManager; 11 | 12 | class hkbContext 13 | { 14 | public: 15 | // members 16 | hkbCharacter* character; // 00 17 | hkbBehaviorGraph* behavior; // 08 18 | hkRefVariant nodeToIndexMap; // 10 hkPointerMap 19 | hkbEventQueue* eventQueue; // 18 20 | hkaDefaultAnimationControlMapperData* sharedEventQueue; // 20 21 | hkRefPtr generatorOutputListener; // 28 22 | bool eventTriggeredTransition; // 30 23 | std::uint8_t pad31[7]; // 31 24 | hkRefVariant world; // 38 25 | hkbAttachmentManager* attachmentManager; // 40 26 | hkRefVariant animationCache; // 48 27 | }; 28 | static_assert(sizeof(hkbContext) == 0x50); 29 | } 30 | -------------------------------------------------------------------------------- /include/RE/H/hkbGenerator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkbNode.h" 4 | 5 | namespace RE 6 | { 7 | class hkbGenerator : public hkbNode 8 | { 9 | public: 10 | inline static constexpr auto RTTI = RTTI_hkbGenerator; 11 | 12 | ~hkbGenerator() override; // 00 13 | 14 | // override (hkbNode) 15 | void Unk_15(void) override; // 15 - { return 1; } 16 | 17 | // add 18 | virtual void Generate(const hkbContext& a_context) = 0; // 17 19 | virtual void Unk_18(void); // 18 - { return 0; } 20 | virtual void UpdateSync(const hkbContext& a_context); // 19 21 | virtual void Unk_1A(void); // 1A - { return; } 22 | virtual void Unk_1B(void); // 1B - { return; } 23 | }; 24 | static_assert(sizeof(hkbGenerator) == 0x48); 25 | } 26 | -------------------------------------------------------------------------------- /include/RE/H/hkbNode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkStringPtr.h" 4 | #include "RE/H/hkbBindable.h" 5 | #include "RE/H/hkbContext.h" 6 | 7 | namespace RE 8 | { 9 | class hkbNode : public hkbBindable 10 | { 11 | public: 12 | inline static constexpr auto RTTI = RTTI_hkbNode; 13 | 14 | ~hkbNode() override; // 00 15 | 16 | // add 17 | virtual void Activate(const hkbContext& a_context); // 04 - { return; } 18 | virtual void Update(const hkbContext& a_context, float a_timestep); // 05 - { userData |= 1; } 19 | virtual void Unk_06(void); // 06 - { return; } 20 | virtual void Deactivate(const hkbContext& a_context); // 07 - { return; } 21 | virtual void Unk_08(void); // 08 - { return 2; } 22 | virtual void Unk_09(void); // 09 - { return; } 23 | virtual void Unk_0A(void); // 0A - { return 1; } 24 | virtual void Unk_0B(void); // 0B - { return; } 25 | virtual void Unk_0C(void); // 0C 26 | virtual void Unk_0D(void); // 0D - { return 0; } 27 | virtual void Unk_0E(void); // 0E - { return; } 28 | virtual void Unk_0F(void); // 0F - { return; } 29 | virtual void Unk_10(void); // 10 - { return; } 30 | virtual void Unk_11(void); // 11 - { return; } 31 | virtual void Unk_12(void); // 12 - { return; } 32 | virtual void Unk_13(void); // 13 - { return 0; } 33 | virtual void Unk_14(void); // 14 - { return; } 34 | virtual void Unk_15(void); // 15 - { return 0; } 35 | virtual void Unk_16(void); // 16 - { return 0; } 36 | 37 | enum class GetChildrenFlagBits 38 | { 39 | kActiveOnly = 1 << 0, 40 | kGeneratorsOnly = 1 << 1, 41 | kIgnoreReferencedBehaviour = 1 << 2 42 | }; 43 | 44 | enum class CloneState 45 | { 46 | kDefault = 0, 47 | kTemplate = 1, 48 | kClone = 2, 49 | kShareable = 3 50 | }; 51 | 52 | // members 53 | std::uint32_t userData; // 30 54 | std::uint32_t pad34; // 34 55 | hkStringPtr name; // 38 56 | std::uint16_t id; // 40 57 | stl::enumeration cloneState; // 42 58 | std::uint8_t pad43; // 43 59 | std::uint32_t pad44; // 44 60 | }; 61 | static_assert(sizeof(hkbNode) == 0x48); 62 | } 63 | -------------------------------------------------------------------------------- /include/RE/H/hkbStateMachine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkArray.h" 4 | #include "RE/H/hkRefPtr.h" 5 | #include "RE/H/hkReferencedObject.h" 6 | #include "RE/H/hkbBindable.h" 7 | #include "RE/H/hkbEvent.h" 8 | #include "RE/H/hkbGenerator.h" 9 | 10 | namespace RE 11 | { 12 | class hkbStateChooser; 13 | 14 | class hkbStateMachine : public hkbGenerator 15 | { 16 | public: 17 | inline static constexpr auto RTTI = RTTI_hkbStateMachine; 18 | 19 | enum class StartStateMode 20 | { 21 | kDefault = 0, 22 | kSync = 1, 23 | kRandom = 2, 24 | kChooser = 3 25 | }; 26 | 27 | enum class StateMachineSelfTransitionMode 28 | { 29 | kNoTransition = 0, 30 | kTransitionToStartState = 1, 31 | kForceTransitionToStartState = 2 32 | }; 33 | 34 | class StateInfo : public hkbBindable 35 | { 36 | public: 37 | inline static constexpr auto RTTI = RTTI_hkbStateMachine__StateInfo; 38 | 39 | ~StateInfo() override; // 00 40 | 41 | // members 42 | std::uint64_t unk30; // 30 43 | std::uint64_t unk38; // 38 44 | std::uint64_t unk40; // 40 45 | std::uint64_t unk48; // 48 46 | std::uint64_t unk50; // 50 47 | std::uint64_t unk58; // 58 48 | std::uint64_t unk60; // 60 49 | std::uint64_t unk68; // 68 50 | std::uint64_t unk70; // 70 51 | }; 52 | static_assert(sizeof(StateInfo) == 0x78); 53 | 54 | class TransitionInfoArray : public hkReferencedObject 55 | { 56 | public: 57 | inline static constexpr auto RTTI = RTTI_hkbStateMachine__TransitionInfoArray; 58 | 59 | ~TransitionInfoArray() override; // 00 60 | 61 | // members 62 | std::uint64_t unk10; // 10 63 | std::uint64_t unk18; // 18 64 | }; 65 | static_assert(sizeof(TransitionInfoArray) == 0x20); 66 | 67 | ~hkbStateMachine() override; // 00 68 | 69 | // override (hkbGenerator) 70 | hkClass* GetClassType() const override; // 01 71 | void CalcContentStatistics(hkStatisticsCollector* a_collector, const hkClass* a_class) const override; // 02 72 | void Unk_03(void) override; // 03 73 | void Activate(const hkbContext& a_context) override; // 04 74 | void Update(const hkbContext& a_context, float a_timestep) override; // 05 75 | void Unk_06(void) override; // 06 76 | void Deactivate(const hkbContext& a_context) override; // 07 77 | void Unk_08(void) override; // 08 78 | void Unk_09(void) override; // 09 79 | void Unk_0A(void) override; // 0A 80 | void Unk_0C(void) override; // 0C 81 | void Unk_0D(void) override; // 0D 82 | void Unk_0E(void) override; // 0E 83 | void Unk_0F(void) override; // 0F 84 | void Unk_10(void) override; // 10 85 | void Unk_11(void) override; // 11 86 | void Unk_12(void) override; // 12 87 | void Unk_14(void) override; // 14 88 | void Generate(const hkbContext& a_context) override; // 17 89 | void Unk_18(void) override; // 18 - { return 1; } 90 | void UpdateSync(const hkbContext& a_context) override; // 19 91 | void Unk_1B(void) override; // 1B - { echoNextUpdate = true; } 92 | 93 | // members 94 | hkbEvent eventToSendWhenStateOrTransitionChanges; // 048 95 | hkRefPtr startStateChooser; // 060 96 | std::int32_t startStateID; // 068 97 | std::int32_t returnToPreviousStateEventID; // 06C 98 | std::int32_t randomTransitionEventID; // 070 99 | std::int32_t transitionToNextHigherStateEventID; // 074 100 | std::int32_t transitionToNextLowerStateEventID; // 078 101 | std::int32_t syncVariableIndex; // 07C 102 | std::int32_t currentStateID; // 080 103 | bool wrapAroundStateID; // 084 104 | std::int8_t maxSimultaneousTransitions; // 085 105 | stl::enumeration startStateMode; // 086 106 | stl::enumeration selfTransitionMode; // 087 107 | bool isActive; // 088 108 | std::uint8_t pad41; // 089 109 | std::uint16_t pad42; // 08A 110 | std::uint32_t pad44; // 08C 111 | hkArray states; // 090 112 | hkRefPtr wildcardTransitions; // 0A0 113 | hkRefVariant stateIDToIndexMap; // 0A8 114 | hkArray activeTransitions; // 0B0 115 | hkArray transitionFlags; // 0C0 116 | hkArray wildcardTransitionFlags; // 0D0 117 | hkArray delayedTransitions; // 0E0 118 | float timeInState; // 0F0 119 | float lastLocalTime; // 0F4 120 | std::int32_t previousStateID; // 0F8 121 | std::int32_t nextStartStateIndexOverride; // 0FC 122 | bool stateOrTransitionChanged; // 100 123 | bool echoNextUpdate; // 101 124 | std::uint16_t currentStateIndexAndEntered; // 102 125 | std::uint32_t pad0BC; // 104 126 | }; 127 | static_assert(sizeof(hkbStateMachine) == 0x108); 128 | } 129 | -------------------------------------------------------------------------------- /include/RE/H/hkpCharacterMovementUtil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/H/hkVector4.h" 4 | 5 | namespace RE 6 | { 7 | class hkpCharacterMovementUtil 8 | { 9 | public: 10 | 11 | struct hkpMovementUtilInput 12 | { 13 | /// Forward direction in world space 14 | hkVector4 forward; 15 | 16 | /// Up direction in world space 17 | hkVector4 up; 18 | 19 | /// Normal of the surface we're standing on in world space 20 | hkVector4 surfaceNormal; 21 | 22 | /// Our current velocity in world space 23 | hkVector4 currentVelocity; 24 | 25 | /// Our desired velocity in the surface frame 26 | hkVector4 desiredVelocity; 27 | 28 | /// Velocity of the surface we're standing on in world space 29 | hkVector4 surfaceVelocity; 30 | 31 | /// Gain for the character controller. 32 | /// This variable controls the acceleration of the character. It should be 33 | /// scaled by the current timestep to ensure that the characters acceleration 34 | /// is not timestep dependent. 35 | float gain; 36 | 37 | /// Limit the maximum acceleration of the character 38 | float maxVelocityDelta; 39 | }; 40 | 41 | /// Calculate a new output velocity based on the input 42 | static void CalculateMovement(const hkpMovementUtilInput& a_input, hkVector4& a_velocityOut) 43 | { 44 | using func_t = decltype(CalculateMovement); 45 | REL::Relocation func{ RELOCATION_ID(78988, 81002) }; 46 | 47 | func(a_input, a_velocityOut); 48 | } 49 | }; 50 | 51 | } -------------------------------------------------------------------------------- /include/RE/M/MotionDataContainer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/half.h" 4 | 5 | namespace RE 6 | { 7 | class MotionDataContainer 8 | { 9 | public: 10 | 11 | // Translation 12 | constexpr bool IsTranslationDataAligned() const 13 | { 14 | return !(translationDataPtr & 1); 15 | } 16 | 17 | inline NiPoint3& GetSegTranslation(int a_segIndex) const 18 | { 19 | return GetSegTranslationList()[a_segIndex - 1]; 20 | } 21 | 22 | inline NiPoint3& GetEndTranslation() const 23 | { 24 | return GetSegTranslation(translationSegCount); 25 | } 26 | 27 | inline float GetSegTranslationTime(int a_segIndex) const 28 | { 29 | return GetSegTranslationTimesList()[a_segIndex - 1]; 30 | } 31 | 32 | inline float GetEndTranslationTime() const 33 | { 34 | return GetSegTranslationTime(translationSegCount); 35 | } 36 | 37 | // Rotation 38 | constexpr bool IsRotationDataAligned() const 39 | { 40 | return !(rotationDataPtr & 1); 41 | } 42 | 43 | inline NiQuaternion& GetSegRotation(int a_segIndex) const 44 | { 45 | return GetSegRotationList()[a_segIndex - 1]; 46 | } 47 | 48 | inline NiQuaternion& GetEndRotation() const 49 | { 50 | return GetSegRotation(rotationSegCount); 51 | } 52 | 53 | inline float GetSegRotationTime(int a_segIndex) const 54 | { 55 | return GetSegRotationTimesList()[a_segIndex - 1]; 56 | } 57 | 58 | inline float GetEndRotationTime() const 59 | { 60 | return GetSegRotationTime(rotationSegCount); 61 | } 62 | 63 | private: 64 | 65 | // Translation 66 | inline std::uintptr_t TranslationDataAligned() const 67 | { 68 | return translationDataPtr & ~1; 69 | } 70 | 71 | inline NiPoint3* GetSegTranslationList() const 72 | { 73 | return reinterpret_cast(TranslationDataAligned()); 74 | } 75 | 76 | inline half* GetSegTranslationTimesList() const 77 | { 78 | return reinterpret_cast(&GetSegTranslationList()[translationSegCount]); 79 | } 80 | 81 | // Rotation 82 | inline std::uintptr_t RotationDataAligned() const 83 | { 84 | return rotationDataPtr & ~1; 85 | } 86 | 87 | inline NiQuaternion* GetSegRotationList() const 88 | { 89 | return reinterpret_cast(RotationDataAligned()); 90 | } 91 | 92 | inline half* GetSegRotationTimesList() const 93 | { 94 | return reinterpret_cast(&GetSegRotationList()[rotationSegCount]); 95 | } 96 | 97 | public: 98 | 99 | // members 100 | std::uintptr_t translationDataPtr; // 00 101 | unsigned int translationSegCount; // 08 102 | std::uintptr_t rotationDataPtr; // 10 103 | unsigned int rotationSegCount; // 18 104 | }; 105 | } -------------------------------------------------------------------------------- /include/RE/RTTI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //#include "RE/RTTI.h" 4 | 5 | namespace RE 6 | { 7 | namespace detail 8 | { 9 | template 10 | struct cast_to_is_valid : 11 | std::conjunction< 12 | types_are_compat< 13 | To, 14 | From>, 15 | target_is_valid< 16 | To>, 17 | implements_rtti> 18 | {}; 19 | 20 | template 21 | inline constexpr bool cast_to_is_valid_v = cast_to_is_valid::value; 22 | 23 | template 24 | inline constexpr bool target_is_valid_v = target_is_valid::value; 25 | } 26 | 27 | template < 28 | class T, 29 | std::enable_if_t< 30 | detail::target_is_valid_v, 31 | int> = 0> 32 | auto get_dynamic_cast_info(T* a_type) 33 | { 34 | auto vfTableAddr = *reinterpret_cast(a_type); 35 | auto col = *reinterpret_cast(vfTableAddr - sizeof(std::uintptr_t)); 36 | return std::pairtypeDescriptor), std::uint32_t>{ col->typeDescriptor, col->offset }; 37 | } 38 | } 39 | 40 | template < 41 | class To, 42 | class From, 43 | std::enable_if_t< 44 | RE::detail::cast_to_is_valid_v< 45 | To, From*>, 46 | int> = 0> 47 | To skyrim_cast(From* a_from) 48 | { 49 | /*auto vfTable = *reinterpret_cast(a_from); 50 | auto completeObjectLocator = *reinterpret_cast(vfTable - sizeof(void*)); 51 | 52 | auto from = completeObjectLocator->typeDescriptor; 53 | 54 | auto vfDelta = completeObjectLocatorFrom->offset;*/ 55 | 56 | auto [from, vfDelta] = RE::get_dynamic_cast_info(a_from); 57 | 58 | REL::Relocation to{ RE::detail::remove_cvpr_t::RTTI }; 59 | 60 | if (!from.get() || !to.get()) { 61 | return nullptr; 62 | } 63 | 64 | return static_cast( 65 | RE::RTDynamicCast( 66 | const_cast( 67 | static_cast(a_from)), 68 | vfDelta, 69 | from.get(), 70 | to.get(), 71 | false)); 72 | } -------------------------------------------------------------------------------- /include/Settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace SKSE::log 4 | { 5 | using level = spdlog::level::level_enum; 6 | } 7 | namespace logger = SKSE::log; 8 | 9 | namespace settings 10 | { 11 | void Init(const std::string& a_iniFileName); 12 | 13 | // Default values 14 | 15 | namespace debug 16 | { 17 | inline logger::level logLevel = logger::level::err; 18 | } 19 | } -------------------------------------------------------------------------------- /include/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/Skyrim.h" 4 | #include "REL/Relocation.h" 5 | #include "SKSE/SKSE.h" 6 | 7 | #include 8 | 9 | #ifndef NDEBUG 10 | #include 11 | #endif 12 | 13 | #define DLLEXPORT __declspec(dllexport) 14 | 15 | using namespace std::literals; 16 | using namespace REL::literals; 17 | -------------------------------------------------------------------------------- /include/utils/INISettingCollection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/I/INISettingCollection.h" 4 | 5 | #include "Setting.h" 6 | 7 | namespace utils 8 | { 9 | // Deriving from RE::INISettingCollection does not compile as the virtual functions are not defined (Unresolved external symbols). 10 | // The best way I could find is to replicate that class layout into mine. 11 | class INISettingCollection 12 | { 13 | static constexpr REL::RelocationID vTableId = RELOCATION_ID(230108, 187074); 14 | 15 | public: 16 | static INISettingCollection* GetSingleton() 17 | { 18 | static INISettingCollection singleton; 19 | 20 | return &singleton; 21 | } 22 | 23 | template 24 | void AddSettings(Last a_last) 25 | { 26 | _this()->InsertSetting(a_last); 27 | } 28 | 29 | template 30 | void AddSettings(First a_first, Rest... a_rest) 31 | { 32 | _this()->InsertSetting(a_first); 33 | AddSettings(a_rest...); 34 | } 35 | 36 | template 37 | T GetSetting(const char* a_name) const 38 | { 39 | return const_cast(_this())->GetSetting(a_name); 40 | } 41 | 42 | template <> 43 | bool GetSetting(const char* a_name) const 44 | { 45 | return GetSetting(a_name)->GetBool(); 46 | } 47 | template <> 48 | float GetSetting(const char* a_name) const 49 | { 50 | return GetSetting(a_name)->GetFloat(); 51 | } 52 | template <> 53 | std::int32_t GetSetting(const char* a_name) const 54 | { 55 | return GetSetting(a_name)->GetSInt(); 56 | } 57 | template <> 58 | RE::Color GetSetting(const char* a_name) const 59 | { 60 | return GetSetting(a_name)->GetColor(); 61 | } 62 | template <> 63 | const char* GetSetting(const char* a_name) const 64 | { 65 | return GetSetting(a_name)->GetString(); 66 | } 67 | template <> 68 | std::uint32_t GetSetting(const char* a_name) const 69 | { 70 | return GetSetting(a_name)->GetUInt(); 71 | } 72 | 73 | bool ReadFromFile(std::string_view a_fileName); 74 | 75 | private: 76 | INISettingCollection() noexcept; 77 | 78 | // Virtual table auto-generation. Needed to replace the original class. 79 | virtual ~INISettingCollection() = default; // 00 80 | 81 | virtual void InsertSetting(RE::Setting*) { throw(""); } // 01 82 | virtual void RemoveSetting(RE::Setting*) { throw(""); } // 02 83 | virtual bool WriteSetting(RE::Setting*) { throw(""); } // 03 84 | virtual bool ReadSetting(RE::Setting*) { throw(""); } // 04 85 | virtual bool OpenHandle(bool) { throw(""); } // 05 86 | virtual bool CloseHandle() { throw(""); } // 06 87 | virtual void Unk_07() { throw(""); } // 07 88 | virtual void Unk_08() { throw(""); } // 08 89 | virtual void Unk_09() { throw(""); } // 09 90 | 91 | RE::INISettingCollection* _this() { return reinterpret_cast(this); } 92 | const RE::INISettingCollection* _this() const { return reinterpret_cast(this); } 93 | 94 | // members 95 | char subKey[MAX_PATH]; // 008 96 | std::uint32_t pad10C; // 10C 97 | void* handle; // 110 98 | RE::BSSimpleList settings; // 118 99 | }; 100 | static_assert(sizeof(INISettingCollection) == sizeof(RE::INISettingCollection)); 101 | } 102 | -------------------------------------------------------------------------------- /include/utils/Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "PCH.h" 4 | 5 | namespace SKSE::log 6 | { 7 | using level = spdlog::level::level_enum; 8 | 9 | inline void set_level(level a_log_level, level a_flush_level) 10 | { 11 | spdlog::default_logger()->set_level(a_log_level); 12 | spdlog::default_logger()->flush_on(a_flush_level); 13 | } 14 | 15 | inline bool init(const std::string_view& a_log_name) 16 | { 17 | if (!log_directory()) 18 | { 19 | return false; 20 | } 21 | 22 | std::filesystem::path path = *log_directory() / a_log_name; 23 | path += ".log"; 24 | std::shared_ptr log = spdlog::basic_logger_mt("global log", path.string(), true); 25 | 26 | spdlog::set_default_logger(std::move(log)); 27 | 28 | set_level(level::info, level::info); 29 | 30 | spdlog::set_pattern("%D - %H:%M:%S.%f [%^%l%$] %v"); 31 | 32 | return true; 33 | } 34 | 35 | inline void flush() 36 | { 37 | spdlog::default_logger()->flush(); 38 | } 39 | 40 | template 41 | void at_level(spdlog::level::level_enum a_level, fmt::format_string a_fmt, Args&&... args) 42 | { 43 | switch (a_level) { 44 | case level::trace: 45 | trace(a_fmt, std::forward(args)...); 46 | break; 47 | case level::debug: 48 | debug(a_fmt, std::forward(args)...); 49 | break; 50 | case level::info: 51 | info(a_fmt, std::forward(args)...); 52 | break; 53 | case level::warn: 54 | warn(a_fmt, std::forward(args)...); 55 | break; 56 | case level::err: 57 | error(a_fmt, std::forward(args)...); 58 | break; 59 | case level::critical: 60 | critical(a_fmt, std::forward(args)...); 61 | break; 62 | } 63 | } 64 | } 65 | 66 | namespace GFxLogger 67 | { 68 | struct info; 69 | struct error; 70 | 71 | bool RegisterStaticFunctions(RE::GFxMovieView* a_view); 72 | } 73 | 74 | namespace logger = SKSE::log; -------------------------------------------------------------------------------- /include/utils/Setting.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RE/S/Setting.h" 4 | 5 | #include 6 | 7 | namespace utils 8 | { 9 | // Deriving from RE::Setting does not compile as the virtual functions are not defined (Unresolved external symbols). 10 | // The best way I could find is to replicate that class layout into mine. 11 | class Setting 12 | { 13 | template 14 | friend RE::Setting* MakeSetting(const char* a_name, T a_data); 15 | 16 | using Type = RE::Setting::Type; 17 | 18 | template 19 | Setting(const char* a_name, T a_data) 20 | requires(std::same_as || 21 | std::same_as || 22 | std::same_as || 23 | std::same_as || 24 | std::same_as) 25 | { 26 | std::size_t nameLen = std::strlen(a_name) + sizeof('\0'); 27 | 28 | if (nameLen < MAX_PATH) 29 | { 30 | name = new char[nameLen]; 31 | strcpy_s(name, nameLen, a_name); 32 | 33 | if constexpr (std::is_same_v) 34 | { 35 | if (GetType() == Type::kString) 36 | { 37 | std::size_t dataLen = std::strlen(a_data) + sizeof('\0'); 38 | if (dataLen < MAX_PATH) 39 | { 40 | data.s = new char[dataLen]; 41 | strcpy_s(data.s, dataLen, a_data); 42 | 43 | return; 44 | } 45 | } 46 | } 47 | else if constexpr (std::is_same_v) 48 | { 49 | if (GetType() == Type::kBool) 50 | { 51 | data.b = a_data; 52 | 53 | return; 54 | } 55 | } 56 | else if constexpr (std::is_same_v) 57 | { 58 | if (GetType() == Type::kSignedInteger) 59 | { 60 | data.i = a_data; 61 | 62 | return; 63 | } 64 | } 65 | else if constexpr (std::is_same_v) 66 | { 67 | if (GetType() == Type::kUnsignedInteger) 68 | { 69 | data.u = a_data; 70 | 71 | return; 72 | } 73 | } 74 | else if constexpr (std::is_same_v) 75 | { 76 | if (GetType() == Type::kFloat) 77 | { 78 | data.f = a_data; 79 | 80 | return; 81 | } 82 | } 83 | 84 | delete[] name; 85 | name = nullptr; 86 | } 87 | } 88 | 89 | virtual ~Setting() // 00 90 | { 91 | if (name) 92 | { 93 | if (GetType() == Type::kString) 94 | { 95 | delete[] data.s; 96 | } 97 | 98 | delete[] name; 99 | } 100 | } 101 | 102 | // For the virtual table auto-generation. 103 | virtual bool Unk_01(void) { return false; } // 01 104 | 105 | Type GetType() const 106 | { 107 | return reinterpret_cast(this)->GetType(); 108 | } 109 | 110 | // members 111 | RE::Setting::Data data{}; // 08 112 | char* name; // 10 113 | }; 114 | static_assert(sizeof(Setting) == sizeof(RE::Setting)); 115 | 116 | template 117 | RE::Setting* MakeSetting(const char* a_name, T a_data) 118 | { 119 | return reinterpret_cast(new Setting{ a_name, a_data }); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /include/utils/Trampoline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(SKSE_SUPPORT_XBYAK) 4 | 5 | #include 6 | 7 | #include "SKSE/Trampoline.h" 8 | 9 | namespace utils 10 | { 11 | template 12 | static constexpr std::size_t GetTrampolineBaseSize() 13 | { 14 | static_assert(N == 5 || N == 6); 15 | 16 | // Reference: write_5branch() and write_6branch() of Trampoline.h 17 | if constexpr (N == 5) { 18 | #pragma pack(push, 1) 19 | // FF /4 20 | // JMP r/m64 21 | struct TrampolineAssembly 22 | { 23 | // jmp [rip] 24 | std::uint8_t jmp; // 0 - 0xFF 25 | std::uint8_t modrm; // 1 - 0x25 26 | std::int32_t disp; // 2 - 0x00000000 27 | std::uint64_t addr; // 6 - [rip] 28 | }; 29 | #pragma pack(pop) 30 | 31 | return sizeof(TrampolineAssembly); 32 | } 33 | 34 | return sizeof(std::uintptr_t); 35 | } 36 | 37 | template 38 | static constexpr void AllocExactSizeTrampoline(Xbyak::CodeGenerator& a_hookCode) 39 | { 40 | a_hookCode.ready(); 41 | 42 | SKSE::AllocTrampoline(GetTrampolineBaseSize() + a_hookCode.getSize()); 43 | } 44 | 45 | template 46 | static constexpr void AllocExactSizeTrampoline() 47 | { 48 | SKSE::AllocTrampoline(GetTrampolineBaseSize()); 49 | } 50 | 51 | template 52 | static constexpr std::uintptr_t WriteBranchTrampoline(std::uintptr_t a_location, const Xbyak::CodeGenerator& a_hookCode) 53 | { 54 | auto& hookCode = const_cast(a_hookCode); 55 | 56 | AllocExactSizeTrampoline(hookCode); 57 | 58 | return SKSE::GetTrampoline().write_branch(a_location, SKSE::GetTrampoline().allocate(hookCode)); 59 | } 60 | 61 | template 62 | static constexpr std::uintptr_t WriteCallTrampoline(std::uintptr_t a_location, const Xbyak::CodeGenerator& a_hookCode) 63 | { 64 | auto& hookCode = const_cast(a_hookCode); 65 | 66 | AllocExactSizeTrampoline(hookCode); 67 | 68 | return SKSE::GetTrampoline().write_call(a_location, SKSE::GetTrampoline().allocate(hookCode)); 69 | } 70 | } 71 | 72 | #else 73 | #error "Xbyak support needed for Trampoline" 74 | #endif -------------------------------------------------------------------------------- /include/utils/half.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | class half 8 | { 9 | public: 10 | 11 | //half(std::uint16_t a_value) 12 | //: value{ a_value } 13 | //{ } 14 | 15 | half(float a_value) 16 | : value{ _mm_cvtps_ph([&a_value]() -> __m128 17 | { 18 | __m128 value128; 19 | value128.m128_f32[0] = a_value; 20 | 21 | return value128; 22 | }(), 1).m128i_u16[0] 23 | } 24 | { } 25 | 26 | inline operator float() 27 | { 28 | __m128i value128; 29 | 30 | value128.m128i_u16[0] = value; 31 | 32 | return _mm_cvtph_ps(value128).m128_f32[0]; 33 | } 34 | 35 | private: 36 | 37 | std::uint16_t value; 38 | }; -------------------------------------------------------------------------------- /source/Hooks.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks.h" 2 | 3 | #include "AnimMotionHandler.h" 4 | 5 | #include "utils/Logger.h" 6 | 7 | #include "RE/H/hkpCharacterMovementUtil.h" 8 | #include "RE/H/hkVector4.h" 9 | 10 | #include "RE/RTTI.h" 11 | 12 | namespace hooks 13 | { 14 | // Container for each character instance that is playing an animation with custom motion data 15 | class CharacterClipAnimMotionMap 16 | { 17 | public: 18 | static CharacterClipAnimMotionMap* GetSingleton() 19 | { 20 | static CharacterClipAnimMotionMap singleton; 21 | 22 | return &singleton; 23 | } 24 | 25 | template 26 | void Add(const RE::hkbCharacter* a_hkbCharacter, const StringT& a_clipName, const AnimMotionData& a_animMotionData) 27 | { 28 | std::string clipName{ a_clipName.c_str() }; 29 | 30 | try { 31 | data[a_hkbCharacter][clipName] = a_animMotionData; 32 | } catch (const std::exception& e) { 33 | logger::warn("Exception thrown: {}, clearing to avoid possible memory overflow", e.what()); 34 | logger::flush(); 35 | 36 | data.clear(); 37 | } 38 | } 39 | 40 | template 41 | AnimMotionData* Get(const RE::hkbCharacter* a_hkbCharacter, const StringT& a_clipName) 42 | { 43 | std::string clipName{ a_clipName.c_str() }; 44 | 45 | if (data.contains(a_hkbCharacter) && data[a_hkbCharacter].contains(clipName)) { 46 | return &data[a_hkbCharacter][clipName]; 47 | } else { 48 | return nullptr; 49 | } 50 | } 51 | 52 | template 53 | void Remove(const RE::hkbCharacter* a_hkbCharacter, const StringT& a_clipName) 54 | { 55 | std::string clipName{ a_clipName.c_str() }; 56 | 57 | if (data.contains(a_hkbCharacter) && data[a_hkbCharacter].contains(clipName)) { 58 | if (data[a_hkbCharacter].size() == 1) { 59 | data.erase(a_hkbCharacter); 60 | } else { 61 | data[a_hkbCharacter].erase(clipName); 62 | } 63 | } 64 | } 65 | 66 | mutable RE::BSSpinLock lock; 67 | 68 | private: 69 | std::map> data; 70 | }; 71 | 72 | // Called when animations are activated by clip generators 73 | std::uint32_t hkbClipGenerator::ComputeStartTime_Hook(const RE::hkbClipGenerator* a_this, const RE::hkbContext* a_hkbContext) 74 | { 75 | auto characterClipAnimMotionMap = CharacterClipAnimMotionMap::GetSingleton(); 76 | 77 | RE::BSSpinLockGuard lockguard(characterClipAnimMotionMap->lock); 78 | 79 | const RE::hkaAnimation* boundAnimation = GetBoundAnimation(a_this); 80 | 81 | if (boundAnimation) 82 | { 83 | const RE::hkbCharacter* hkbCharacter = a_hkbContext ? a_hkbContext->character : nullptr; 84 | 85 | if (hkbCharacter) 86 | { 87 | AnimMotionData* animMotionData = characterClipAnimMotionMap->Get(hkbCharacter, a_this->name); 88 | 89 | // Animation activation is multithreaded, therefore I need to keep track of the number of times 90 | // they are activated 91 | if (animMotionData && animMotionData->animation == boundAnimation) 92 | { 93 | animMotionData->activeCount++; 94 | } 95 | else 96 | { 97 | Translation* translation = nullptr; 98 | Rotation* rotation = nullptr; 99 | 100 | for (const RE::hkaAnnotationTrack& annotationTrack : boundAnimation->annotationTracks) 101 | { 102 | for (const RE::hkaAnnotationTrack::Annotation& annotation : annotationTrack.annotations) 103 | { 104 | auto dataParsed = ParseAnnotation(annotation); 105 | 106 | translation = std::get_if(&dataParsed); 107 | 108 | if (translation) 109 | { 110 | if (animMotionData && animMotionData->animation == boundAnimation) 111 | { 112 | animMotionData->Add(translation); 113 | } 114 | else 115 | { 116 | characterClipAnimMotionMap->Add(hkbCharacter, a_this->name, AnimMotionData{ boundAnimation, translation }); 117 | 118 | if (!animMotionData) 119 | { 120 | animMotionData = characterClipAnimMotionMap->Get(hkbCharacter, a_this->name); 121 | } 122 | } 123 | } 124 | else 125 | { 126 | rotation = std::get_if(&dataParsed); 127 | 128 | if (rotation) 129 | { 130 | if (animMotionData && animMotionData->animation == boundAnimation) 131 | { 132 | animMotionData->Add(rotation); 133 | } 134 | else 135 | { 136 | characterClipAnimMotionMap->Add(hkbCharacter, a_this->name, AnimMotionData{ boundAnimation, rotation }); 137 | 138 | if (!animMotionData) 139 | { 140 | animMotionData = characterClipAnimMotionMap->Get(hkbCharacter, a_this->name); 141 | } 142 | } 143 | } 144 | } 145 | } 146 | 147 | if (animMotionData) 148 | { 149 | animMotionData->SortListsByTime(); 150 | 151 | if (!animMotionData->translationList.empty() && animMotionData->translationList.back().time != boundAnimation->duration) 152 | { 153 | logger::warn("Animation={} of hkbCharacter=0x{:08x} ends at {}, while custom translation ends at {}", 154 | a_this->animationName.c_str(), reinterpret_cast(hkbCharacter), 155 | boundAnimation->duration, animMotionData->translationList.back().time); 156 | } 157 | 158 | if (!animMotionData->rotationList.empty() && animMotionData->rotationList.back().time != boundAnimation->duration) 159 | { 160 | logger::warn("Animation={} of hkbCharacter=0x{:08x} ends at {}, while custom rotation ends at {}", 161 | a_this->animationName.c_str(), reinterpret_cast(hkbCharacter), 162 | boundAnimation->duration, animMotionData->rotationList.back().time); 163 | } 164 | 165 | // Support only for annotations in the same track, quit the loop when found 166 | break; 167 | } 168 | } 169 | } 170 | } 171 | } 172 | 173 | return ComputeStartTime(a_this); 174 | } 175 | 176 | // Called when animations are deactivated by clip generators 177 | void hkbClipGenerator::ResetIgnoreStartTime_Hook(const RE::hkbClipGenerator* a_this, const RE::hkbContext* a_hkbContext) 178 | { 179 | auto characterClipAnimMotionMap = CharacterClipAnimMotionMap::GetSingleton(); 180 | 181 | RE::BSSpinLockGuard lockguard(characterClipAnimMotionMap->lock); 182 | 183 | const RE::hkaAnimation* boundAnimation = GetBoundAnimation(a_this); 184 | 185 | if (boundAnimation) 186 | { 187 | const RE::hkbCharacter* hkbCharacter = a_hkbContext ? a_hkbContext->character : nullptr; 188 | 189 | if (hkbCharacter) 190 | { 191 | AnimMotionData* animMotionData = characterClipAnimMotionMap->Get(hkbCharacter, a_this->name); 192 | 193 | // Animation deactivation is also multithreaded, so keep track of the number of 194 | // activated times left 195 | if (animMotionData && animMotionData->animation == boundAnimation) 196 | { 197 | animMotionData->activeCount--; 198 | 199 | // Erase from the list when deactivated same times as activated 200 | if (!animMotionData->activeCount) 201 | { 202 | characterClipAnimMotionMap->Remove(hkbCharacter, a_this->name); 203 | } 204 | } 205 | } 206 | } 207 | 208 | ResetIgnoreStartTime(a_this); 209 | } 210 | 211 | void MotionDataContainer::ProcessTranslationData_Hook(RE::MotionDataContainer* a_this, float a_motionTime, 212 | RE::NiPoint3& a_translation, const RE::BSFixedString* a_clipName, 213 | RE::Character* a_character) 214 | { 215 | auto characterClipAnimMotionMap = CharacterClipAnimMotionMap::GetSingleton(); 216 | 217 | RE::BSSpinLockGuard lockguard(characterClipAnimMotionMap->lock); 218 | 219 | RE::hkbCharacter* hkbCharacter = GethkbCharacter(a_character); 220 | 221 | AnimMotionData* animMotionData = characterClipAnimMotionMap->Get(hkbCharacter, *a_clipName); 222 | 223 | std::vector* customTranslationList = animMotionData ? &animMotionData->translationList : nullptr; 224 | 225 | bool hasCustomMotionList = customTranslationList && !customTranslationList->empty(); 226 | 227 | if (hasCustomMotionList) 228 | { 229 | float endMotionTime = customTranslationList->back().time; 230 | 231 | float curMotionTime = (a_motionTime > endMotionTime) ? endMotionTime : a_motionTime; 232 | 233 | auto segCount = static_cast(customTranslationList->size()); 234 | 235 | for (std::uint32_t segIndex = 1; segIndex <= segCount; segIndex++) 236 | { 237 | float curSegMotionTime = customTranslationList->at(segIndex - 1).time; 238 | 239 | if (curMotionTime <= curSegMotionTime) 240 | { 241 | std::uint32_t prevSegIndex = segIndex - 1; 242 | float segProgress = 1.0f; 243 | 244 | float prevSegMotionTime = prevSegIndex ? customTranslationList->at(prevSegIndex - 1).time : 0.0f; 245 | 246 | float curSegMotionDuration = curSegMotionTime - prevSegMotionTime; 247 | if (curSegMotionDuration > std::numeric_limits::epsilon()) 248 | { 249 | segProgress = (curMotionTime - prevSegMotionTime) / curSegMotionDuration; 250 | } 251 | 252 | const RE::NiPoint3& curSegTranslation = customTranslationList->at(segIndex - 1).delta; 253 | 254 | const RE::NiPoint3& prevSegTranslation = prevSegIndex ? 255 | customTranslationList->at(prevSegIndex - 1).delta : 256 | RE::NiPoint3{ 0.0f, 0.0f, 0.0f }; 257 | 258 | a_translation = (curSegTranslation * segProgress + prevSegTranslation * (1.0f - segProgress)); 259 | 260 | auto charController = a_character->GetCharController(); 261 | auto charStateOnGround = reinterpret_cast(charController->context.stateManager->registeredState[RE::hkpCharacterStateType::kOnGround]); 262 | 263 | charStateOnGround->unk10 = a_translation.z == 0.0; 264 | 265 | 266 | return; 267 | } 268 | } 269 | } 270 | else 271 | { 272 | // The game checks this in the original code, so we do 273 | bool hasVanillaMotionList = a_this->translationSegCount > static_cast(a_this->IsTranslationDataAligned()); 274 | 275 | if (hasVanillaMotionList) 276 | { 277 | ProcessTranslationData(&a_this->translationDataPtr, a_motionTime, a_translation); 278 | 279 | return; 280 | } 281 | } 282 | 283 | a_translation = RE::NiPoint3{ 0.0f, 0.0f, 0.0f }; 284 | } 285 | 286 | void MotionDataContainer::ProcessRotationData_Hook(RE::MotionDataContainer* a_this, float a_motionTime, 287 | RE::NiQuaternion& a_rotation, const RE::BSFixedString* a_clipName, 288 | RE::Character* a_character) 289 | { 290 | auto characterClipAnimMotionMap = CharacterClipAnimMotionMap::GetSingleton(); 291 | RE::BSSpinLockGuard lockguard(characterClipAnimMotionMap->lock); 292 | 293 | RE::hkbCharacter* hkbCharacter = GethkbCharacter(a_character); 294 | 295 | auto characterController = a_character->GetCharController(); 296 | 297 | AnimMotionData* animMotionData = characterClipAnimMotionMap->Get(hkbCharacter, *a_clipName); 298 | 299 | std::vector* customRotationList = animMotionData ? &animMotionData->rotationList : nullptr; 300 | 301 | bool hasCustomMotionList = customRotationList && !customRotationList->empty(); 302 | 303 | if (hasCustomMotionList) 304 | { 305 | float endMotionTime = customRotationList->back().time; 306 | 307 | float curMotionTime = (a_motionTime > endMotionTime) ? endMotionTime : a_motionTime; 308 | 309 | for (std::uint32_t segIndex = 1; segIndex <= customRotationList->size(); segIndex++) 310 | { 311 | float curSegMotionTime = customRotationList->at(segIndex - 1).time; 312 | 313 | if (curMotionTime <= curSegMotionTime) 314 | { 315 | std::uint32_t prevSegIndex = segIndex - 1; 316 | float segProgress = 1.0f; 317 | 318 | float prevSegMotionTime = prevSegIndex ? customRotationList->at(prevSegIndex - 1).time : 0.0f; 319 | 320 | float curSegMotionDuration = curSegMotionTime - prevSegMotionTime; 321 | if (curSegMotionDuration > std::numeric_limits::epsilon()) 322 | { 323 | segProgress = (curMotionTime - prevSegMotionTime) / curSegMotionDuration; 324 | } 325 | const RE::NiQuaternion& curSegRotation = customRotationList->at(segIndex - 1).delta; 326 | const RE::NiQuaternion& prevSegRotation = prevSegIndex ? 327 | customRotationList->at(prevSegIndex - 1).delta : 328 | RE::NiQuaternion{ 1.0f, 0.0f, 0.0f, 0.0f }; 329 | 330 | InterpolateRotation(a_rotation, segProgress, prevSegRotation, curSegRotation); 331 | 332 | return; 333 | } 334 | } 335 | } 336 | else 337 | { 338 | // The game checks this in the original code, so we do 339 | bool hasVanillaMotionList = a_this->rotationSegCount > static_cast(a_this->IsRotationDataAligned()); 340 | 341 | if (hasVanillaMotionList) 342 | { 343 | ProcessRotationData(&a_this->rotationDataPtr, a_motionTime, a_rotation); 344 | 345 | return; 346 | } 347 | } 348 | 349 | a_rotation = RE::NiQuaternion{ 1.0f, 0.0f, 0.0f, 0.0f }; 350 | } 351 | 352 | RE::hkpCharacterStateType hkpCharacterContext::GetCharacterState_Hook(RE::hkpCharacterContext* a_this) 353 | { 354 | auto charStateOnGround = reinterpret_cast(a_this->stateManager->registeredState[RE::hkpCharacterStateType::kOnGround]); 355 | 356 | return charStateOnGround->unk10 ? a_this->currentState : RE::hkpCharacterStateType::kSwimming; 357 | } 358 | 359 | void SimulateStatePhysics(RE::bhkCharacterStateOnGround* a_this, RE::bhkCharacterController* a_characterController) 360 | { 361 | RE::Character* character = nullptr; 362 | for (RE::BSTEventSink* sink : a_characterController->sinks) 363 | { 364 | character = skyrim_cast(sink); 365 | if (character) 366 | { 367 | break; 368 | } 369 | else 370 | { 371 | character = skyrim_cast(sink); 372 | if (character) 373 | { 374 | break; 375 | } 376 | } 377 | } 378 | 379 | RE::BSAnimationGraphManagerPtr animGraphManager; 380 | 381 | if (character && character->GetAnimationGraphManager(animGraphManager)) 382 | { 383 | std::uint32_t activeGraph = animGraphManager->GetRuntimeData().activeGraph; 384 | 385 | RE::BShkbAnimationGraph* animGraph = animGraphManager->graphs[activeGraph].get(); 386 | RE::BSTEventSource* animGraphEventSource = animGraph; 387 | 388 | RE::BSAnimationGraphEvent event{}; 389 | 390 | animGraphEventSource->SendEvent(&event); 391 | } 392 | 393 | RE::bhkWorld* world = a_characterController->GetHavokWorld(); 394 | 395 | RE::hkVector4 position = a_characterController->GetPosition(); 396 | 397 | a_characterController->fallStartHeight = position.z * 69.991249; 398 | 399 | std::uint32_t collisionFilterInfo; 400 | a_characterController->GetCollisionFilterInfo(collisionFilterInfo); 401 | 402 | RE::bhkPickData pickData; 403 | pickData.rayInput.from = position; 404 | pickData.rayInput.to = position - RE::hkVector4{ 0.0F, 0.0F, 0.5F, 0.0F }; 405 | pickData.rayInput.filterInfo = collisionFilterInfo; 406 | 407 | world->PickObject(pickData); 408 | 409 | if (pickData.rayOutput.rootCollidable) 410 | { 411 | if (pickData.rayOutput.normal.z < a_characterController->unk300) 412 | { 413 | a_characterController->flags.set(RE::CHARACTER_FLAGS::kSupport); 414 | } 415 | } 416 | else 417 | { 418 | a_characterController->wantState = RE::hkpCharacterStateType::kInAir; 419 | a_characterController->flags.reset(RE::CHARACTER_FLAGS::kSupport); 420 | } 421 | 422 | RE::hkVector4 desiredVelocity = a_characterController->velocityMod; 423 | 424 | desiredVelocity.x = a_characterController->velocityMod.y; 425 | desiredVelocity.y = a_characterController->velocityMod.x; 426 | 427 | RE::hkpCharacterMovementUtil::hkpMovementUtilInput movementUtil; 428 | movementUtil.forward = a_characterController->forwardVec; 429 | movementUtil.up = RE::hkVector4{ 0.0F, 0.0F, 1.0F, 0.0F }; 430 | movementUtil.surfaceNormal = a_characterController->supportNorm; 431 | movementUtil.currentVelocity = a_characterController->outVelocity; 432 | movementUtil.desiredVelocity = desiredVelocity; 433 | movementUtil.surfaceVelocity = a_characterController->surfaceInfo.surfaceVelocity; 434 | movementUtil.gain = 1.0F; 435 | movementUtil.maxVelocityDelta = 500.0F; 436 | 437 | RE::hkpCharacterMovementUtil::CalculateMovement(movementUtil, a_characterController->outVelocity); 438 | 439 | a_characterController->outVelocity += a_characterController->surfaceInfo.surfaceVelocity; 440 | 441 | float gravity = world->GetGravity().quad.m128_f32[0]; 442 | 443 | a_characterController->outVelocity += gravity * a_characterController->stepInfo.deltaTime; 444 | 445 | a_characterController->SetWantedState(); 446 | 447 | RE::hkpCharacterStateType stateType = a_characterController->context.currentState; 448 | 449 | if (stateType != RE::hkpCharacterStateType::kOnGround) 450 | { 451 | auto characterState = static_cast(a_characterController->context.stateManager->registeredState[stateType]); 452 | 453 | characterState->SimulateStatePhysics(a_characterController); 454 | } 455 | } 456 | } -------------------------------------------------------------------------------- /source/Settings.cpp: -------------------------------------------------------------------------------- 1 | #include "Settings.h" 2 | 3 | #include "utils/INISettingCollection.h" 4 | 5 | namespace settings 6 | { 7 | using namespace utils; 8 | 9 | void Init(const std::string& a_iniFileName) 10 | { 11 | INISettingCollection* iniSettingCollection = INISettingCollection::GetSingleton(); 12 | 13 | { 14 | using namespace debug; 15 | iniSettingCollection->AddSettings( 16 | MakeSetting("uLogLevel:Debug", static_cast(logLevel))); 17 | } 18 | 19 | if (!iniSettingCollection->ReadFromFile(a_iniFileName)) 20 | { 21 | logger::warn("Could not read {}, falling back to default options", a_iniFileName); 22 | } 23 | 24 | { 25 | using namespace debug; 26 | logLevel = static_cast(iniSettingCollection->GetSetting("uLogLevel:Debug")); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /source/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks.h" 2 | #include "Settings.h" 3 | 4 | #include "utils/Logger.h" 5 | 6 | SKSEPluginLoad(const SKSE::LoadInterface* a_skse) 7 | { 8 | REL::Module::reset(); 9 | 10 | const SKSE::PluginDeclaration* plugin = SKSE::PluginDeclaration::GetSingleton(); 11 | 12 | if (!logger::init(plugin->GetName())) 13 | { 14 | return false; 15 | } 16 | 17 | logger::info("Loading {} {}...", plugin->GetName(), plugin->GetVersion()); 18 | 19 | SKSE::Init(a_skse); 20 | 21 | settings::Init(std::string(plugin->GetName()) + ".ini"); 22 | 23 | logger::set_level(settings::debug::logLevel, settings::debug::logLevel); 24 | 25 | hooks::Install(); 26 | 27 | logger::set_level(logger::level::info, logger::level::info); 28 | logger::info("Succesfully loaded!"); 29 | 30 | logger::set_level(settings::debug::logLevel, settings::debug::logLevel); 31 | 32 | return true; 33 | } 34 | -------------------------------------------------------------------------------- /source/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /source/utils/INISettingCollection.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/INISettingCollection.h" 2 | 3 | #include "utils/Logger.h" 4 | 5 | namespace utils 6 | { 7 | INISettingCollection::INISettingCollection() noexcept 8 | { 9 | REL::Relocation __vTable(*reinterpret_cast(this)); 10 | 11 | // Use the Skyrim's INISettingCollection virtual functions 12 | static std::uintptr_t* skyrimsVTable = REL::Relocation{ vTableId }.get(); 13 | 14 | // Replace all except the destructor (index 0) 15 | for (int i = 1; i < 10; i++) { 16 | __vTable.write_vfunc(i, skyrimsVTable[i]); 17 | } 18 | } 19 | 20 | bool INISettingCollection::ReadFromFile(std::string_view a_fileName) 21 | { 22 | std::filesystem::path iniPath = std::filesystem::current_path().append("Data\\SKSE\\Plugins").append(a_fileName); 23 | 24 | // Reference: decompiled source code in 1.5.97 25 | if (iniPath.string().c_str()) { 26 | if (iniPath.string().c_str() != subKey) { 27 | strcpy_s(subKey, iniPath.string().c_str()); 28 | } 29 | } else { 30 | subKey[0] = '\0'; 31 | } 32 | 33 | if (_this()->OpenHandle(false)) { 34 | _this()->Unk_09(); 35 | _this()->CloseHandle(); 36 | 37 | return true; 38 | } 39 | 40 | return false; 41 | } 42 | } -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "registries": [ 3 | { 4 | "kind": "git", 5 | "repository": "https://gitlab.com/colorglass/vcpkg-colorglass", 6 | "baseline": "1b279b20c7e0db1c9d549ff3b64e024c01317b55", 7 | "packages": [ 8 | "commonlibsse", 9 | "commonlibsse-po3-ae", 10 | "commonlibsse-po3-se", 11 | "commonlibvr", 12 | "commonlibsse-ng", 13 | "commonlibsse-ng-ae", 14 | "commonlibsse-ng-se", 15 | "commonlibsse-ng-vr", 16 | "commonlibsse-ng-flatrim" 17 | ] 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "animationmotionrevolution", 3 | "version-string": "1.5.3", 4 | "description": "", 5 | "homepage": "https://github.com/alexsylex/AnimationMotionRevolution", 6 | "license": "MIT", 7 | "dependencies": [ 8 | "boost-atomic", 9 | "boost-stl-interfaces", 10 | "rsm-binary-io", 11 | "span-lite", 12 | "xbyak", 13 | "tsl-ordered-map", 14 | "commonlibsse-ng" 15 | ] 16 | } -------------------------------------------------------------------------------- /version.rc.in: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 1 VERSIONINFO 4 | FILEVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 5 | PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 6 | FILEFLAGSMASK 0x17L 7 | #ifdef _DEBUG 8 | FILEFLAGS 0x1L 9 | #else 10 | FILEFLAGS 0x0L 11 | #endif 12 | FILEOS 0x4L 13 | FILETYPE 0x1L 14 | FILESUBTYPE 0x0L 15 | BEGIN 16 | BLOCK "StringFileInfo" 17 | BEGIN 18 | BLOCK "040904b0" 19 | BEGIN 20 | VALUE "FileDescription", "@PROJECT_NAME@" 21 | VALUE "FileVersion", "@PROJECT_VERSION@.0" 22 | VALUE "InternalName", "@PROJECT_NAME@" 23 | VALUE "LegalCopyright", "MIT License" 24 | VALUE "ProductName", "@PROJECT_NAME@" 25 | VALUE "ProductVersion", "@PROJECT_VERSION@.0" 26 | END 27 | END 28 | BLOCK "VarFileInfo" 29 | BEGIN 30 | VALUE "Translation", 0x409, 1200 31 | END 32 | END 33 | --------------------------------------------------------------------------------