diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index 912dff45..92999ef0 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -565,6 +565,23 @@ void InternalCall_Transform_Rotate(uint64_t gameObjectUUID, XCEngine::Math::Vect gameObject->GetTransform()->Rotate(*eulers, ResolveManagedSpace(relativeTo)); } +void InternalCall_Transform_RotateAxisAngle( + uint64_t gameObjectUUID, + XCEngine::Math::Vector3* axis, + float angle, + int32_t relativeTo) { + if (!axis) { + return; + } + + Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); + if (!gameObject || !gameObject->GetTransform()) { + return; + } + + gameObject->GetTransform()->Rotate(*axis, angle, ResolveManagedSpace(relativeTo)); +} + void InternalCall_Transform_LookAt(uint64_t gameObjectUUID, XCEngine::Math::Vector3* target) { if (!target) { return; @@ -578,6 +595,22 @@ void InternalCall_Transform_LookAt(uint64_t gameObjectUUID, XCEngine::Math::Vect gameObject->GetTransform()->LookAt(*target); } +void InternalCall_Transform_LookAtWithUp( + uint64_t gameObjectUUID, + XCEngine::Math::Vector3* target, + XCEngine::Math::Vector3* up) { + if (!target || !up) { + return; + } + + Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); + if (!gameObject || !gameObject->GetTransform()) { + return; + } + + gameObject->GetTransform()->LookAt(*target, *up); +} + void InternalCall_Transform_TransformPoint(uint64_t gameObjectUUID, XCEngine::Math::Vector3* point, XCEngine::Math::Vector3* outPoint) { if (!point || !outPoint) { return; @@ -831,7 +864,9 @@ void RegisterInternalCalls() { mono_add_internal_call("XCEngine.InternalCalls::Transform_GetUp", reinterpret_cast(&InternalCall_Transform_GetUp)); mono_add_internal_call("XCEngine.InternalCalls::Transform_Translate", reinterpret_cast(&InternalCall_Transform_Translate)); mono_add_internal_call("XCEngine.InternalCalls::Transform_Rotate", reinterpret_cast(&InternalCall_Transform_Rotate)); + mono_add_internal_call("XCEngine.InternalCalls::Transform_RotateAxisAngle", reinterpret_cast(&InternalCall_Transform_RotateAxisAngle)); mono_add_internal_call("XCEngine.InternalCalls::Transform_LookAt", reinterpret_cast(&InternalCall_Transform_LookAt)); + mono_add_internal_call("XCEngine.InternalCalls::Transform_LookAtWithUp", reinterpret_cast(&InternalCall_Transform_LookAtWithUp)); mono_add_internal_call("XCEngine.InternalCalls::Transform_TransformPoint", reinterpret_cast(&InternalCall_Transform_TransformPoint)); mono_add_internal_call("XCEngine.InternalCalls::Transform_InverseTransformPoint", reinterpret_cast(&InternalCall_Transform_InverseTransformPoint)); mono_add_internal_call("XCEngine.InternalCalls::Transform_TransformDirection", reinterpret_cast(&InternalCall_Transform_TransformDirection)); diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index 8dac6dab..0fc3e111 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -73,6 +73,7 @@ set(XCENGINE_GAME_SCRIPT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/LifecycleProbe.cs ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/TransformConversionProbe.cs ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/TransformMotionProbe.cs + ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/TransformOrientationProbe.cs ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/TransformSpaceProbe.cs ) diff --git a/managed/GameScripts/TransformOrientationProbe.cs b/managed/GameScripts/TransformOrientationProbe.cs new file mode 100644 index 00000000..cc319e0f --- /dev/null +++ b/managed/GameScripts/TransformOrientationProbe.cs @@ -0,0 +1,40 @@ +using XCEngine; + +namespace Gameplay +{ + public sealed class TransformOrientationProbe : MonoBehaviour + { + public Vector3 ObservedForwardAfterAxisAngleRotate; + public Vector3 ObservedForwardAfterWorldAxisAngleRotate; + public Vector3 ObservedForwardAfterDefaultLookAt; + public Vector3 ObservedRightAfterDefaultLookAt; + public Vector3 ObservedUpAfterDefaultLookAt; + public Vector3 ObservedForwardAfterLookAtWithUp; + public Vector3 ObservedRightAfterLookAtWithUp; + public Vector3 ObservedUpAfterLookAtWithUp; + + public void Start() + { + transform.localPosition = new Vector3(0.0f, 0.0f, 0.0f); + transform.localRotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); + + transform.Rotate(new Vector3(0.0f, 1.0f, 0.0f), 90.0f); + ObservedForwardAfterAxisAngleRotate = transform.forward; + + transform.Rotate(new Vector3(1.0f, 0.0f, 0.0f), 90.0f, Space.World); + ObservedForwardAfterWorldAxisAngleRotate = transform.forward; + + transform.localRotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); + transform.LookAt(new Vector3(1.0f, 0.0f, 1.0f)); + ObservedForwardAfterDefaultLookAt = transform.forward; + ObservedRightAfterDefaultLookAt = transform.right; + ObservedUpAfterDefaultLookAt = transform.up; + + transform.localRotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); + transform.LookAt(new Vector3(1.0f, 0.0f, 1.0f), new Vector3(1.0f, 0.0f, 0.0f)); + ObservedForwardAfterLookAtWithUp = transform.forward; + ObservedRightAfterLookAtWithUp = transform.right; + ObservedUpAfterLookAtWithUp = transform.up; + } + } +} diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index eed59f4a..67027db4 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -101,9 +101,15 @@ namespace XCEngine [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void Transform_Rotate(ulong gameObjectUUID, ref Vector3 eulers, int relativeTo); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Transform_RotateAxisAngle(ulong gameObjectUUID, ref Vector3 axis, float angle, int relativeTo); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void Transform_LookAt(ulong gameObjectUUID, ref Vector3 target); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Transform_LookAtWithUp(ulong gameObjectUUID, ref Vector3 target, ref Vector3 up); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void Transform_TransformPoint(ulong gameObjectUUID, ref Vector3 point, out Vector3 transformedPoint); diff --git a/managed/XCEngine.ScriptCore/Transform.cs b/managed/XCEngine.ScriptCore/Transform.cs index 4cbceaa4..d96ddc9d 100644 --- a/managed/XCEngine.ScriptCore/Transform.cs +++ b/managed/XCEngine.ScriptCore/Transform.cs @@ -231,11 +231,26 @@ namespace XCEngine InternalCalls.Transform_Rotate(GameObjectUUID, ref eulers, (int)relativeTo); } + public void Rotate(Vector3 axis, float angle) + { + Rotate(axis, angle, Space.Self); + } + + public void Rotate(Vector3 axis, float angle, Space relativeTo) + { + InternalCalls.Transform_RotateAxisAngle(GameObjectUUID, ref axis, angle, (int)relativeTo); + } + public void LookAt(Vector3 worldPosition) { InternalCalls.Transform_LookAt(GameObjectUUID, ref worldPosition); } + public void LookAt(Vector3 worldPosition, Vector3 worldUp) + { + InternalCalls.Transform_LookAtWithUp(GameObjectUUID, ref worldPosition, ref worldUp); + } + public Vector3 TransformPoint(Vector3 point) { InternalCalls.Transform_TransformPoint(GameObjectUUID, ref point, out Vector3 transformedPoint); diff --git a/tests/scripting/test_mono_script_runtime.cpp b/tests/scripting/test_mono_script_runtime.cpp index 3993d5fc..0c0a2dc5 100644 --- a/tests/scripting/test_mono_script_runtime.cpp +++ b/tests/scripting/test_mono_script_runtime.cpp @@ -470,6 +470,50 @@ TEST_F(MonoScriptRuntimeTest, TransformConversionApiTransformsPointsAndDirection ExpectVector3Near(host->GetTransform()->InverseTransformDirection(XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f)), XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f)); } +TEST_F(MonoScriptRuntimeTest, TransformOrientationOverloadsSupportAxisAngleAndCustomUpVector) { + Scene* runtimeScene = CreateScene("MonoRuntimeScene"); + GameObject* host = runtimeScene->CreateGameObject("Host"); + ScriptComponent* component = AddScript(host, "Gameplay", "TransformOrientationProbe"); + + engine->OnRuntimeStart(runtimeScene); + engine->OnUpdate(0.016f); + + XCEngine::Math::Vector3 observedForwardAfterAxisAngleRotate; + XCEngine::Math::Vector3 observedForwardAfterWorldAxisAngleRotate; + XCEngine::Math::Vector3 observedForwardAfterDefaultLookAt; + XCEngine::Math::Vector3 observedRightAfterDefaultLookAt; + XCEngine::Math::Vector3 observedUpAfterDefaultLookAt; + XCEngine::Math::Vector3 observedForwardAfterLookAtWithUp; + XCEngine::Math::Vector3 observedRightAfterLookAtWithUp; + XCEngine::Math::Vector3 observedUpAfterLookAtWithUp; + + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterAxisAngleRotate", observedForwardAfterAxisAngleRotate)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterWorldAxisAngleRotate", observedForwardAfterWorldAxisAngleRotate)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterDefaultLookAt", observedForwardAfterDefaultLookAt)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRightAfterDefaultLookAt", observedRightAfterDefaultLookAt)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpAfterDefaultLookAt", observedUpAfterDefaultLookAt)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterLookAtWithUp", observedForwardAfterLookAtWithUp)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRightAfterLookAtWithUp", observedRightAfterLookAtWithUp)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpAfterLookAtWithUp", observedUpAfterLookAtWithUp)); + + ExpectVector3Near(observedForwardAfterAxisAngleRotate, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f)); + ExpectVector3Near(observedForwardAfterWorldAxisAngleRotate, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f)); + + EXPECT_NEAR(observedForwardAfterDefaultLookAt.Magnitude(), 1.0f, 0.001f); + EXPECT_NEAR(observedRightAfterDefaultLookAt.Magnitude(), 1.0f, 0.001f); + EXPECT_NEAR(observedUpAfterDefaultLookAt.Magnitude(), 1.0f, 0.001f); + EXPECT_NEAR(observedForwardAfterLookAtWithUp.Magnitude(), 1.0f, 0.001f); + EXPECT_NEAR(observedRightAfterLookAtWithUp.Magnitude(), 1.0f, 0.001f); + EXPECT_NEAR(observedUpAfterLookAtWithUp.Magnitude(), 1.0f, 0.001f); + + EXPECT_GT((observedRightAfterLookAtWithUp - observedRightAfterDefaultLookAt).Magnitude(), 0.5f); + EXPECT_GT((observedUpAfterLookAtWithUp - observedUpAfterDefaultLookAt).Magnitude(), 0.5f); + + ExpectVector3Near(host->GetTransform()->GetForward(), observedForwardAfterLookAtWithUp); + ExpectVector3Near(host->GetTransform()->GetRight(), observedRightAfterLookAtWithUp); + ExpectVector3Near(host->GetTransform()->GetUp(), observedUpAfterLookAtWithUp); +} + TEST_F(MonoScriptRuntimeTest, ManagedBehaviourCanDisableItselfThroughEnabledProperty) { Scene* runtimeScene = CreateScene("MonoRuntimeScene"); GameObject* host = runtimeScene->CreateGameObject("Host");