diff --git a/examples/example_plugin/Plugin.cpp b/examples/example_plugin/Plugin.cpp index 4ca9f672..f84ce2dc 100644 --- a/examples/example_plugin/Plugin.cpp +++ b/examples/example_plugin/Plugin.cpp @@ -186,6 +186,31 @@ class ExamplePlugin : public uevr::Plugin { std::string name_narrow{std::wstring_convert>{}.to_bytes(test_name.to_string())}; API::get()->log_info("Test FName: %s", name_narrow.c_str()); + // Test attaching skeletal mesh components with UObjectHook. + struct { + API::UClass* c; + API::TArray return_value{}; + } component_params; + + component_params.c = API::get()->find_uobject(L"Class /Script/Engine.SkeletalMeshComponent"); + const auto pawn = API::get()->get_local_pawn(0); + + if (component_params.c != nullptr && pawn != nullptr) { + // either or. + pawn->call_function(L"K2_GetComponentsByClass", &component_params); + pawn->call_function(L"GetComponentsByClass", &component_params); + + if (component_params.return_value.empty()) { + API::get()->log_error("Failed to find any SkeletalMeshComponents"); + } + + for (auto mesh : component_params.return_value) { + auto state = API::UObjectHook::get_or_add_motion_controller_state(mesh); + } + } else { + API::get()->log_error("Failed to find SkeletalMeshComponent class or local pawn"); + } + // Iterate over all console variables. const auto console_manager = API::get()->get_console_manager(); diff --git a/include/uevr/API.h b/include/uevr/API.h index afa3b3d5..a6f3595c 100644 --- a/include/uevr/API.h +++ b/include/uevr/API.h @@ -36,7 +36,7 @@ SOFTWARE. #define UEVR_OUT #define UEVR_PLUGIN_VERSION_MAJOR 2 -#define UEVR_PLUGIN_VERSION_MINOR 12 +#define UEVR_PLUGIN_VERSION_MINOR 13 #define UEVR_PLUGIN_VERSION_PATCH 0 #define UEVR_RENDERER_D3D11 0 @@ -298,6 +298,15 @@ typedef struct { UEVR_FNameHandle (*get_fname)(UEVR_UObjectHandle object); } UEVR_UObjectFunctions; +DECLARE_UEVR_HANDLE(UEVR_UObjectHookMotionControllerStateHandle); + +typedef struct { + void (*set_rotation_offset)(UEVR_UObjectHookMotionControllerStateHandle, const UEVR_Quaternionf* rotation); + void (*set_location_offset)(UEVR_UObjectHookMotionControllerStateHandle, const UEVR_Vector3f* location); + void (*set_hand)(UEVR_UObjectHookMotionControllerStateHandle, unsigned int hand); + void (*set_permanent)(UEVR_UObjectHookMotionControllerStateHandle, bool permanent); +} UEVR_UObjectHookMotionControllerStateFunctions; + typedef struct { void (*activate)(); bool (*exists)(UEVR_UObjectHandle object); @@ -309,6 +318,11 @@ typedef struct { UEVR_UObjectHandle (*get_first_object_by_class)(UEVR_UClassHandle klass, bool allow_default); UEVR_UObjectHandle (*get_first_object_by_class_name)(const wchar_t* class_name, bool allow_default); + + UEVR_UObjectHookMotionControllerStateHandle (*get_or_add_motion_controller_state)(UEVR_UObjectHandle object); + UEVR_UObjectHookMotionControllerStateHandle (*get_motion_controller_state)(UEVR_UObjectHandle object); + + UEVR_UObjectHookMotionControllerStateFunctions* mc_state; } UEVR_UObjectHookFunctions; typedef struct { diff --git a/include/uevr/API.hpp b/include/uevr/API.hpp index e416c2e4..24fb4345 100644 --- a/include/uevr/API.hpp +++ b/include/uevr/API.hpp @@ -128,6 +128,7 @@ class API { struct IConsoleVariable; struct IConsoleCommand; struct ConsoleObjectElement; + struct UObjectHook; template struct TArray; @@ -677,7 +678,9 @@ class API { // TODO struct UEngine : public UObject { - + static UEngine* get() { + return API::get()->get_engine(); + } }; struct UGameEngine : public UEngine { @@ -689,7 +692,9 @@ class API { }; struct FUObjectArray { - + static FUObjectArray* get() { + return API::get()->get_uobject_array(); + } }; // One of the very few non-opaque structs @@ -730,6 +735,100 @@ class API { return data + count; } + + bool empty() const { + return count == 0 || data == nullptr; + } + }; + +public: + // UEVR specific stuff + struct UObjectHook { + struct MotionControllerState; + + static void activate() { + static const auto fn = initialize()->activate; + fn(); + } + + static bool exists(UObject* obj) { + static const auto fn = initialize()->exists; + return fn(obj->to_handle()); + } + + static std::vector get_objects_by_class(UClass* c, bool allow_default = false) { + if (c == nullptr) { + return {}; + } + + return c->get_objects_matching(allow_default); + } + + static UObject* get_first_object_by_class(UClass* c, bool allow_default = false) { + if (c == nullptr) { + return nullptr; + } + + return c->get_first_object_matching(allow_default); + } + + // Must be a USceneComponent + // Also, do NOT keep the pointer around, it will be invalidated at any time + // Call it every time you need it + static MotionControllerState* get_or_add_motion_controller_state(UObject* obj) { + static const auto fn = initialize()->get_or_add_motion_controller_state; + return (MotionControllerState*)fn(obj->to_handle()); + } + + static MotionControllerState* get_motion_controller_state(UObject* obj) { + static const auto fn = initialize()->get_motion_controller_state; + return (MotionControllerState*)fn(obj->to_handle()); + } + + struct MotionControllerState { + inline UEVR_UObjectHookMotionControllerStateHandle to_handle() { return (UEVR_UObjectHookMotionControllerStateHandle)this; } + inline UEVR_UObjectHookMotionControllerStateHandle to_handle() const { return (UEVR_UObjectHookMotionControllerStateHandle)this; } + + void set_rotation_offset(const UEVR_Quaternionf* offset) { + static const auto fn = initialize()->set_rotation_offset; + fn(to_handle(), offset); + } + + void set_location_offset(const UEVR_Vector3f* offset) { + static const auto fn = initialize()->set_location_offset; + fn(to_handle(), offset); + } + + void set_hand(uint32_t hand) { + static const auto fn = initialize()->set_hand; + fn(to_handle(), hand); + } + + void set_permanent(bool permanent) { + static const auto fn = initialize()->set_permanent; + fn(to_handle(), permanent); + } + + private: + static inline const UEVR_UObjectHookMotionControllerStateFunctions* s_functions{nullptr}; + static inline const UEVR_UObjectHookMotionControllerStateFunctions* initialize() { + if (s_functions == nullptr) { + s_functions = API::get()->sdk()->uobject_hook->mc_state; + } + + return s_functions; + } + }; + + private: + static inline const UEVR_UObjectHookFunctions* s_functions{nullptr}; + static inline const UEVR_UObjectHookFunctions* initialize() { + if (s_functions == nullptr) { + s_functions = API::get()->sdk()->uobject_hook; + } + + return s_functions; + } }; private: diff --git a/src/CommitHash.hpp b/src/CommitHash.hpp index 7e57bc1d..41fa454c 100644 --- a/src/CommitHash.hpp +++ b/src/CommitHash.hpp @@ -1,4 +1,4 @@ #pragma once -#define UEVR_COMMIT_HASH "89f476243d2ebacea6f941a8458dd571a181b568" +#define UEVR_COMMIT_HASH "f48ed675e2c1f8730b2a7ebfb9a1981c9a6ebf3d" #define UEVR_BUILD_DATE "20.02.2024" #define UEVR_BUILD_TIME "00:00" diff --git a/src/mods/PluginLoader.cpp b/src/mods/PluginLoader.cpp index 0a3f9ed7..6f4d1efd 100644 --- a/src/mods/PluginLoader.cpp +++ b/src/mods/PluginLoader.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "UObjectHook.hpp" #include "VR.hpp" @@ -507,16 +508,99 @@ namespace uobjecthook { return get_first_object_by_class((UEVR_UClassHandle)c, allow_default); } + + UEVR_UObjectHookMotionControllerStateHandle get_or_add_motion_controller_state(UEVR_UObjectHandle obj_handle) { + const auto obj = (sdk::USceneComponent*)obj_handle; + if (obj == nullptr || !obj->is_a(sdk::USceneComponent::static_class())) { + return nullptr; + } + + const auto result = UObjectHook::get()->get_or_add_motion_controller_state(obj); + + return (UEVR_UObjectHookMotionControllerStateHandle)result.get(); + } + + UEVR_UObjectHookMotionControllerStateHandle get_motion_controller_state(UEVR_UObjectHandle obj_handle) { + const auto obj = (sdk::USceneComponent*)obj_handle; + if (obj == nullptr || !obj->is_a(sdk::USceneComponent::static_class())) { + return nullptr; + } + + const auto result = UObjectHook::get()->get_motion_controller_state(obj); + + if (!result.has_value()) { + return nullptr; + } + + return (UEVR_UObjectHookMotionControllerStateHandle)result->get(); + } + +namespace mc_state { + void set_rotation_offset(UEVR_UObjectHookMotionControllerStateHandle state, const UEVR_Quaternionf* rotation) { + if (state == nullptr) { + return; + } + + auto& s = *(UObjectHook::MotionControllerState*)state; + s.rotation_offset.x = rotation->x; + s.rotation_offset.y = rotation->y; + s.rotation_offset.z = rotation->z; + s.rotation_offset.w = rotation->w; + } + + void set_location_offset(UEVR_UObjectHookMotionControllerStateHandle state, const UEVR_Vector3f* location) { + if (state == nullptr) { + return; + } + + auto& s = *(UObjectHook::MotionControllerState*)state; + s.location_offset.x = location->x; + s.location_offset.y = location->y; + s.location_offset.z = location->z; + } + + void set_hand(UEVR_UObjectHookMotionControllerStateHandle state, unsigned int hand) { + if (state == nullptr) { + return; + } + + if (hand > 1) { + return; + } + + auto& s = *(UObjectHook::MotionControllerState*)state; + s.hand = (uint8_t)hand; + } + + void set_permanent(UEVR_UObjectHookMotionControllerStateHandle state, bool permanent) { + if (state == nullptr) { + return; + } + + auto& s = *(UObjectHook::MotionControllerState*)state; + s.permanent = permanent; + } +} } } +UEVR_UObjectHookMotionControllerStateFunctions g_mc_functions { + uevr::uobjecthook::mc_state::set_rotation_offset, + uevr::uobjecthook::mc_state::set_location_offset, + uevr::uobjecthook::mc_state::set_hand, + uevr::uobjecthook::mc_state::set_permanent +}; + UEVR_UObjectHookFunctions g_uobjecthook_functions { uevr::uobjecthook::activate, uevr::uobjecthook::exists, uevr::uobjecthook::get_objects_by_class, uevr::uobjecthook::get_objects_by_class_name, uevr::uobjecthook::get_first_object_by_class, - uevr::uobjecthook::get_first_object_by_class_name + uevr::uobjecthook::get_first_object_by_class_name, + uevr::uobjecthook::get_or_add_motion_controller_state, + uevr::uobjecthook::get_motion_controller_state, + &g_mc_functions }; #define FFIELDCLASS(x) ((sdk::FFieldClass*)x) diff --git a/src/mods/UObjectHook.hpp b/src/mods/UObjectHook.hpp index e4936c37..f4c708ad 100644 --- a/src/mods/UObjectHook.hpp +++ b/src/mods/UObjectHook.hpp @@ -75,9 +75,67 @@ class UObjectHook : public Mod { void on_post_calculate_stereo_view_offset(void* stereo_device, const int32_t view_index, Rotator* view_rotation, const float world_to_meters, Vector3f* view_location, bool is_double) override; +public: + struct MotionControllerStateBase { + nlohmann::json to_json() const; + void from_json(const nlohmann::json& data); + + MotionControllerStateBase& operator=(const MotionControllerStateBase& other) = default; + + // State that can be parsed from disk + glm::quat rotation_offset{glm::identity()}; + glm::vec3 location_offset{0.0f, 0.0f, 0.0f}; + uint8_t hand{1}; + bool permanent{false}; + }; + + // Assert if MotionControllerStateBase is not trivially copyable + static_assert(std::is_trivially_copyable_v); + static_assert(std::is_trivially_destructible_v); + static_assert(std::is_standard_layout_v); + + struct MotionControllerState final : MotionControllerStateBase { + ~MotionControllerState(); + + MotionControllerState& operator=(const MotionControllerStateBase& other) { + MotionControllerStateBase::operator=(other); + return *this; + } + + operator MotionControllerStateBase&() { + return *this; + } + + // In-memory state + sdk::AActor* adjustment_visualizer{nullptr}; + bool adjusting{false}; + }; + + std::shared_ptr get_or_add_motion_controller_state(sdk::USceneComponent* component) { + { + std::shared_lock _{m_mutex}; + if (auto it = m_motion_controller_attached_components.find(component); it != m_motion_controller_attached_components.end()) { + return it->second; + } + } + + std::unique_lock _{m_mutex}; + auto result = std::make_shared(); + return m_motion_controller_attached_components[component] = result; + + return result; + } + + std::optional> get_motion_controller_state(sdk::USceneComponent* component) { + std::shared_lock _{m_mutex}; + if (auto it = m_motion_controller_attached_components.find(component); it != m_motion_controller_attached_components.end()) { + return it->second; + } + + return {}; + } + private: - struct MotionControllerStateBase; - struct MotionControllerState; struct StatePath; struct PersistentState; struct PersistentCameraState; @@ -173,41 +231,6 @@ class UObjectHook : public Mod { std::deque m_most_recent_objects{}; std::unordered_set m_motion_controller_attached_objects{}; - struct MotionControllerStateBase { - nlohmann::json to_json() const; - void from_json(const nlohmann::json& data); - - MotionControllerStateBase& operator=(const MotionControllerStateBase& other) = default; - - // State that can be parsed from disk - glm::quat rotation_offset{glm::identity()}; - glm::vec3 location_offset{0.0f, 0.0f, 0.0f}; - uint8_t hand{1}; - bool permanent{false}; - }; - - // Assert if MotionControllerStateBase is not trivially copyable - static_assert(std::is_trivially_copyable_v); - static_assert(std::is_trivially_destructible_v); - static_assert(std::is_standard_layout_v); - - struct MotionControllerState final : MotionControllerStateBase { - ~MotionControllerState(); - - MotionControllerState& operator=(const MotionControllerStateBase& other) { - MotionControllerStateBase::operator=(other); - return *this; - } - - operator MotionControllerStateBase&() { - return *this; - } - - // In-memory state - sdk::AActor* adjustment_visualizer{nullptr}; - bool adjusting{false}; - }; - std::unordered_map> m_motion_controller_attached_components{}; sdk::AActor* m_overlap_detection_actor{nullptr}; sdk::AActor* m_overlap_detection_actor_left{nullptr}; @@ -217,30 +240,6 @@ class UObjectHook : public Mod { glm::vec3 offset{}; } m_camera_attach{}; - std::shared_ptr get_or_add_motion_controller_state(sdk::USceneComponent* component) { - { - std::shared_lock _{m_mutex}; - if (auto it = m_motion_controller_attached_components.find(component); it != m_motion_controller_attached_components.end()) { - return it->second; - } - } - - std::unique_lock _{m_mutex}; - auto result = std::make_shared(); - return m_motion_controller_attached_components[component] = result; - - return result; - } - - std::optional> get_motion_controller_state(sdk::USceneComponent* component) { - std::shared_lock _{m_mutex}; - if (auto it = m_motion_controller_attached_components.find(component); it != m_motion_controller_attached_components.end()) { - return it->second; - } - - return {}; - } - void remove_motion_controller_state(sdk::USceneComponent* component) { std::unique_lock _{m_mutex}; m_motion_controller_attached_components.erase(component);