From f5f02c793078885dbeefb9f68c0ea0e97a3d47a1 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:03:03 -0400 Subject: [PATCH] Add/refactor interop wrappers for Tracks functionality --- include/Animation/Events.h | 3 + shared/Animation/Animation.h | 129 ---- .../Animation/GameObjectTrackController.hpp | 2 + shared/Animation/PointDefinition.h | 136 +--- shared/Animation/Track.h | 652 +----------------- shared/AssociatedData.h | 7 +- shared/interop.h | 31 + .../context_wrappers.h} | 74 +- shared/interop/coroutine_wrapper.h | 68 ++ shared/interop/definition_wrappers.h | 136 ++++ shared/interop/property_helpers.h | 140 ++++ shared/interop/property_wrappers.h | 375 ++++++++++ shared/interop/time_wrapper.h | 55 ++ shared/interop/track_wrapper.h | 218 ++++++ src/Animation/GameObjectTrackController.cpp | 13 +- 15 files changed, 1058 insertions(+), 981 deletions(-) create mode 100644 shared/interop.h rename shared/{binding_wrappers.hpp => interop/context_wrappers.h} (73%) create mode 100644 shared/interop/coroutine_wrapper.h create mode 100644 shared/interop/definition_wrappers.h create mode 100644 shared/interop/property_helpers.h create mode 100644 shared/interop/property_wrappers.h create mode 100644 shared/interop/time_wrapper.h create mode 100644 shared/interop/track_wrapper.h diff --git a/include/Animation/Events.h b/include/Animation/Events.h index 8ded9b1..a911c82 100644 --- a/include/Animation/Events.h +++ b/include/Animation/Events.h @@ -2,6 +2,9 @@ #include "Animation/PointDefinition.h" #include "Animation/Track.h" + +#include "Animation/Easings.h" + namespace GlobalNamespace { class BeatmapCallbacksController; } diff --git a/shared/Animation/Animation.h b/shared/Animation/Animation.h index 94c5c7c..d34fb42 100644 --- a/shared/Animation/Animation.h +++ b/shared/Animation/Animation.h @@ -72,134 +72,5 @@ MirrorQuaternionNullable(std::optional const& quaternion) return NEVector::Quaternion(modifiedVector.x, modifiedVector.y * -1, modifiedVector.z * -1, modifiedVector.w); } -#pragma region property_utils - -// Conversion functions -[[nodiscard]] constexpr static NEVector::Vector3 ToVector3(Tracks::ffi::WrapBaseValue const& val) { - if (val.ty != Tracks::ffi::WrapBaseValueType::Vec3) return {}; - return { val.value.vec3.x, val.value.vec3.y, val.value.vec3.z }; -} - -[[nodiscard]] constexpr static NEVector::Vector4 ToVector4(Tracks::ffi::WrapBaseValue const& val) { - if (val.ty != Tracks::ffi::WrapBaseValueType::Vec4) return {}; - return { val.value.vec4.x, val.value.vec4.y, val.value.vec4.z, val.value.vec4.w }; -} - -[[nodiscard]] constexpr static NEVector::Quaternion ToQuaternion(Tracks::ffi::WrapBaseValue const& val) { - if (val.ty != Tracks::ffi::WrapBaseValueType::Quat) return {}; - return { val.value.quat.x, val.value.quat.y, val.value.quat.z, val.value.quat.w }; -} - -[[nodiscard]] constexpr static float ToFloat(Tracks::ffi::WrapBaseValue const& val) { - if (val.ty != Tracks::ffi::WrapBaseValueType::Float) return {}; - return val.value.float_v; -} - -// Base versions that return WrapBaseValue -[[nodiscard]] -constexpr static std::vector getProperties(std::span tracks, - PropertyNames name, TimeUnit time) { - std::vector properties; - properties.reserve(tracks.size()); - for (auto const& track : tracks) { - auto property = track.GetPropertyNamed(name); - auto value = property.GetValue(); - if (TimeUnit(value.last_updated) <= time) continue; - if (!value.value.has_value) continue; - properties.push_back(value.value.value); - } - - return properties; -} - -[[nodiscard]] -static std::vector getPathProperties(std::span tracks, PropertyNames name, - uint64_t time) { - std::vector properties; - - properties.reserve(tracks.size()); - for (auto const& track : tracks) { - auto property = track.GetPathPropertyNamed(name); - auto value = property.Interpolate(time); - if (!value.has_value()) continue; - properties.push_back(*value); - } - - return properties; -} - -// Macro to generate type-specific property getters -#define GENERATE_PROPERTY_GETTERS(ReturnType, Suffix, Conversion) \ - [[nodiscard]] \ - static std::vector getProperties##Suffix(std::span tracks, PropertyNames name, \ - TimeUnit time) { \ - std::vector properties; \ - properties.reserve(tracks.size()); \ - for (auto const& track : tracks) { \ - auto property = track.GetPropertyNamed(name); \ - auto value = property.GetValue(); \ - if (!value.value.has_value) continue; \ - if (TimeUnit(value.last_updated) <= time) continue; \ - properties.push_back(Conversion(value.value.value)); \ - } \ - return properties; \ - } \ - \ - [[nodiscard]] \ - static std::vector getPathProperties##Suffix(std::span tracks, PropertyNames name, \ - float time) { \ - std::vector properties; \ - properties.reserve(tracks.size()); \ - for (auto const& track : tracks) { \ - auto property = track.GetPathPropertyNamed(name); \ - auto value = property.Interpolate(time); \ - if (!value.has_value()) continue; \ - properties.push_back(Conversion(*value)); \ - } \ - return properties; \ - } - -// Generate specialized versions for different types -GENERATE_PROPERTY_GETTERS(NEVector::Vector3, Vec3, ToVector3) -GENERATE_PROPERTY_GETTERS(NEVector::Vector4, Vec4, ToVector4) -GENERATE_PROPERTY_GETTERS(NEVector::Quaternion, Quat, ToQuaternion) -GENERATE_PROPERTY_GETTERS(float, Float, ToFloat) - -// Macro to generate addition functions for spans -#define GENERATE_ADD_FUNCTIONS(Type, TypeName) \ - [[nodiscard]] \ - constexpr static std::optional add##TypeName##s(std::span values) { \ - if (values.empty()) return std::nullopt; \ - Type result = values[0]; \ - for (size_t i = 1; i < values.size(); ++i) { \ - result = result + values[i]; \ - } \ - return result; \ - } - -// Macro to generate multiplication functions for spans -#define GENERATE_MUL_FUNCTIONS(Type, TypeName) \ - [[nodiscard]] \ - constexpr static std::optional multiply##TypeName##s(std::span values) { \ - if (values.empty()) return std::nullopt; \ - Type result = values[0]; \ - for (size_t i = 1; i < values.size(); ++i) { \ - result = result * values[i]; \ - } \ - return result; \ - } - -// Generate addition functions for different types -GENERATE_ADD_FUNCTIONS(NEVector::Vector3, Vector3) -GENERATE_ADD_FUNCTIONS(NEVector::Vector4, Vector4) -GENERATE_ADD_FUNCTIONS(float, Float) - -// Generate multiplication functions for different types -GENERATE_MUL_FUNCTIONS(NEVector::Vector3, Vector3) -GENERATE_MUL_FUNCTIONS(NEVector::Vector4, Vector4) -GENERATE_MUL_FUNCTIONS(NEVector::Quaternion, Quaternion) -GENERATE_MUL_FUNCTIONS(float, Float) - -#pragma endregion // property_utils } // namespace Animation \ No newline at end of file diff --git a/shared/Animation/GameObjectTrackController.hpp b/shared/Animation/GameObjectTrackController.hpp index a1e51a4..c7ba026 100644 --- a/shared/Animation/GameObjectTrackController.hpp +++ b/shared/Animation/GameObjectTrackController.hpp @@ -5,6 +5,8 @@ #include "UnityEngine/Transform.hpp" #include "Track.h" +#include "./interop/track_wrapper.h" +#include "./interop.h" #include #include diff --git a/shared/Animation/PointDefinition.h b/shared/Animation/PointDefinition.h index f8b470e..17b3df2 100644 --- a/shared/Animation/PointDefinition.h +++ b/shared/Animation/PointDefinition.h @@ -1,138 +1,12 @@ #pragma once -#include -#include -#include -#include -#include "../Vector.h" -#include "beatsaber-hook/shared/config/rapidjson-utils.hpp" -#include "Easings.h" -#include "../Hash.h" -#include "UnityEngine/Color.hpp" -#include "beatsaber-hook/shared/rapidjson/include/rapidjson/document.h" -#include "../bindings.h" -#include "../binding_wrappers.hpp" +// Import point definition wrappers from interop module +#include "../interop/definition_wrappers.h" -extern Tracks::ffi::FFIJsonValue const* convert_rapidjson(rapidjson::Value const& value); - -class PointDefinitionW { -public: - explicit PointDefinitionW(rapidjson::Value const& value, Tracks::ffi::WrapBaseValueType type, - std::shared_ptr base_provider_context) { - auto* json = convert_rapidjson(value); - this->base_provider_context = base_provider_context; - - internalPointDefinition = std::shared_ptr( - Tracks::ffi::tracks_make_base_point_definition(json, type, *base_provider_context), - [](Tracks::ffi::BasePointDefinition* ptr) { - if (!ptr) return; - Tracks::ffi::base_point_definition_free(ptr); - }); - } - - // takes ownership - PointDefinitionW(Tracks::ffi::BasePointDefinition* pointDefinition, - std::shared_ptr context) - : base_provider_context(context) { - internalPointDefinition = std::shared_ptr( - pointDefinition, - [](Tracks::ffi::BasePointDefinition* ptr) { - if (!ptr) return; - Tracks::ffi::base_point_definition_free(ptr); - }); - } - - ~PointDefinitionW() = default; - - PointDefinitionW(PointDefinitionW const& other) = default; - explicit PointDefinitionW(std::nullptr_t) : internalPointDefinition(nullptr) {}; - - [[nodiscard]] - Tracks::ffi::WrapBaseValueType GetType() const { - return Tracks::ffi::tracks_base_point_definition_get_type(internalPointDefinition.get()); - } - - [[nodiscard]] - - Tracks::ffi::WrapBaseValue Interpolate(float time) const { - bool last; - return Interpolate(time, last); - } - - Tracks::ffi::WrapBaseValue Interpolate(float time, bool& last) const { - auto result = Tracks::ffi::tracks_interpolate_base_point_definition(internalPointDefinition.get(), time, &last, - *base_provider_context); - - return result; - } - NEVector::Vector3 InterpolateVec3(float time, bool& last) const { - auto result = Interpolate(time, last); - return { result.value.vec3.x, result.value.vec3.y, result.value.vec3.z }; - } - - NEVector::Quaternion InterpolateQuaternion(float time, bool& last) const { - auto result = Interpolate(time, last); - return { result.value.quat.x, result.value.quat.y, result.value.quat.z, result.value.quat.w }; - } - - float InterpolateLinear(float time, bool& last) const { - auto result = Interpolate(time, last); - return result.value.float_v; - } - - NEVector::Vector4 InterpolateVector4(float time, bool& last) const { - auto result = Interpolate(time, last); - return { result.value.vec4.x, result.value.vec4.y, result.value.vec4.z, result.value.vec4.w }; - } - - NEVector::Vector3 InterpolateVec3(float time) const { - bool last; - return InterpolateVec3(time, last); - } - - NEVector::Quaternion InterpolateQuaternion(float time) const { - bool last; - return InterpolateQuaternion(time, last); - } - - float InterpolateLinear(float time) const { - bool last; - return InterpolateLinear(time, last); - } - - NEVector::Vector4 InterpolateVector4(float time) const { - bool last; - return InterpolateVector4(time, last); - } - - uintptr_t count() const { - return Tracks::ffi::tracks_base_point_definition_count(internalPointDefinition.get()); - } - - bool hasBaseProvider() const { - return Tracks::ffi::tracks_base_point_definition_has_base_provider(internalPointDefinition.get()); - } - - operator Tracks::ffi::BasePointDefinition const*() const { - return internalPointDefinition.get(); - } - - operator Tracks::ffi::BasePointDefinition*() { - return internalPointDefinition.get(); - } - - - // operator Tracks::ffi::BasePointDefinition*() { - // return internalPointDefinition; - // } - -private: - constexpr PointDefinitionW() = default; - - std::shared_ptr internalPointDefinition; - std::shared_ptr base_provider_context; -}; +// PointDefinitionW and PointDefinitionManager are now defined in interop/definition_wrappers.h +// This header simply re-exports them for backward compatibility. +/// Manager for point definitions - stores and retrieves named point definitions class PointDefinitionManager { public: std::unordered_map pointData; diff --git a/shared/Animation/Track.h b/shared/Animation/Track.h index da65e6b..4783727 100644 --- a/shared/Animation/Track.h +++ b/shared/Animation/Track.h @@ -1,647 +1,5 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../Vector.h" -#include "../sv/small_vector.h" -#include "../Constants.h" -#include "PointDefinition.h" -#include "UnityEngine/GameObject.hpp" - -#include "beatsaber-hook/shared/utils/typedefs-wrappers.hpp" - -#include - -#include "../bindings.h" -#include "../binding_wrappers.hpp" - -namespace Events { -struct AnimateTrackContext; -} - -struct PropertyW; -struct PathPropertyW; - -using PropertyNames = Tracks::ffi::PropertyNames; - -struct TimeUnit { - Tracks::ffi::CTimeUnit time; - - constexpr TimeUnit(Tracks::ffi::CTimeUnit time) : time(time) {} - constexpr TimeUnit() = default; - constexpr TimeUnit(TimeUnit const&) = default; - - [[nodiscard]] constexpr operator Tracks::ffi::CTimeUnit() const { - return time; - } - - // get seconds - constexpr uint64_t get_seconds() const { - return time._0; - } - - // get nanoseconds - constexpr uint64_t get_nanoseconds() const { - return time._1; - } - - constexpr bool operator<(TimeUnit o) const { - return get_seconds() < o.get_seconds() || - (o.get_seconds() == get_seconds() && get_nanoseconds() < o.get_nanoseconds()); - } - - constexpr bool operator>(TimeUnit o) const { - return get_seconds() > o.get_seconds() || - (o.get_seconds() == get_seconds() && get_nanoseconds() > o.get_nanoseconds()); - } - - constexpr bool operator==(TimeUnit o) const { - return get_seconds() == o.get_seconds() && get_nanoseconds() == o.get_nanoseconds(); - } - - constexpr bool operator!=(TimeUnit o) const { - return get_seconds() != o.get_seconds() || get_nanoseconds() != o.get_nanoseconds(); - } - - constexpr bool operator<=(TimeUnit o) const { - return o == *this || *this < o; - } - - constexpr bool operator>=(TimeUnit o) const { - return o == *this || *this > o; - } -}; - -/// Owned by TrackW -struct PropertyW { - Tracks::ffi::ValueProperty* property; - - constexpr PropertyW() = default; - constexpr PropertyW(Tracks::ffi::ValueProperty* property) : property(property) {} - - operator Tracks::ffi::ValueProperty const*() const { - return property; - } - operator Tracks::ffi::ValueProperty*() const { - return property; - } - operator bool() const { - return property != nullptr; - } - - [[nodiscard]] Tracks::ffi::WrapBaseValueType GetType() const { - CRASH_UNLESS(property); - - return Tracks::ffi::property_get_type(property); - } - [[nodiscard]] Tracks::ffi::CValueProperty GetValue() const { - CRASH_UNLESS(property); - return Tracks::ffi::property_get_value(property); - } - - [[nodiscard]] TimeUnit GetTime() const { - CRASH_UNLESS(property); - - return Tracks::ffi::property_get_last_updated(property); - } - - constexpr bool hasUpdated(Tracks::ffi::CValueProperty value, TimeUnit lastCheckedTime = {}) const { - return lastCheckedTime == TimeUnit() || TimeUnit(value.last_updated) >= lastCheckedTime; - } - - [[nodiscard]] std::optional GetQuat(TimeUnit lastCheckedTime = {}) const { - auto value = GetValue(); - if (!value.value.has_value) { - return std::nullopt; - } - if (!hasUpdated(value, lastCheckedTime)) { - return std::nullopt; - } - if (value.value.value.ty != Tracks::ffi::WrapBaseValueType::Quat) { - return std::nullopt; - } - auto v = value.value.value.value; - return NEVector::Quaternion{ v.quat.x, v.quat.y, v.quat.z, v.quat.w }; - } - [[nodiscard]] std::optional GetVec3(TimeUnit lastCheckedTime = {}) const { - auto value = GetValue(); - if (!value.value.has_value) return std::nullopt; - if (!hasUpdated(value, lastCheckedTime)) return std::nullopt; - if (value.value.value.ty != Tracks::ffi::WrapBaseValueType::Vec3) return std::nullopt; - - auto v = value.value.value.value; - return NEVector::Vector3{ v.vec3.x, v.vec3.y, v.vec3.z }; - } - [[nodiscard]] std::optional GetVec4(TimeUnit lastCheckedTime = {}) const { - auto value = GetValue(); - if (!value.value.has_value) return std::nullopt; - if (!hasUpdated(value, lastCheckedTime)) return std::nullopt; - if (value.value.value.ty != Tracks::ffi::WrapBaseValueType::Vec4) return std::nullopt; - auto v = value.value.value.value; - - return NEVector::Vector4{ v.vec4.x, v.vec4.y, v.vec4.z, v.vec4.w }; - } - [[nodiscard]] std::optional GetFloat(TimeUnit lastCheckedTime = {}) const { - auto value = GetValue(); - if (!value.value.has_value) return std::nullopt; - if (!hasUpdated(value, lastCheckedTime)) return std::nullopt; - if (value.value.value.ty != Tracks::ffi::WrapBaseValueType::Float) return std::nullopt; - - return value.value.value.value.float_v; - } -}; - -/// Owned by TrackW -struct PathPropertyW { - Tracks::ffi::PathProperty* property; - std::shared_ptr internal_tracks_context; - - PathPropertyW(Tracks::ffi::PathProperty* property, - std::shared_ptr internal_tracks_context) - : property(property), internal_tracks_context(internal_tracks_context) {} - operator Tracks::ffi::PathProperty*() const { - return property; - } - operator Tracks::ffi::PathProperty const*() const { - return property; - } - operator bool() const { - return property != nullptr; - } - - [[nodiscard]] - float GetTime() const { - return Tracks::ffi::path_property_get_time(property); - } - - [[nodiscard]] - std::optional Interpolate(float time) const { - auto result = Tracks::ffi::path_property_interpolate(property, time, *this->internal_tracks_context); - if (!result.has_value) { - return std::nullopt; - } - - return result.value; - } - - [[nodiscard]] - std::optional InterpolateVec3(float time) const { - auto result = Interpolate(time); - if (!result) return std::nullopt; - auto unwrapped = *result; - if (unwrapped.ty != Tracks::ffi::WrapBaseValueType::Vec3) { - return std::nullopt; - } - - return NEVector::Vector3{ unwrapped.value.vec3.x, unwrapped.value.vec3.y, unwrapped.value.vec3.z }; - } - - [[nodiscard]] - std::optional InterpolateVec4(float time) const { - auto result = Interpolate(time); - if (!result) return std::nullopt; - auto unwrapped = *result; - if (unwrapped.ty != Tracks::ffi::WrapBaseValueType::Vec4) { - return std::nullopt; - } - - return NEVector::Vector4{ unwrapped.value.vec4.x, unwrapped.value.vec4.y, unwrapped.value.vec4.z, - unwrapped.value.vec4.w }; - } - - [[nodiscard]] - std::optional InterpolateQuat(float time) const { - auto result = Interpolate(time); - if (!result) return std::nullopt; - auto unwrapped = *result; - if (unwrapped.ty != Tracks::ffi::WrapBaseValueType::Quat) { - return std::nullopt; - } - - return NEVector::Quaternion{ unwrapped.value.quat.x, unwrapped.value.quat.y, unwrapped.value.quat.z, - unwrapped.value.quat.w }; - } - - [[nodiscard]] - std::optional InterpolateLinear(float time) const { - auto result = Interpolate(time); - if (!result) return std::nullopt; - if (result->ty != Tracks::ffi::WrapBaseValueType::Float) { - return std::nullopt; - } - - return result->value.float_v; - } - - [[nodiscard]] Tracks::ffi::WrapBaseValueType GetType() const { - return Tracks::ffi::path_property_get_type(property); - } - - // [[nodiscard]] float GetTime() const { - // return Tracks::ffi::path_property_get_time(property); - // } - - // void SetTime(float time) const { - // Tracks::ffi::path_property_set_time(property, time); - // } - - // void Finish() const { - // Tracks::ffi::path_property_finish(property); - // } - - // void Init(std::optional newPointData) const { - // Tracks::ffi::path_property_init(property, newPointData.value_or(PointDefinitionW(nullptr))); - // } -}; -struct PropertiesMapW { - PropertiesMapW(Tracks::ffi::CPropertiesMap map) - : position(map.position), offsetPosition(map.offset_position), rotation(map.rotation), scale(map.scale), - localRotation(map.local_rotation), localPosition(map.local_position), dissolve(map.dissolve), - dissolveArrow(map.dissolve_arrow), time(map.time), cuttable(map.cuttable), color(map.color), - attentuation(map.attentuation), fogOffset(map.fog_offset), heightFogStartY(map.height_fog_start_y), - heightFogHeight(map.height_fog_height) {} - - PropertyW position; - PropertyW offsetPosition; - PropertyW rotation; - PropertyW offsetRotation; - PropertyW scale; - PropertyW localRotation; - PropertyW localPosition; - PropertyW dissolve; - PropertyW dissolveArrow; - PropertyW time; - PropertyW cuttable; - PropertyW color; - PropertyW attentuation; - PropertyW fogOffset; - PropertyW heightFogStartY; - PropertyW heightFogHeight; -}; - -struct PathPropertiesMapW { - PathPropertiesMapW(Tracks::ffi::CPathPropertiesMap map, - std::shared_ptr internal_tracks_context) - : position(map.position, internal_tracks_context), offsetPosition(map.offset_position, internal_tracks_context), - rotation(map.rotation, internal_tracks_context), offsetRotation(map.offset_rotation, internal_tracks_context), - scale(map.scale, internal_tracks_context), localRotation(map.local_rotation, internal_tracks_context), - localPosition(map.local_position, internal_tracks_context), definitePosition(map.definite_position, internal_tracks_context), - dissolve(map.dissolve, internal_tracks_context), dissolveArrow(map.dissolve_arrow, internal_tracks_context), - cuttable(map.cuttable, internal_tracks_context), color(map.color, internal_tracks_context) {} - - PathPropertyW position; - PathPropertyW offsetPosition; - PathPropertyW rotation; - PathPropertyW offsetRotation; - PathPropertyW scale; - PathPropertyW localRotation; - PathPropertyW localPosition; - PathPropertyW definitePosition; - PathPropertyW dissolve; - PathPropertyW dissolveArrow; - PathPropertyW cuttable; - PathPropertyW color; -}; - -struct PropertiesValuesW { - constexpr PropertiesValuesW(Tracks::ffi::CPropertiesValues values) { - if (values.position.has_value) { - position = NEVector::Vector3{ values.position.value.x, values.position.value.y, values.position.value.z }; - } - if (values.offset_position.has_value) { - offsetPosition = NEVector::Vector3{ values.offset_position.value.x, values.offset_position.value.y, values.offset_position.value.z }; - } - if (values.rotation.has_value) { - rotation = NEVector::Quaternion{ values.rotation.value.x, values.rotation.value.y, values.rotation.value.z, - values.rotation.value.w }; - } - if (values.offset_rotation.has_value) { - offsetRotation = NEVector::Quaternion{ values.offset_rotation.value.x, values.offset_rotation.value.y, values.offset_rotation.value.z, - values.offset_rotation.value.w }; - } - if (values.scale.has_value) { - scale = NEVector::Vector3{ values.scale.value.x, values.scale.value.y, values.scale.value.z }; - } - if (values.local_rotation.has_value) { - localRotation = NEVector::Quaternion{ values.local_rotation.value.x, values.local_rotation.value.y, - values.local_rotation.value.z, values.local_rotation.value.w }; - } - if (values.local_position.has_value) { - localPosition = - NEVector::Vector3{ values.local_position.value.x, values.local_position.value.y, - values.local_position.value.z }; - } - if (values.dissolve.has_value) { - dissolve = values.dissolve.value; - } - if (values.dissolve_arrow.has_value) { - dissolveArrow = values.dissolve_arrow.value; - } - if (values.time.has_value) { - time = values.time.value; - } - if (values.cuttable.has_value) { - cuttable = values.cuttable.value; - } - if (values.color.has_value) { - color = NEVector::Vector4{ values.color.value.x, values.color.value.y, values.color.value.z, - values.color.value.w }; - } - if (values.attentuation.has_value) { - attentuation = values.attentuation.value; - } - if (values.fog_offset.has_value) { - fogOffset = values.fog_offset.value; - } - if (values.height_fog_start_y.has_value) { - heightFogStartY = values.height_fog_start_y.value; - } - if (values.height_fog_height.has_value) { - heightFogHeight = values.height_fog_height.value; - } - } - - std::optional position; - std::optional offsetPosition; - std::optional rotation; - std::optional offsetRotation; - std::optional scale; - std::optional localRotation; - std::optional localPosition; - std::optional dissolve; - std::optional dissolveArrow; - std::optional time; - std::optional cuttable; - std::optional color; - std::optional attentuation; - std::optional fogOffset; - std::optional heightFogStartY; - std::optional heightFogHeight; -}; - -struct PathPropertiesValuesW { - constexpr PathPropertiesValuesW(Tracks::ffi::CPathPropertiesValues values) { - if (values.position.has_value) { - position = NEVector::Vector3{ values.position.value.x, values.position.value.y, values.position.value.z }; - } - if (values.offset_position.has_value) { - offsetPosition = NEVector::Vector3{ values.offset_position.value.x, values.offset_position.value.y, values.offset_position.value.z }; - } - if (values.rotation.has_value) { - rotation = NEVector::Quaternion{ values.rotation.value.x, values.rotation.value.y, values.rotation.value.z, - values.rotation.value.w }; - } - if (values.offset_rotation.has_value) { - offsetRotation = NEVector::Quaternion{ values.offset_rotation.value.x, values.offset_rotation.value.y, values.offset_rotation.value.z, - values.offset_rotation.value.w }; - } - if (values.scale.has_value) { - scale = NEVector::Vector3{ values.scale.value.x, values.scale.value.y, values.scale.value.z }; - } - if (values.local_rotation.has_value) { - localRotation = NEVector::Quaternion{ values.local_rotation.value.x, values.local_rotation.value.y, - values.local_rotation.value.z, values.local_rotation.value.w }; - } - if (values.local_position.has_value) { - localPosition = - NEVector::Vector3{ values.local_position.value.x, values.local_position.value.y, - values.local_position.value.z }; - } - if (values.definite_position.has_value) { - definitePosition = values.definite_position.value; - } - if (values.dissolve.has_value) { - dissolve = values.dissolve.value; - } - if (values.dissolve_arrow.has_value) { - dissolveArrow = values.dissolve_arrow.value; - } - if (values.cuttable.has_value) { - cuttable = values.cuttable.value; - } - if (values.color.has_value) { - color = NEVector::Vector4{ values.color.value.x, values.color.value.y, values.color.value.z, - values.color.value.w }; - } - } - - std::optional position; - std::optional offsetPosition; - std::optional rotation; - std::optional offsetRotation; - std::optional scale; - std::optional localRotation; - std::optional localPosition; - std::optional definitePosition; - std::optional dissolve; - std::optional dissolveArrow; - std::optional cuttable; - std::optional color; -}; - -struct TrackW { - Tracks::ffi::TrackKeyFFI track = Tracks::ffi::TrackKeyFFI{ static_cast(-1) }; - std::shared_ptr internal_tracks_context; - std::shared_ptr base_provider_context; - bool v2; - - // using CWrappedCallback = void (* *)(struct Tracks::ffi::GameObject, bool, void*); - using CWrappedCallback = decltype(Tracks::ffi::track_register_game_object_callback(nullptr, nullptr, nullptr)); - - constexpr TrackW() = default; - TrackW(Tracks::ffi::TrackKeyFFI track, bool v2, std::shared_ptr internal_tracks_context, - std::shared_ptr base_provider_context) - : track(track), v2(v2), internal_tracks_context(internal_tracks_context), - base_provider_context(base_provider_context) {} - - operator Tracks::ffi::TrackKeyFFI() const { - return track; - } - - operator bool() const { - return track._0 != -1; - } - - bool operator==(TrackW const& rhs) const { - return this->track._0 == rhs.track._0; - } - - bool operator<(TrackW const& rhs) const { - return this->track._0 < rhs.track._0; - } - - [[nodiscard]] Tracks::ffi::Track* getTrackPtr() const { - return Tracks::ffi::tracks_holder_get_track_mut(*internal_tracks_context, track); - } - - Tracks::ffi::PropertyNames AliasPropertyName(Tracks::ffi::PropertyNames original) const { - // if v3, return original - if (!v2) return original; - // alias offsetPosition into position - if (original == Tracks::ffi::PropertyNames::OffsetPosition) return Tracks::ffi::PropertyNames::Position; - - return original; - } - - std::string_view AliasPropertyName(std::string_view original) const { - // if v3, return original - if (!v2) return original; - // alias offsetPosition into position - if (original == TracksAD::Constants::OFFSET_POSITION) return TracksAD::Constants::POSITION; - - return original; - } - - [[nodiscard]] PropertyW GetProperty(std::string_view name) const { - auto ptr = getTrackPtr(); - auto prop = Tracks::ffi::track_get_property(ptr, AliasPropertyName(name).data()); - return PropertyW(prop); - } - [[nodiscard]] PropertyW GetPropertyNamed(Tracks::ffi::PropertyNames name) const { - auto ptr = getTrackPtr(); - auto prop = Tracks::ffi::track_get_property_by_name(ptr, AliasPropertyName(name)); - return PropertyW(prop); - } - - [[nodiscard]] PathPropertyW GetPathProperty(std::string_view name) const { - auto ptr = getTrackPtr(); - auto prop = Tracks::ffi::track_get_path_property(ptr, AliasPropertyName(name).data()); - return PathPropertyW(prop, base_provider_context); - } - [[nodiscard]] PathPropertyW GetPathPropertyNamed(Tracks::ffi::PropertyNames name) const { - auto track = getTrackPtr(); - auto prop = Tracks::ffi::track_get_path_property_by_name(getTrackPtr(), AliasPropertyName(name)); - return PathPropertyW(prop, base_provider_context); - } - - [[nodiscard]] - PropertiesMapW GetPropertiesMapW() const { - auto track = getTrackPtr(); - auto map = Tracks::ffi::track_get_properties_map(track); - return PropertiesMapW(map); - } - - [[nodiscard]] - PathPropertiesMapW GetPathPropertiesMapW() const { - auto track = getTrackPtr(); - auto map = Tracks::ffi::track_get_path_properties_map(track); - return PathPropertiesMapW(map, base_provider_context); - } - - [[nodiscard]] - PropertiesValuesW GetPropertiesValuesW() const { - auto track = getTrackPtr(); - auto values = Tracks::ffi::track_get_properties_values(track); - return PropertiesValuesW(values); - } - - [[nodiscard]] - PathPropertiesValuesW GetPathPropertiesValuesW(float time) const { - auto track = getTrackPtr(); - auto values = Tracks::ffi::track_get_path_properties_values(track, time, *base_provider_context); - return PathPropertiesValuesW(values); - } - - void RegisterGameObject(UnityEngine::GameObject* gameObject) const { - auto ptr = getTrackPtr(); - Tracks::ffi::track_register_game_object(ptr, Tracks::ffi::GameObject{ .ptr = gameObject }); - } - - void UnregisterGameObject(UnityEngine::GameObject* gameObject) const { - auto track = getTrackPtr(); - Tracks::ffi::track_unregister_game_object(track, Tracks::ffi::GameObject{ .ptr = gameObject }); - } - - // very nasty - CWrappedCallback RegisterGameObjectCallback(std::function callback) const { - if (!callback) { - return nullptr; - } - - // leaks memory, oh well - auto* callbackPtr = new std::function(std::move(callback)); - - // wrap the callback to a function pointer - // this is a C-style function pointer that can be used in the FFI - auto wrapper = +[](Tracks::ffi::GameObject gameObjectWrapper, bool isNew, void* userData) { - auto* cb = reinterpret_cast*>(userData); - auto gameObject = reinterpret_cast(const_cast(gameObjectWrapper.ptr)); - - (*cb)(gameObject, isNew); - }; - - auto track = getTrackPtr(); - return Tracks::ffi::track_register_game_object_callback(track, wrapper, callbackPtr); - } - - void RemoveGameObjectCallback(CWrappedCallback callback) const { - if (!callback) { - return; - } - - auto track = getTrackPtr(); - Tracks::ffi::track_remove_game_object_callback(track, callback); - } - - void RegisterProperty(std::string_view id, PropertyW property) { - auto track = getTrackPtr(); - Tracks::ffi::track_register_property(track, id.data(), const_cast(property.property)); - } - void RegisterPathProperty(std::string_view id, PathPropertyW property) const { - auto track = getTrackPtr(); - Tracks::ffi::track_register_path_property(track, id.data(), property); - } - - [[nodiscard]] Tracks::ffi::CPropertiesMap GetPropertiesMap() const { - auto track = getTrackPtr(); - return Tracks::ffi::track_get_properties_map(track); - } - - [[nodiscard]] Tracks::ffi::CPathPropertiesMap GetPathPropertiesMap() const { - auto track = getTrackPtr(); - return Tracks::ffi::track_get_path_properties_map(track); - } - - /** - * @brief Get the Name object - * - * @return std::string_view Return a string view as the original string is leaked from the FFI. - */ - [[nodiscard]] std::string_view GetName() const { - auto track = getTrackPtr(); - return Tracks::ffi::track_get_name(track); - } - - /** - * @brief Set the Name object - * - * @param name The name to set - */ - void SetName(std::string_view name) const { - auto track = getTrackPtr(); - Tracks::ffi::track_set_name(track, name.data()); - } - - [[nodiscard]] std::span GetGameObjects() const { - static_assert(sizeof(UnityEngine::GameObject*) == sizeof(Tracks::ffi::GameObject), - "Tracks wrapper and GameObject pointer do not match size!"); - std::size_t count = 0; - auto track = getTrackPtr(); - auto const* ptr = Tracks::ffi::track_get_game_objects(track, &count); - auto const* castedPtr = reinterpret_cast(ptr); - - return std::span(castedPtr, count); - } -}; - -namespace std { -template <> struct hash { - size_t operator()(TrackW const& obj) const { - size_t track_hash = std::hash()(obj.track._0); - - return track_hash; - } -}; -} // namespace std +#pragma once + +#include "../interop.h" + +// TODO: Remove \ No newline at end of file diff --git a/shared/AssociatedData.h b/shared/AssociatedData.h index 65e86ee..c5430ef 100644 --- a/shared/AssociatedData.h +++ b/shared/AssociatedData.h @@ -10,13 +10,16 @@ #include "Animation/Animation.h" #include "Hash.h" #include "Vector.h" -#include "bindings.h" -#include "binding_wrappers.hpp" +#include "./bindings.h" #include "custom-json-data/shared/CustomBeatmapData.h" #include "custom-json-data/shared/CustomEventData.h" #include "sv/small_vector.h" +#include "./interop/coroutine_wrapper.h" +#include "./interop/track_wrapper.h" +#include "./interop.h" + namespace UnityEngine { class Renderer; } diff --git a/shared/interop.h b/shared/interop.h new file mode 100644 index 0000000..da0b993 --- /dev/null +++ b/shared/interop.h @@ -0,0 +1,31 @@ +#pragma once + + +#include "bindings.h" + +namespace TracksRS { + struct PropertyW; + struct PathPropertyW; + struct TimeUnit; + struct BaseProviderContextW; + struct TracksHolderW; + struct TrackW; + struct PointDefinitionW; + struct CoroutineManagerW; + struct EventDataW; +} + +// Forward declarations - full definitions are in interop/property_wrappers.h +using PropertyW = TracksRS::PropertyW; +using PathPropertyW = TracksRS::PathPropertyW; +using EventDataW = TracksRS::EventDataW; + +using TimeUnit = TracksRS::TimeUnit; + +using BaseProviderContextW = TracksRS::BaseProviderContextW; +using TracksHolderW = TracksRS::TracksHolderW; + +using TrackW = TracksRS::TrackW; +using PointDefinitionW = TracksRS::PointDefinitionW; +using CoroutineManagerW = TracksRS::CoroutineManagerW; +using PropertyNames = Tracks::ffi::PropertyNames; \ No newline at end of file diff --git a/shared/binding_wrappers.hpp b/shared/interop/context_wrappers.h similarity index 73% rename from shared/binding_wrappers.hpp rename to shared/interop/context_wrappers.h index 2b1009c..fb50b0a 100644 --- a/shared/binding_wrappers.hpp +++ b/shared/interop/context_wrappers.h @@ -1,13 +1,15 @@ #pragma once -#include "Vector.h" -#include "bindings.h" +#include "../bindings.h" +#include "../Vector.h" #include #include #include -namespace TracksAD { +namespace TracksRS { + +/// Wrapper for Tracks::ffi::TracksHolder with RAII semantics class TracksHolderW { public: Tracks::ffi::TracksHolder* internal_tracks_context = nullptr; @@ -64,6 +66,7 @@ class TracksHolderW { } }; +/// Wrapper for Tracks::ffi::BaseProviderContext with type-safe accessors struct BaseProviderContextW { Tracks::ffi::BaseProviderContext* internal_base_provider_context; @@ -103,7 +106,6 @@ struct BaseProviderContextW { Tracks::ffi::base_provider_context_update(internal_base_provider_context, deltaTime); } - void SetBaseValue(std::string_view key, Tracks::ffi::WrapBaseValue const& value) { Tracks::ffi::base_provider_context_set_value(internal_base_provider_context, key.data(), value); } @@ -170,68 +172,6 @@ struct BaseProviderContextW { wrapValue.value.vec4 = Tracks::ffi::WrapVec4{value.x, value.y, value.z, value.w}; SetBaseValue(key, wrapValue); } - }; -struct EventDataW { - Tracks::ffi::EventData* internal_event_data; - - EventDataW(Tracks::ffi::EventData* event_data) : internal_event_data(event_data) {}; - EventDataW(EventDataW const&) = delete; - EventDataW(EventDataW&& o) noexcept : internal_event_data(o.internal_event_data) { - o.internal_event_data = nullptr; - } - - operator Tracks::ffi::EventData*() { - return internal_event_data; - } - - operator Tracks::ffi::EventData const*() const { - return internal_event_data; - } - - ~EventDataW() { - if (internal_event_data) { - Tracks::ffi::event_data_dispose(internal_event_data); - } - } -}; - -struct CoroutineManagerW { - Tracks::ffi::CoroutineManager* internal_coroutine; - - CoroutineManagerW() { - internal_coroutine = Tracks::ffi::create_coroutine_manager(); - } - CoroutineManagerW(Tracks::ffi::CoroutineManager* coroutine) : internal_coroutine(coroutine) {}; - CoroutineManagerW(CoroutineManagerW const&) = delete; - CoroutineManagerW(CoroutineManagerW&& o) noexcept : internal_coroutine(o.internal_coroutine) { - o.internal_coroutine = nullptr; - } - - operator Tracks::ffi::CoroutineManager*() { - return internal_coroutine; - } - - operator Tracks::ffi::CoroutineManager const*() const { - return internal_coroutine; - } - - ~CoroutineManagerW() { - if (internal_coroutine) { - Tracks::ffi::destroy_coroutine_manager(internal_coroutine); - } - } - - void StartCoroutine(float bpm, float songTime, BaseProviderContextW const& context, TracksHolderW& tracksHolder, - EventDataW const& eventData) { - Tracks::ffi::start_event_coroutine(internal_coroutine, bpm, songTime, context.internal_base_provider_context, - tracksHolder, eventData.internal_event_data); - } - - void PollCoroutines(float songTime, BaseProviderContextW const& context, TracksHolderW& tracksHolder) { - Tracks::ffi::poll_events(internal_coroutine, songTime, context.internal_base_provider_context, - tracksHolder); - } -}; -} // namespace TracksAD \ No newline at end of file +} // namespace TracksAD diff --git a/shared/interop/coroutine_wrapper.h b/shared/interop/coroutine_wrapper.h new file mode 100644 index 0000000..3d828bd --- /dev/null +++ b/shared/interop/coroutine_wrapper.h @@ -0,0 +1,68 @@ +#pragma once + +#include "../bindings.h" +#include "interop/context_wrappers.h" + +namespace TracksRS { + +struct EventDataW { + Tracks::ffi::EventData* internal_event_data; + + EventDataW(Tracks::ffi::EventData* event_data) : internal_event_data(event_data) {}; + EventDataW(EventDataW const&) = delete; + EventDataW(EventDataW&& o) noexcept : internal_event_data(o.internal_event_data) { + o.internal_event_data = nullptr; + } + + operator Tracks::ffi::EventData*() { + return internal_event_data; + } + + operator Tracks::ffi::EventData const*() const { + return internal_event_data; + } + + ~EventDataW() { + if (internal_event_data) { + Tracks::ffi::event_data_dispose(internal_event_data); + } + } +}; + +struct CoroutineManagerW { + Tracks::ffi::CoroutineManager* internal_coroutine; + + CoroutineManagerW() { + internal_coroutine = Tracks::ffi::create_coroutine_manager(); + } + CoroutineManagerW(Tracks::ffi::CoroutineManager* coroutine) : internal_coroutine(coroutine) {}; + CoroutineManagerW(CoroutineManagerW const&) = delete; + CoroutineManagerW(CoroutineManagerW&& o) noexcept : internal_coroutine(o.internal_coroutine) { + o.internal_coroutine = nullptr; + } + + operator Tracks::ffi::CoroutineManager*() { + return internal_coroutine; + } + + operator Tracks::ffi::CoroutineManager const*() const { + return internal_coroutine; + } + + ~CoroutineManagerW() { + if (internal_coroutine) { + Tracks::ffi::destroy_coroutine_manager(internal_coroutine); + } + } + + void StartCoroutine(float bpm, float songTime, BaseProviderContextW const& context, TracksHolderW& tracksHolder, + EventDataW const& eventData) { + Tracks::ffi::start_event_coroutine(internal_coroutine, bpm, songTime, context.internal_base_provider_context, + tracksHolder, eventData.internal_event_data); + } + + void PollCoroutines(float songTime, BaseProviderContextW const& context, TracksHolderW& tracksHolder) { + Tracks::ffi::poll_events(internal_coroutine, songTime, context.internal_base_provider_context, tracksHolder); + } +}; +} // namespace TracksAD \ No newline at end of file diff --git a/shared/interop/definition_wrappers.h b/shared/interop/definition_wrappers.h new file mode 100644 index 0000000..2a7246d --- /dev/null +++ b/shared/interop/definition_wrappers.h @@ -0,0 +1,136 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "../Vector.h" +#include "../Hash.h" +#include "beatsaber-hook/shared/config/rapidjson-utils.hpp" +#include "../bindings.h" +#include "context_wrappers.h" +#include "property_wrappers.h" + +#include "beatsaber-hook/shared/rapidjson/include/rapidjson/document.h" + +extern Tracks::ffi::FFIJsonValue const* convert_rapidjson(rapidjson::Value const& value); + +/// Wrapper for Tracks::ffi::BasePointDefinition - represents a point definition for interpolation +namespace TracksRS { + +class PointDefinitionW { +public: + explicit PointDefinitionW(rapidjson::Value const& value, Tracks::ffi::WrapBaseValueType type, + std::shared_ptr base_provider_context) { + auto* json = convert_rapidjson(value); + this->base_provider_context = base_provider_context; + + internalPointDefinition = std::shared_ptr( + Tracks::ffi::tracks_make_base_point_definition(json, type, *base_provider_context), + [](Tracks::ffi::BasePointDefinition* ptr) { + if (!ptr) return; + Tracks::ffi::base_point_definition_free(ptr); + }); + } + + // takes ownership + PointDefinitionW(Tracks::ffi::BasePointDefinition* pointDefinition, + std::shared_ptr context) + : base_provider_context(context) { + internalPointDefinition = + std::shared_ptr(pointDefinition, [](Tracks::ffi::BasePointDefinition* ptr) { + if (!ptr) return; + Tracks::ffi::base_point_definition_free(ptr); + }); + } + + ~PointDefinitionW() = default; + + PointDefinitionW(PointDefinitionW const& other) = default; + explicit PointDefinitionW(std::nullptr_t) : internalPointDefinition(nullptr) {}; + + [[nodiscard]] + Tracks::ffi::WrapBaseValueType GetType() const { + return Tracks::ffi::tracks_base_point_definition_get_type(internalPointDefinition.get()); + } + + [[nodiscard]] + Tracks::ffi::WrapBaseValue Interpolate(float time) const { + bool last; + return Interpolate(time, last); + } + + Tracks::ffi::WrapBaseValue Interpolate(float time, bool& last) const { + auto result = Tracks::ffi::tracks_interpolate_base_point_definition(internalPointDefinition.get(), time, &last, + *base_provider_context); + return result; + } + + NEVector::Vector3 InterpolateVec3(float time, bool& last) const { + auto result = Interpolate(time, last); + return { result.value.vec3.x, result.value.vec3.y, result.value.vec3.z }; + } + + NEVector::Quaternion InterpolateQuaternion(float time, bool& last) const { + auto result = Interpolate(time, last); + return { result.value.quat.x, result.value.quat.y, result.value.quat.z, result.value.quat.w }; + } + + float InterpolateLinear(float time, bool& last) const { + auto result = Interpolate(time, last); + return result.value.float_v; + } + + NEVector::Vector4 InterpolateVector4(float time, bool& last) const { + auto result = Interpolate(time, last); + return { result.value.vec4.x, result.value.vec4.y, result.value.vec4.z, result.value.vec4.w }; + } + + NEVector::Vector3 InterpolateVec3(float time) const { + bool last; + return InterpolateVec3(time, last); + } + + NEVector::Quaternion InterpolateQuaternion(float time) const { + bool last; + return InterpolateQuaternion(time, last); + } + + float InterpolateLinear(float time) const { + bool last; + return InterpolateLinear(time, last); + } + + NEVector::Vector4 InterpolateVector4(float time) const { + bool last; + return InterpolateVector4(time, last); + } + + uintptr_t count() const { + return Tracks::ffi::tracks_base_point_definition_count(internalPointDefinition.get()); + } + + bool hasBaseProvider() const { + return Tracks::ffi::tracks_base_point_definition_has_base_provider(internalPointDefinition.get()); + } + + operator Tracks::ffi::BasePointDefinition const*() const { + return internalPointDefinition.get(); + } + + operator Tracks::ffi::BasePointDefinition*() { + return internalPointDefinition.get(); + } + +private: + constexpr PointDefinitionW() = default; + + std::shared_ptr internalPointDefinition; + std::shared_ptr base_provider_context; +}; + + + +} // namespace TracksRS \ No newline at end of file diff --git a/shared/interop/property_helpers.h b/shared/interop/property_helpers.h new file mode 100644 index 0000000..ce23329 --- /dev/null +++ b/shared/interop/property_helpers.h @@ -0,0 +1,140 @@ +#include "../Vector.h" +#include "../bindings.h" +#include "./track_wrapper.h" +#include "./time_wrapper.h" + + +namespace TracksRS { +namespace Animation { + +#pragma region property_utils + +// Conversion functions +[[nodiscard]] constexpr static NEVector::Vector3 ToVector3(Tracks::ffi::WrapBaseValue const& val) { + if (val.ty != Tracks::ffi::WrapBaseValueType::Vec3) return {}; + return { val.value.vec3.x, val.value.vec3.y, val.value.vec3.z }; +} + +[[nodiscard]] constexpr static NEVector::Vector4 ToVector4(Tracks::ffi::WrapBaseValue const& val) { + if (val.ty != Tracks::ffi::WrapBaseValueType::Vec4) return {}; + return { val.value.vec4.x, val.value.vec4.y, val.value.vec4.z, val.value.vec4.w }; +} + +[[nodiscard]] constexpr static NEVector::Quaternion ToQuaternion(Tracks::ffi::WrapBaseValue const& val) { + if (val.ty != Tracks::ffi::WrapBaseValueType::Quat) return {}; + return { val.value.quat.x, val.value.quat.y, val.value.quat.z, val.value.quat.w }; +} + +[[nodiscard]] constexpr static float ToFloat(Tracks::ffi::WrapBaseValue const& val) { + if (val.ty != Tracks::ffi::WrapBaseValueType::Float) return {}; + return val.value.float_v; +} + +// Base versions that return WrapBaseValue +[[nodiscard]] +constexpr static std::vector +getProperties(std::span tracks, Tracks::ffi::PropertyNames name, TracksRS::TimeUnit time) { + std::vector properties; + properties.reserve(tracks.size()); + for (auto const& track : tracks) { + auto property = track.GetPropertyNamed(name); + auto value = property.GetValue(); + if (TimeUnit(value.last_updated) <= time) continue; + if (!value.value.has_value) continue; + properties.push_back(value.value.value); + } + + return properties; +} + +[[nodiscard]] +static std::vector getPathProperties(std::span tracks, + Tracks::ffi::PropertyNames name, uint64_t time) { + std::vector properties; + + properties.reserve(tracks.size()); + for (auto const& track : tracks) { + auto property = track.GetPathPropertyNamed(name); + auto value = property.Interpolate(time); + if (!value.has_value()) continue; + properties.push_back(*value); + } + + return properties; +} + +// Macro to generate type-specific property getters +#define GENERATE_PROPERTY_GETTERS(ReturnType, Suffix, Conversion) \ + [[nodiscard]] \ + static std::vector getProperties##Suffix(std::span tracks, \ + Tracks::ffi::PropertyNames name, TimeUnit time) { \ + std::vector properties; \ + properties.reserve(tracks.size()); \ + for (auto const& track : tracks) { \ + auto property = track.GetPropertyNamed(name); \ + auto value = property.GetValue(); \ + if (!value.value.has_value) continue; \ + if (TimeUnit(value.last_updated) <= time) continue; \ + properties.push_back(Conversion(value.value.value)); \ + } \ + return properties; \ + } \ + \ + [[nodiscard]] \ + static std::vector getPathProperties##Suffix(std::span tracks, \ + Tracks::ffi::PropertyNames name, float time) { \ + std::vector properties; \ + properties.reserve(tracks.size()); \ + for (auto const& track : tracks) { \ + auto property = track.GetPathPropertyNamed(name); \ + auto value = property.Interpolate(time); \ + if (!value.has_value()) continue; \ + properties.push_back(Conversion(*value)); \ + } \ + return properties; \ + } + +// Generate specialized versions for different types +GENERATE_PROPERTY_GETTERS(NEVector::Vector3, Vec3, ToVector3) +GENERATE_PROPERTY_GETTERS(NEVector::Vector4, Vec4, ToVector4) +GENERATE_PROPERTY_GETTERS(NEVector::Quaternion, Quat, ToQuaternion) +GENERATE_PROPERTY_GETTERS(float, Float, ToFloat) + +// Macro to generate addition functions for spans +#define GENERATE_ADD_FUNCTIONS(Type, TypeName) \ + [[nodiscard]] \ + constexpr static std::optional add##TypeName##s(std::span values) { \ + if (values.empty()) return std::nullopt; \ + Type result = values[0]; \ + for (size_t i = 1; i < values.size(); ++i) { \ + result = result + values[i]; \ + } \ + return result; \ + } + +// Macro to generate multiplication functions for spans +#define GENERATE_MUL_FUNCTIONS(Type, TypeName) \ + [[nodiscard]] \ + constexpr static std::optional multiply##TypeName##s(std::span values) { \ + if (values.empty()) return std::nullopt; \ + Type result = values[0]; \ + for (size_t i = 1; i < values.size(); ++i) { \ + result = result * values[i]; \ + } \ + return result; \ + } + +// Generate addition functions for different types +GENERATE_ADD_FUNCTIONS(NEVector::Vector3, Vector3) +GENERATE_ADD_FUNCTIONS(NEVector::Vector4, Vector4) +GENERATE_ADD_FUNCTIONS(float, Float) + +// Generate multiplication functions for different types +GENERATE_MUL_FUNCTIONS(NEVector::Vector3, Vector3) +GENERATE_MUL_FUNCTIONS(NEVector::Vector4, Vector4) +GENERATE_MUL_FUNCTIONS(NEVector::Quaternion, Quaternion) +GENERATE_MUL_FUNCTIONS(float, Float) + +#pragma endregion // property_utils +} // namespace Animation +} // namespace TracksRS \ No newline at end of file diff --git a/shared/interop/property_wrappers.h b/shared/interop/property_wrappers.h new file mode 100644 index 0000000..78a32c2 --- /dev/null +++ b/shared/interop/property_wrappers.h @@ -0,0 +1,375 @@ +#pragma once + +#include +#include +#include + +#include "../Vector.h" +#include "../bindings.h" + +#include "./context_wrappers.h" +#include "./time_wrapper.h" + +namespace TracksRS { + +using PropertyNames = Tracks::ffi::PropertyNames; + +/// Wrapper for Tracks::ffi::ValueProperty - represents an animated property value +/// Owned by TrackW, use as a non-owning reference +struct PropertyW { + Tracks::ffi::ValueProperty* property; + + constexpr PropertyW() = default; + constexpr PropertyW(Tracks::ffi::ValueProperty* property) : property(property) {} + + operator Tracks::ffi::ValueProperty const*() const { + return property; + } + operator Tracks::ffi::ValueProperty*() const { + return property; + } + operator bool() const { + return property != nullptr; + } + + [[nodiscard]] Tracks::ffi::WrapBaseValueType GetType() const { + CRASH_UNLESS(property); + return Tracks::ffi::property_get_type(property); + } + + [[nodiscard]] Tracks::ffi::CValueProperty GetValue() const { + CRASH_UNLESS(property); + return Tracks::ffi::property_get_value(property); + } + + [[nodiscard]] TimeUnit GetTime() const { + CRASH_UNLESS(property); + return Tracks::ffi::property_get_last_updated(property); + } + + constexpr bool hasUpdated(Tracks::ffi::CValueProperty value, TimeUnit lastCheckedTime = {}) const { + return lastCheckedTime == TimeUnit() || TimeUnit(value.last_updated) >= lastCheckedTime; + } + + [[nodiscard]] std::optional GetQuat(TimeUnit lastCheckedTime = {}) const { + auto value = GetValue(); + if (!value.value.has_value) { + return std::nullopt; + } + if (!hasUpdated(value, lastCheckedTime)) { + return std::nullopt; + } + if (value.value.value.ty != Tracks::ffi::WrapBaseValueType::Quat) { + return std::nullopt; + } + auto v = value.value.value.value; + return NEVector::Quaternion{ v.quat.x, v.quat.y, v.quat.z, v.quat.w }; + } + + [[nodiscard]] std::optional GetVec3(TimeUnit lastCheckedTime = {}) const { + auto value = GetValue(); + if (!value.value.has_value) return std::nullopt; + if (!hasUpdated(value, lastCheckedTime)) return std::nullopt; + if (value.value.value.ty != Tracks::ffi::WrapBaseValueType::Vec3) return std::nullopt; + + auto v = value.value.value.value; + return NEVector::Vector3{ v.vec3.x, v.vec3.y, v.vec3.z }; + } + + [[nodiscard]] std::optional GetVec4(TimeUnit lastCheckedTime = {}) const { + auto value = GetValue(); + if (!value.value.has_value) return std::nullopt; + if (!hasUpdated(value, lastCheckedTime)) return std::nullopt; + if (value.value.value.ty != Tracks::ffi::WrapBaseValueType::Vec4) return std::nullopt; + auto v = value.value.value.value; + + return NEVector::Vector4{ v.vec4.x, v.vec4.y, v.vec4.z, v.vec4.w }; + } + + [[nodiscard]] std::optional GetFloat(TimeUnit lastCheckedTime = {}) const { + auto value = GetValue(); + if (!value.value.has_value) return std::nullopt; + if (!hasUpdated(value, lastCheckedTime)) return std::nullopt; + if (value.value.value.ty != Tracks::ffi::WrapBaseValueType::Float) return std::nullopt; + + return value.value.value.value.float_v; + } +}; + +/// Wrapper for Tracks::ffi::PathProperty - represents an animated path/curve +/// Owned by TrackW, use as a non-owning reference with context reference for interpolation +struct PathPropertyW { + Tracks::ffi::PathProperty* property; + std::shared_ptr internal_tracks_context; + + PathPropertyW(Tracks::ffi::PathProperty* property, + std::shared_ptr internal_tracks_context) + : property(property), internal_tracks_context(internal_tracks_context) {} + + operator Tracks::ffi::PathProperty*() const { + return property; + } + + operator Tracks::ffi::PathProperty const*() const { + return property; + } + + operator bool() const { + return property != nullptr; + } + + [[nodiscard]] + float GetTime() const { + return Tracks::ffi::path_property_get_time(property); + } + + [[nodiscard]] + std::optional Interpolate(float time) const { + auto result = Tracks::ffi::path_property_interpolate(property, time, *this->internal_tracks_context); + if (!result.has_value) { + return std::nullopt; + } + return result.value; + } + + [[nodiscard]] + std::optional InterpolateVec3(float time) const { + auto result = Interpolate(time); + if (!result) return std::nullopt; + auto unwrapped = *result; + if (unwrapped.ty != Tracks::ffi::WrapBaseValueType::Vec3) { + return std::nullopt; + } + + return NEVector::Vector3{ unwrapped.value.vec3.x, unwrapped.value.vec3.y, unwrapped.value.vec3.z }; + } + + [[nodiscard]] + std::optional InterpolateVec4(float time) const { + auto result = Interpolate(time); + if (!result) return std::nullopt; + auto unwrapped = *result; + if (unwrapped.ty != Tracks::ffi::WrapBaseValueType::Vec4) { + return std::nullopt; + } + + return NEVector::Vector4{ unwrapped.value.vec4.x, unwrapped.value.vec4.y, unwrapped.value.vec4.z, + unwrapped.value.vec4.w }; + } + + [[nodiscard]] + std::optional InterpolateQuat(float time) const { + auto result = Interpolate(time); + if (!result) return std::nullopt; + auto unwrapped = *result; + if (unwrapped.ty != Tracks::ffi::WrapBaseValueType::Quat) { + return std::nullopt; + } + + return NEVector::Quaternion{ unwrapped.value.quat.x, unwrapped.value.quat.y, unwrapped.value.quat.z, + unwrapped.value.quat.w }; + } + + [[nodiscard]] + std::optional InterpolateLinear(float time) const { + auto result = Interpolate(time); + if (!result) return std::nullopt; + if (result->ty != Tracks::ffi::WrapBaseValueType::Float) { + return std::nullopt; + } + + return result->value.float_v; + } + + [[nodiscard]] Tracks::ffi::WrapBaseValueType GetType() const { + return Tracks::ffi::path_property_get_type(property); + } +}; + +/// Collection of all properties on a Track +struct PropertiesMapW { + PropertyW position; + PropertyW offsetPosition; + PropertyW rotation; + PropertyW offsetRotation; + PropertyW scale; + PropertyW localRotation; + PropertyW localPosition; + PropertyW dissolve; + PropertyW dissolveArrow; + PropertyW time; + PropertyW cuttable; + PropertyW color; + PropertyW attentuation; + PropertyW fogOffset; + PropertyW heightFogStartY; + PropertyW heightFogHeight; + + PropertiesMapW(Tracks::ffi::CPropertiesMap map) + : position(map.position), offsetPosition(map.offset_position), rotation(map.rotation), + offsetRotation(map.offset_rotation), scale(map.scale), localRotation(map.local_rotation), + localPosition(map.local_position), dissolve(map.dissolve), dissolveArrow(map.dissolve_arrow), time(map.time), + cuttable(map.cuttable), color(map.color), attentuation(map.attentuation), fogOffset(map.fog_offset), + heightFogStartY(map.height_fog_start_y), heightFogHeight(map.height_fog_height) {} +}; + +struct PathPropertiesMapW { + PathPropertyW position; + PathPropertyW offsetPosition; + PathPropertyW rotation; + PathPropertyW offsetRotation; + PathPropertyW scale; + PathPropertyW localRotation; + PathPropertyW localPosition; + PathPropertyW definitePosition; + PathPropertyW dissolve; + PathPropertyW dissolveArrow; + PathPropertyW cuttable; + PathPropertyW color; + + PathPropertiesMapW(Tracks::ffi::CPathPropertiesMap map, std::shared_ptr context) + : position(map.position, context), offsetPosition(map.offset_position, context), rotation(map.rotation, context), + offsetRotation(map.offset_rotation, context), scale(map.scale, context), + localRotation(map.local_rotation, context), localPosition(map.local_position, context), + definitePosition(map.definite_position, context), dissolve(map.dissolve, context), + dissolveArrow(map.dissolve_arrow, context), cuttable(map.cuttable, context), color(map.color, context) {} +}; + +// Property wrapper value struct - contains all possible property values from a track +struct PropertiesValuesW { + constexpr PropertiesValuesW(Tracks::ffi::CPropertiesValues values) { + if (values.position.has_value) { + position = NEVector::Vector3{ values.position.value.x, values.position.value.y, values.position.value.z }; + } + if (values.offset_position.has_value) { + offsetPosition = NEVector::Vector3{ values.offset_position.value.x, values.offset_position.value.y, + values.offset_position.value.z }; + } + if (values.rotation.has_value) { + rotation = NEVector::Quaternion{ values.rotation.value.x, values.rotation.value.y, values.rotation.value.z, + values.rotation.value.w }; + } + if (values.offset_rotation.has_value) { + offsetRotation = NEVector::Quaternion{ values.offset_rotation.value.x, values.offset_rotation.value.y, + values.offset_rotation.value.z, values.offset_rotation.value.w }; + } + if (values.scale.has_value) { + scale = NEVector::Vector3{ values.scale.value.x, values.scale.value.y, values.scale.value.z }; + } + if (values.local_rotation.has_value) { + localRotation = NEVector::Quaternion{ values.local_rotation.value.x, values.local_rotation.value.y, + values.local_rotation.value.z, values.local_rotation.value.w }; + } + if (values.local_position.has_value) { + localPosition = NEVector::Vector3{ values.local_position.value.x, values.local_position.value.y, + values.local_position.value.z }; + } + if (values.dissolve.has_value) { + dissolve = values.dissolve.value; + } + if (values.dissolve_arrow.has_value) { + dissolveArrow = values.dissolve_arrow.value; + } + if (values.time.has_value) { + time = values.time.value; + } + if (values.cuttable.has_value) { + cuttable = values.cuttable.value; + } + if (values.color.has_value) { + color = + NEVector::Vector4{ values.color.value.x, values.color.value.y, values.color.value.z, values.color.value.w }; + } + if (values.attentuation.has_value) { + attentuation = values.attentuation.value; + } + if (values.fog_offset.has_value) { + fogOffset = values.fog_offset.value; + } + if (values.height_fog_start_y.has_value) { + heightFogStartY = values.height_fog_start_y.value; + } + if (values.height_fog_height.has_value) { + heightFogHeight = values.height_fog_height.value; + } + } + + std::optional position; + std::optional offsetPosition; + std::optional rotation; + std::optional offsetRotation; + std::optional scale; + std::optional localRotation; + std::optional localPosition; + std::optional dissolve; + std::optional dissolveArrow; + std::optional time; + std::optional cuttable; + std::optional color; + std::optional attentuation; + std::optional fogOffset; + std::optional heightFogStartY; + std::optional heightFogHeight; +}; + +struct PathPropertiesValuesW { + constexpr PathPropertiesValuesW(Tracks::ffi::CPathPropertiesValues values) { + if (values.position.has_value) { + position = NEVector::Vector3{ values.position.value.x, values.position.value.y, values.position.value.z }; + } + if (values.offset_position.has_value) { + offsetPosition = NEVector::Vector3{ values.offset_position.value.x, values.offset_position.value.y, + values.offset_position.value.z }; + } + if (values.rotation.has_value) { + rotation = NEVector::Quaternion{ values.rotation.value.x, values.rotation.value.y, values.rotation.value.z, + values.rotation.value.w }; + } + if (values.offset_rotation.has_value) { + offsetRotation = NEVector::Quaternion{ values.offset_rotation.value.x, values.offset_rotation.value.y, + values.offset_rotation.value.z, values.offset_rotation.value.w }; + } + if (values.scale.has_value) { + scale = NEVector::Vector3{ values.scale.value.x, values.scale.value.y, values.scale.value.z }; + } + if (values.local_rotation.has_value) { + localRotation = NEVector::Quaternion{ values.local_rotation.value.x, values.local_rotation.value.y, + values.local_rotation.value.z, values.local_rotation.value.w }; + } + if (values.local_position.has_value) { + localPosition = NEVector::Vector3{ values.local_position.value.x, values.local_position.value.y, + values.local_position.value.z }; + } + if (values.definite_position.has_value) { + definitePosition = values.definite_position.value; + } + if (values.dissolve.has_value) { + dissolve = values.dissolve.value; + } + if (values.dissolve_arrow.has_value) { + dissolveArrow = values.dissolve_arrow.value; + } + if (values.cuttable.has_value) { + cuttable = values.cuttable.value; + } + if (values.color.has_value) { + color = + NEVector::Vector4{ values.color.value.x, values.color.value.y, values.color.value.z, values.color.value.w }; + } + } + + std::optional position; + std::optional offsetPosition; + std::optional rotation; + std::optional offsetRotation; + std::optional scale; + std::optional localRotation; + std::optional localPosition; + std::optional definitePosition; + std::optional dissolve; + std::optional dissolveArrow; + std::optional cuttable; + std::optional color; +}; + +} // namespace TracksRS \ No newline at end of file diff --git a/shared/interop/time_wrapper.h b/shared/interop/time_wrapper.h new file mode 100644 index 0000000..7d3590e --- /dev/null +++ b/shared/interop/time_wrapper.h @@ -0,0 +1,55 @@ +#pragma once +#include "../bindings.h" + +namespace TracksRS { + +/// Time unit wrapper with nanosecond precision +struct TimeUnit { + Tracks::ffi::CTimeUnit time; + + constexpr TimeUnit(Tracks::ffi::CTimeUnit time) : time(time) {} + constexpr TimeUnit() = default; + constexpr TimeUnit(TimeUnit const&) = default; + + [[nodiscard]] constexpr operator Tracks::ffi::CTimeUnit() const { + return time; + } + + // get seconds + constexpr uint64_t get_seconds() const { + return time._0; + } + + // get nanoseconds + constexpr uint64_t get_nanoseconds() const { + return time._1; + } + + constexpr bool operator<(TimeUnit o) const { + return get_seconds() < o.get_seconds() || + (o.get_seconds() == get_seconds() && get_nanoseconds() < o.get_nanoseconds()); + } + + constexpr bool operator>(TimeUnit o) const { + return get_seconds() > o.get_seconds() || + (o.get_seconds() == get_seconds() && get_nanoseconds() > o.get_nanoseconds()); + } + + constexpr bool operator==(TimeUnit o) const { + return get_seconds() == o.get_seconds() && get_nanoseconds() == o.get_nanoseconds(); + } + + constexpr bool operator!=(TimeUnit o) const { + return get_seconds() != o.get_seconds() || get_nanoseconds() != o.get_nanoseconds(); + } + + constexpr bool operator<=(TimeUnit o) const { + return o == *this || *this < o; + } + + constexpr bool operator>=(TimeUnit o) const { + return o == *this || *this > o; + } +}; + +} // namespace TracksRS \ No newline at end of file diff --git a/shared/interop/track_wrapper.h b/shared/interop/track_wrapper.h new file mode 100644 index 0000000..66d1ae2 --- /dev/null +++ b/shared/interop/track_wrapper.h @@ -0,0 +1,218 @@ +#pragma once + +#include "../bindings.h" + +#include "./context_wrappers.h" +#include "./property_wrappers.h" +#include "../Constants.h" + +#include "UnityEngine/GameObject.hpp" + +namespace TracksRS { + + +struct TrackW { + Tracks::ffi::TrackKeyFFI track = Tracks::ffi::TrackKeyFFI{ static_cast(-1) }; + std::shared_ptr internal_tracks_context; + std::shared_ptr base_provider_context; + bool v2; + + // using CWrappedCallback = void (* *)(struct Tracks::ffi::GameObject, bool, void*); + using CWrappedCallback = decltype(Tracks::ffi::track_register_game_object_callback(nullptr, nullptr, nullptr)); + + constexpr TrackW() = default; + TrackW(Tracks::ffi::TrackKeyFFI track, bool v2, std::shared_ptr internal_tracks_context, + std::shared_ptr base_provider_context) + : track(track), v2(v2), internal_tracks_context(internal_tracks_context), + base_provider_context(base_provider_context) {} + + operator Tracks::ffi::TrackKeyFFI() const { + return track; + } + + operator bool() const { + return track._0 != -1; + } + + bool operator==(TrackW const& rhs) const { + return this->track._0 == rhs.track._0; + } + + bool operator<(TrackW const& rhs) const { + return this->track._0 < rhs.track._0; + } + + [[nodiscard]] Tracks::ffi::Track* getTrackPtr() const { + return Tracks::ffi::tracks_holder_get_track_mut(*internal_tracks_context, track); + } + + Tracks::ffi::PropertyNames AliasPropertyName(Tracks::ffi::PropertyNames original) const { + // if v3, return original + if (!v2) return original; + // alias offsetPosition into position + if (original == Tracks::ffi::PropertyNames::OffsetPosition) return Tracks::ffi::PropertyNames::Position; + + return original; + } + + std::string_view AliasPropertyName(std::string_view original) const { + // if v3, return original + if (!v2) return original; + // alias offsetPosition into position + if (original == TracksAD::Constants::OFFSET_POSITION) return TracksAD::Constants::POSITION; + + return original; + } + + [[nodiscard]] PropertyW GetProperty(std::string_view name) const { + auto ptr = getTrackPtr(); + auto prop = Tracks::ffi::track_get_property(ptr, AliasPropertyName(name).data()); + return PropertyW(prop); + } + [[nodiscard]] PropertyW GetPropertyNamed(Tracks::ffi::PropertyNames name) const { + auto ptr = getTrackPtr(); + auto prop = Tracks::ffi::track_get_property_by_name(ptr, AliasPropertyName(name)); + return PropertyW(prop); + } + + [[nodiscard]] PathPropertyW GetPathProperty(std::string_view name) const { + auto ptr = getTrackPtr(); + auto prop = Tracks::ffi::track_get_path_property(ptr, AliasPropertyName(name).data()); + return PathPropertyW(prop, base_provider_context); + } + [[nodiscard]] PathPropertyW GetPathPropertyNamed(Tracks::ffi::PropertyNames name) const { + auto track = getTrackPtr(); + auto prop = Tracks::ffi::track_get_path_property_by_name(getTrackPtr(), AliasPropertyName(name)); + return PathPropertyW(prop, base_provider_context); + } + + [[nodiscard]] + PropertiesMapW GetPropertiesMapW() const { + auto track = getTrackPtr(); + auto map = Tracks::ffi::track_get_properties_map(track); + return PropertiesMapW(map); + } + + [[nodiscard]] + PathPropertiesMapW GetPathPropertiesMapW() const { + auto track = getTrackPtr(); + auto map = Tracks::ffi::track_get_path_properties_map(track); + return PathPropertiesMapW(map, base_provider_context); + } + + [[nodiscard]] + PropertiesValuesW GetPropertiesValuesW() const { + auto track = getTrackPtr(); + auto values = Tracks::ffi::track_get_properties_values(track); + return PropertiesValuesW(values); + } + + [[nodiscard]] + PathPropertiesValuesW GetPathPropertiesValuesW(float time) const { + auto track = getTrackPtr(); + auto values = Tracks::ffi::track_get_path_properties_values(track, time, *base_provider_context); + return PathPropertiesValuesW(values); + } + + void RegisterGameObject(UnityEngine::GameObject* gameObject) const { + auto ptr = getTrackPtr(); + Tracks::ffi::track_register_game_object(ptr, Tracks::ffi::GameObject{ .ptr = gameObject }); + } + + void UnregisterGameObject(UnityEngine::GameObject* gameObject) const { + auto track = getTrackPtr(); + Tracks::ffi::track_unregister_game_object(track, Tracks::ffi::GameObject{ .ptr = gameObject }); + } + + // very nasty + CWrappedCallback RegisterGameObjectCallback(std::function callback) const { + if (!callback) { + return nullptr; + } + + // leaks memory, oh well + auto* callbackPtr = new std::function(std::move(callback)); + + // wrap the callback to a function pointer + // this is a C-style function pointer that can be used in the FFI + auto wrapper = +[](Tracks::ffi::GameObject gameObjectWrapper, bool isNew, void* userData) { + auto* cb = reinterpret_cast*>(userData); + auto gameObject = reinterpret_cast(const_cast(gameObjectWrapper.ptr)); + + (*cb)(gameObject, isNew); + }; + + auto track = getTrackPtr(); + return Tracks::ffi::track_register_game_object_callback(track, wrapper, callbackPtr); + } + + void RemoveGameObjectCallback(CWrappedCallback callback) const { + if (!callback) { + return; + } + + auto track = getTrackPtr(); + Tracks::ffi::track_remove_game_object_callback(track, callback); + } + + void RegisterProperty(std::string_view id, PropertyW property) { + auto track = getTrackPtr(); + Tracks::ffi::track_register_property(track, id.data(), const_cast(property.property)); + } + void RegisterPathProperty(std::string_view id, PathPropertyW property) const { + auto track = getTrackPtr(); + Tracks::ffi::track_register_path_property(track, id.data(), property); + } + + [[nodiscard]] Tracks::ffi::CPropertiesMap GetPropertiesMap() const { + auto track = getTrackPtr(); + return Tracks::ffi::track_get_properties_map(track); + } + + [[nodiscard]] Tracks::ffi::CPathPropertiesMap GetPathPropertiesMap() const { + auto track = getTrackPtr(); + return Tracks::ffi::track_get_path_properties_map(track); + } + + /** + * @brief Get the Name object + * + * @return std::string_view Return a string view as the original string is leaked from the FFI. + */ + [[nodiscard]] std::string_view GetName() const { + auto track = getTrackPtr(); + return Tracks::ffi::track_get_name(track); + } + + /** + * @brief Set the Name object + * + * @param name The name to set + */ + void SetName(std::string_view name) const { + auto track = getTrackPtr(); + Tracks::ffi::track_set_name(track, name.data()); + } + + [[nodiscard]] std::span GetGameObjects() const { + static_assert(sizeof(UnityEngine::GameObject*) == sizeof(Tracks::ffi::GameObject), + "Tracks wrapper and GameObject pointer do not match size!"); + std::size_t count = 0; + auto track = getTrackPtr(); + auto const* ptr = Tracks::ffi::track_get_game_objects(track, &count); + auto const* castedPtr = reinterpret_cast(ptr); + + return std::span(castedPtr, count); + } +}; +} // namespace TracksAD + +namespace std { +template <> struct hash { + size_t operator()(TracksRS::TrackW const& obj) const { + size_t track_hash = std::hash()(obj.track._0); + + return track_hash; + } +}; +} // namespace std diff --git a/src/Animation/GameObjectTrackController.cpp b/src/Animation/GameObjectTrackController.cpp index 1f91ad3..fb98fbf 100644 --- a/src/Animation/GameObjectTrackController.cpp +++ b/src/Animation/GameObjectTrackController.cpp @@ -6,6 +6,9 @@ #include "sombrero/shared/Vector3Utils.hpp" #include "sombrero/shared/QuaternionUtils.hpp" +#include "interop/property_wrappers.h" +#include "interop/property_helpers.h" + using namespace Tracks; bool GameObjectTrackController::LeftHanded = false; @@ -122,11 +125,11 @@ void GameObjectTrackController::UpdateData(bool force) { } else { // now - auto localRotations = Animation::getPropertiesQuat(tracks, PropertyNames::LocalRotation, lastCheckedTime); - auto rotations = Animation::getPropertiesQuat(tracks, PropertyNames::Rotation, lastCheckedTime); - auto positions = Animation::getPropertiesVec3(tracks, PropertyNames::Position, lastCheckedTime); - auto localPositions = Animation::getPropertiesVec3(tracks, PropertyNames::LocalPosition, lastCheckedTime); - auto scales = Animation::getPropertiesVec3(tracks, PropertyNames::Scale, lastCheckedTime); + auto localRotations = TracksRS::Animation::getPropertiesQuat(tracks, PropertyNames::LocalRotation, lastCheckedTime); + auto rotations = TracksRS::Animation::getPropertiesQuat(tracks, PropertyNames::Rotation, lastCheckedTime); + auto positions = TracksRS::Animation::getPropertiesVec3(tracks, PropertyNames::Position, lastCheckedTime); + auto localPositions = TracksRS::Animation::getPropertiesVec3(tracks, PropertyNames::LocalPosition, lastCheckedTime); + auto scales = TracksRS::Animation::getPropertiesVec3(tracks, PropertyNames::Scale, lastCheckedTime); TRACKS_LIST_OPERATE_MULTIPLE(localRotation, localRotations, *); TRACKS_LIST_OPERATE_MULTIPLE(rotation, rotations, *);