diff --git a/engine/include/XCEngine/Scripting/ScriptEngine.h b/engine/include/XCEngine/Scripting/ScriptEngine.h index 700c9875..13fca0bb 100644 --- a/engine/include/XCEngine/Scripting/ScriptEngine.h +++ b/engine/include/XCEngine/Scripting/ScriptEngine.h @@ -12,6 +12,12 @@ #include namespace XCEngine { +namespace Physics { + +class PhysicsWorld; + +} // namespace Physics + namespace Scripting { class ScriptComponent; @@ -25,6 +31,8 @@ public: IScriptRuntime* GetRuntime() const { return m_runtime; } void SetRuntimeFixedDeltaTime(float fixedDeltaTime); float GetRuntimeFixedDeltaTime() const { return m_runtimeFixedDeltaTime; } + void SetRuntimePhysicsWorld(Physics::PhysicsWorld* physicsWorld) { m_runtimePhysicsWorld = physicsWorld; } + Physics::PhysicsWorld* GetRuntimePhysicsWorld() const { return m_runtimePhysicsWorld; } void OnRuntimeStart(Components::Scene* scene); void OnRuntimeStop(); @@ -153,6 +161,7 @@ private: NullScriptRuntime m_nullRuntime; IScriptRuntime* m_runtime = &m_nullRuntime; Components::Scene* m_runtimeScene = nullptr; + Physics::PhysicsWorld* m_runtimePhysicsWorld = nullptr; bool m_runtimeRunning = false; float m_runtimeFixedDeltaTime = DefaultFixedDeltaTime; uint64_t m_runtimeSceneCreatedSubscription = 0; diff --git a/engine/src/Scene/SceneRuntime.cpp b/engine/src/Scene/SceneRuntime.cpp index f9331080..da93b47c 100644 --- a/engine/src/Scene/SceneRuntime.cpp +++ b/engine/src/Scene/SceneRuntime.cpp @@ -203,9 +203,12 @@ void SceneRuntime::CreatePhysicsWorldForScene(Scene* scene) { createInfo.scene = scene; physicsWorld->Initialize(createInfo); m_physicsWorld = std::move(physicsWorld); + Scripting::ScriptEngine::Get().SetRuntimePhysicsWorld(m_physicsWorld.get()); } void SceneRuntime::DestroyPhysicsWorld() { + Scripting::ScriptEngine::Get().SetRuntimePhysicsWorld(nullptr); + if (!m_physicsWorld) { return; } diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index f1b28c2c..1fb190cf 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -5,9 +5,11 @@ #include "Components/LightComponent.h" #include "Components/MeshFilterComponent.h" #include "Components/MeshRendererComponent.h" +#include "Components/RigidbodyComponent.h" #include "Components/TransformComponent.h" #include "Debug/Logger.h" #include "Input/InputManager.h" +#include "Physics/PhysicsWorld.h" #include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h" #include "Scene/Scene.h" #include "Scripting/ScriptComponent.h" @@ -44,6 +46,7 @@ enum class ManagedComponentKind { Unknown, Script, Transform, + Rigidbody, Camera, Light, MeshFilter, @@ -144,6 +147,10 @@ ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoClass* monoClass) { typeInfo.kind = ManagedComponentKind::Transform; return typeInfo; } + if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "Rigidbody") { + typeInfo.kind = ManagedComponentKind::Rigidbody; + return typeInfo; + } if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "Camera") { typeInfo.kind = ManagedComponentKind::Camera; return typeInfo; @@ -263,6 +270,8 @@ Components::Component* FindNativeComponent(Components::GameObject* gameObject, M switch (componentKind) { case ManagedComponentKind::Transform: return gameObject->GetTransform(); + case ManagedComponentKind::Rigidbody: + return gameObject->GetComponent(); case ManagedComponentKind::Camera: return gameObject->GetComponent(); case ManagedComponentKind::Light: @@ -300,6 +309,10 @@ Components::Component* AddOrGetNativeComponent(Components::GameObject* gameObjec switch (componentKind) { case ManagedComponentKind::Transform: return gameObject->GetTransform(); + case ManagedComponentKind::Rigidbody: + return gameObject->GetComponent() + ? static_cast(gameObject->GetComponent()) + : static_cast(gameObject->AddComponent()); case ManagedComponentKind::Camera: return gameObject->GetComponent() ? static_cast(gameObject->GetComponent()) @@ -441,12 +454,47 @@ Components::MeshRendererComponent* FindMeshRendererComponent(uint64_t gameObject return gameObject ? gameObject->GetComponent() : nullptr; } +Components::RigidbodyComponent* FindRigidbodyComponent(uint64_t gameObjectUUID) { + Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); + return gameObject ? gameObject->GetComponent() : nullptr; +} + Components::Space ResolveManagedSpace(int32_t value) { return value == static_cast(Components::Space::World) ? Components::Space::World : Components::Space::Self; } +Physics::PhysicsBodyType ResolveManagedPhysicsBodyType(int32_t value) { + switch (value) { + case static_cast(Physics::PhysicsBodyType::Static): + return Physics::PhysicsBodyType::Static; + case static_cast(Physics::PhysicsBodyType::Kinematic): + return Physics::PhysicsBodyType::Kinematic; + case static_cast(Physics::PhysicsBodyType::Dynamic): + default: + return Physics::PhysicsBodyType::Dynamic; + } +} + +Physics::PhysicsForceMode ResolveManagedForceMode(int32_t value) { + switch (value) { + case static_cast(Physics::PhysicsForceMode::Acceleration): + return Physics::PhysicsForceMode::Acceleration; + case static_cast(Physics::PhysicsForceMode::Impulse): + return Physics::PhysicsForceMode::Impulse; + case static_cast(Physics::PhysicsForceMode::VelocityChange): + return Physics::PhysicsForceMode::VelocityChange; + case static_cast(Physics::PhysicsForceMode::Force): + default: + return Physics::PhysicsForceMode::Force; + } +} + +Physics::PhysicsWorld* FindRuntimePhysicsWorld() { + return ScriptEngine::Get().GetRuntimePhysicsWorld(); +} + MonoArray* CreateManagedComponentArray(MonoClass* componentClass, const std::vector& components) { if (!componentClass) { return nullptr; @@ -750,6 +798,9 @@ MonoArray* InternalCall_GameObject_GetComponents(uint64_t gameObjectUUID, MonoRe case ManagedComponentKind::Transform: appendNativeComponents(gameObject->GetComponents()); break; + case ManagedComponentKind::Rigidbody: + appendNativeComponents(gameObject->GetComponents()); + break; case ManagedComponentKind::Camera: appendNativeComponents(gameObject->GetComponents()); break; @@ -1533,6 +1584,200 @@ void InternalCall_MeshRenderer_SetRenderLayer(uint64_t gameObjectUUID, int32_t v component->SetRenderLayer(static_cast(std::max(value, 0))); } +int32_t InternalCall_Rigidbody_GetBodyType(uint64_t gameObjectUUID) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + return component + ? static_cast(component->GetBodyType()) + : static_cast(Physics::PhysicsBodyType::Dynamic); +} + +void InternalCall_Rigidbody_SetBodyType(uint64_t gameObjectUUID, int32_t value) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + if (!component) { + return; + } + + component->SetBodyType(ResolveManagedPhysicsBodyType(value)); +} + +float InternalCall_Rigidbody_GetMass(uint64_t gameObjectUUID) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + return component ? component->GetMass() : 1.0f; +} + +void InternalCall_Rigidbody_SetMass(uint64_t gameObjectUUID, float value) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + if (!component) { + return; + } + + component->SetMass(value); +} + +float InternalCall_Rigidbody_GetLinearDamping(uint64_t gameObjectUUID) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + return component ? component->GetLinearDamping() : 0.0f; +} + +void InternalCall_Rigidbody_SetLinearDamping(uint64_t gameObjectUUID, float value) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + if (!component) { + return; + } + + component->SetLinearDamping(value); +} + +float InternalCall_Rigidbody_GetAngularDamping(uint64_t gameObjectUUID) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + return component ? component->GetAngularDamping() : 0.05f; +} + +void InternalCall_Rigidbody_SetAngularDamping(uint64_t gameObjectUUID, float value) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + if (!component) { + return; + } + + component->SetAngularDamping(value); +} + +void InternalCall_Rigidbody_GetLinearVelocity(uint64_t gameObjectUUID, XCEngine::Math::Vector3* outVelocity) { + if (!outVelocity) { + return; + } + + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + *outVelocity = component ? component->GetLinearVelocity() : XCEngine::Math::Vector3::Zero(); +} + +void InternalCall_Rigidbody_SetLinearVelocity(uint64_t gameObjectUUID, XCEngine::Math::Vector3* velocity) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + if (!component || !velocity) { + return; + } + + component->SetLinearVelocity(*velocity); +} + +void InternalCall_Rigidbody_GetAngularVelocity(uint64_t gameObjectUUID, XCEngine::Math::Vector3* outVelocity) { + if (!outVelocity) { + return; + } + + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + *outVelocity = component ? component->GetAngularVelocity() : XCEngine::Math::Vector3::Zero(); +} + +void InternalCall_Rigidbody_SetAngularVelocity(uint64_t gameObjectUUID, XCEngine::Math::Vector3* velocity) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + if (!component || !velocity) { + return; + } + + component->SetAngularVelocity(*velocity); +} + +mono_bool InternalCall_Rigidbody_GetUseGravity(uint64_t gameObjectUUID) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + return (component && component->GetUseGravity()) ? 1 : 0; +} + +void InternalCall_Rigidbody_SetUseGravity(uint64_t gameObjectUUID, mono_bool value) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + if (!component) { + return; + } + + component->SetUseGravity(value != 0); +} + +mono_bool InternalCall_Rigidbody_GetEnableCCD(uint64_t gameObjectUUID) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + return (component && component->GetEnableCCD()) ? 1 : 0; +} + +void InternalCall_Rigidbody_SetEnableCCD(uint64_t gameObjectUUID, mono_bool value) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + if (!component) { + return; + } + + component->SetEnableCCD(value != 0); +} + +void InternalCall_Rigidbody_AddForce(uint64_t gameObjectUUID, XCEngine::Math::Vector3* force, int32_t forceMode) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + if (!component || !force) { + return; + } + + component->AddForce(*force, ResolveManagedForceMode(forceMode)); +} + +void InternalCall_Rigidbody_ClearForces(uint64_t gameObjectUUID) { + Components::RigidbodyComponent* component = FindRigidbodyComponent(gameObjectUUID); + if (!component) { + return; + } + + component->ClearForces(); +} + +mono_bool InternalCall_Physics_Raycast( + XCEngine::Math::Vector3* origin, + XCEngine::Math::Vector3* direction, + float maxDistance, + uint64_t* outHitGameObjectUUID, + XCEngine::Math::Vector3* outHitPoint, + XCEngine::Math::Vector3* outHitNormal, + float* outHitDistance, + int32_t* outHitIsTrigger) { + if (outHitGameObjectUUID) { + *outHitGameObjectUUID = 0; + } + if (outHitPoint) { + *outHitPoint = XCEngine::Math::Vector3::Zero(); + } + if (outHitNormal) { + *outHitNormal = XCEngine::Math::Vector3::Zero(); + } + if (outHitDistance) { + *outHitDistance = 0.0f; + } + if (outHitIsTrigger) { + *outHitIsTrigger = 0; + } + + Physics::PhysicsWorld* physicsWorld = FindRuntimePhysicsWorld(); + if (!physicsWorld || !origin || !direction) { + return 0; + } + + Physics::RaycastHit hit; + if (!physicsWorld->Raycast(*origin, *direction, maxDistance, hit)) { + return 0; + } + + if (outHitGameObjectUUID) { + *outHitGameObjectUUID = hit.gameObject ? hit.gameObject->GetUUID() : 0; + } + if (outHitPoint) { + *outHitPoint = hit.point; + } + if (outHitNormal) { + *outHitNormal = hit.normal; + } + if (outHitDistance) { + *outHitDistance = hit.distance; + } + if (outHitIsTrigger) { + *outHitIsTrigger = hit.isTrigger ? 1 : 0; + } + + return 1; +} + void InternalCall_Rendering_SetRenderPipelineAssetType(MonoReflectionType* assetType) { if (assetType == nullptr) { Rendering::Pipelines::ClearManagedRenderPipelineAssetDescriptor(); @@ -1681,6 +1926,25 @@ void RegisterInternalCalls() { mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_SetReceiveShadows", reinterpret_cast(&InternalCall_MeshRenderer_SetReceiveShadows)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetRenderLayer", reinterpret_cast(&InternalCall_MeshRenderer_GetRenderLayer)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_SetRenderLayer", reinterpret_cast(&InternalCall_MeshRenderer_SetRenderLayer)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_GetBodyType", reinterpret_cast(&InternalCall_Rigidbody_GetBodyType)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_SetBodyType", reinterpret_cast(&InternalCall_Rigidbody_SetBodyType)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_GetMass", reinterpret_cast(&InternalCall_Rigidbody_GetMass)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_SetMass", reinterpret_cast(&InternalCall_Rigidbody_SetMass)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_GetLinearDamping", reinterpret_cast(&InternalCall_Rigidbody_GetLinearDamping)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_SetLinearDamping", reinterpret_cast(&InternalCall_Rigidbody_SetLinearDamping)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_GetAngularDamping", reinterpret_cast(&InternalCall_Rigidbody_GetAngularDamping)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_SetAngularDamping", reinterpret_cast(&InternalCall_Rigidbody_SetAngularDamping)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_GetLinearVelocity", reinterpret_cast(&InternalCall_Rigidbody_GetLinearVelocity)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_SetLinearVelocity", reinterpret_cast(&InternalCall_Rigidbody_SetLinearVelocity)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_GetAngularVelocity", reinterpret_cast(&InternalCall_Rigidbody_GetAngularVelocity)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_SetAngularVelocity", reinterpret_cast(&InternalCall_Rigidbody_SetAngularVelocity)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_GetUseGravity", reinterpret_cast(&InternalCall_Rigidbody_GetUseGravity)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_SetUseGravity", reinterpret_cast(&InternalCall_Rigidbody_SetUseGravity)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_GetEnableCCD", reinterpret_cast(&InternalCall_Rigidbody_GetEnableCCD)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_SetEnableCCD", reinterpret_cast(&InternalCall_Rigidbody_SetEnableCCD)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_AddForce", reinterpret_cast(&InternalCall_Rigidbody_AddForce)); + mono_add_internal_call("XCEngine.InternalCalls::Rigidbody_ClearForces", reinterpret_cast(&InternalCall_Rigidbody_ClearForces)); + mono_add_internal_call("XCEngine.InternalCalls::Physics_Raycast", reinterpret_cast(&InternalCall_Physics_Raycast)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_SetRenderPipelineAssetType", reinterpret_cast(&InternalCall_Rendering_SetRenderPipelineAssetType)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_GetRenderPipelineAssetTypeName", reinterpret_cast(&InternalCall_Rendering_GetRenderPipelineAssetTypeName)); @@ -2459,6 +2723,7 @@ bool MonoScriptRuntime::IsSupportedComponentFieldClass(MonoClass* monoClass) con } return className == "Transform" + || className == "Rigidbody" || className == "Camera" || className == "Light" || className == "MeshFilter" @@ -2853,6 +3118,8 @@ bool MonoScriptRuntime::DestroyManagedObject(MonoObject* managedObject) { const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(monoClass); switch (typeInfo.kind) { + case ManagedComponentKind::Rigidbody: + return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent()); case ManagedComponentKind::Camera: return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent()); case ManagedComponentKind::Light: @@ -3075,6 +3342,8 @@ bool MonoScriptRuntime::TrySetFieldValue( bool hasComponent = false; if (className == "Transform") { hasComponent = targetObject->GetTransform() != nullptr; + } else if (className == "Rigidbody") { + hasComponent = targetObject->GetComponent() != nullptr; } else if (className == "Camera") { hasComponent = targetObject->GetComponent() != nullptr; } else if (className == "Light") { diff --git a/engine/src/Scripting/ScriptEngine.cpp b/engine/src/Scripting/ScriptEngine.cpp index 99595a69..4de21870 100644 --- a/engine/src/Scripting/ScriptEngine.cpp +++ b/engine/src/Scripting/ScriptEngine.cpp @@ -74,6 +74,7 @@ void ScriptEngine::SetRuntimeFixedDeltaTime(float fixedDeltaTime) { void ScriptEngine::OnRuntimeStart(Components::Scene* scene) { const float configuredFixedDeltaTime = m_runtimeFixedDeltaTime; + Physics::PhysicsWorld* configuredPhysicsWorld = m_runtimePhysicsWorld; OnRuntimeStop(); m_runtimeFixedDeltaTime = configuredFixedDeltaTime; @@ -81,6 +82,7 @@ void ScriptEngine::OnRuntimeStart(Components::Scene* scene) { return; } + m_runtimePhysicsWorld = configuredPhysicsWorld; m_runtimeScene = scene; m_runtimeRunning = true; m_runtime->OnRuntimeStart(scene); @@ -118,6 +120,7 @@ void ScriptEngine::OnRuntimeStop() { if (!m_runtimeRunning) { m_runtimeScene = nullptr; + m_runtimePhysicsWorld = nullptr; m_scriptStates.clear(); m_scriptOrder.clear(); m_runtimeFixedDeltaTime = DefaultFixedDeltaTime; @@ -137,6 +140,7 @@ void ScriptEngine::OnRuntimeStop() { m_scriptOrder.clear(); m_runtimeRunning = false; m_runtimeScene = nullptr; + m_runtimePhysicsWorld = nullptr; m_runtimeFixedDeltaTime = DefaultFixedDeltaTime; m_runtime->OnRuntimeStop(stoppedScene); } @@ -144,6 +148,7 @@ void ScriptEngine::OnRuntimeStop() { void ScriptEngine::OnRuntimeSceneReplaced(Components::Scene* scene) { if (!m_runtimeRunning) { m_runtimeScene = nullptr; + m_runtimePhysicsWorld = nullptr; m_runtimeSceneCreatedSubscription = 0; m_scriptStates.clear(); m_scriptOrder.clear(); diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index aca9b30c..7aef231f 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -103,6 +103,7 @@ set(XCENGINE_SCRIPT_CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Camera.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Component.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Debug.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ForceMode.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/GraphicsSettings.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/GameObject.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Input.cs @@ -113,8 +114,12 @@ set(XCENGINE_SCRIPT_CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/MeshRenderer.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/MonoBehaviour.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Object.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Physics.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/PhysicsBodyType.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Quaternion.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/RaycastHit.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/RenderPipelineAsset.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rigidbody.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/SerializeField.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Space.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Time.cs @@ -139,6 +144,7 @@ set(XCENGINE_GAME_SCRIPT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/MeshComponentProbe.cs ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/MeshRendererEdgeCaseProbe.cs ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/ObjectApiProbe.cs + ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/PhysicsApiProbe.cs ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/PhysicsEventProbe.cs ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/RenderPipelineApiProbe.cs ${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/SerializeFieldProbe.cs diff --git a/managed/GameScripts/PhysicsApiProbe.cs b/managed/GameScripts/PhysicsApiProbe.cs new file mode 100644 index 00000000..6dda24eb --- /dev/null +++ b/managed/GameScripts/PhysicsApiProbe.cs @@ -0,0 +1,72 @@ +using XCEngine; + +namespace Gameplay +{ + public sealed class PhysicsApiProbe : MonoBehaviour + { + public bool InitialHasRigidbody; + public bool AddedRigidbody; + public bool HasRigidbodyAfterAdd; + public bool RigidbodyLookupSucceeded; + public int ObservedBodyType; + public float ObservedMass; + public float ObservedLinearDamping; + public float ObservedAngularDamping; + public bool ObservedUseGravity; + public bool ObservedEnableCCD; + public Vector3 ObservedLinearVelocity; + public Vector3 ObservedAngularVelocity; + public bool RaycastHitDetected; + public string RaycastHitName = string.Empty; + public float RaycastHitDistance; + public bool RaycastHitWasTrigger; + public Vector3 RaycastHitPoint; + public Vector3 RaycastHitNormal; + + public void Start() + { + InitialHasRigidbody = HasComponent(); + AddedRigidbody = AddComponent() != null; + HasRigidbodyAfterAdd = HasComponent(); + RigidbodyLookupSucceeded = TryGetComponent(out Rigidbody rigidbody); + + if (rigidbody != null) + { + rigidbody.bodyType = PhysicsBodyType.Dynamic; + rigidbody.mass = 2.5f; + rigidbody.linearDamping = 0.25f; + rigidbody.angularDamping = 0.5f; + rigidbody.useGravity = false; + rigidbody.enableCCD = true; + rigidbody.linearVelocity = new Vector3(0.0f, 0.0f, 0.0f); + rigidbody.angularVelocity = new Vector3(0.0f, 1.0f, 0.0f); + + ObservedBodyType = (int)rigidbody.bodyType; + ObservedMass = rigidbody.mass; + ObservedLinearDamping = rigidbody.linearDamping; + ObservedAngularDamping = rigidbody.angularDamping; + ObservedUseGravity = rigidbody.useGravity; + ObservedEnableCCD = rigidbody.enableCCD; + ObservedLinearVelocity = rigidbody.linearVelocity; + ObservedAngularVelocity = rigidbody.angularVelocity; + + rigidbody.AddForce(new Vector3(10.0f, 0.0f, 0.0f), ForceMode.Impulse); + } + + RaycastHitDetected = Physics.Raycast( + new Vector3(0.0f, 3.0f, 0.0f), + new Vector3(0.0f, 0.0f, -1.0f), + out RaycastHit hit, + 10.0f); + + if (RaycastHitDetected) + { + RaycastHitName = hit.gameObject != null ? hit.gameObject.name : string.Empty; + RaycastHitDistance = hit.distance; + RaycastHitWasTrigger = hit.isTrigger; + RaycastHitPoint = hit.point; + RaycastHitNormal = hit.normal; + } + } + } +} diff --git a/managed/XCEngine.ScriptCore/ForceMode.cs b/managed/XCEngine.ScriptCore/ForceMode.cs new file mode 100644 index 00000000..369570ff --- /dev/null +++ b/managed/XCEngine.ScriptCore/ForceMode.cs @@ -0,0 +1,10 @@ +namespace XCEngine +{ + public enum ForceMode + { + Force = 0, + Acceleration = 1, + Impulse = 2, + VelocityChange = 3 + } +} diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index bb932b6c..924b5c2d 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -311,6 +311,71 @@ namespace XCEngine [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void MeshRenderer_SetRenderLayer(ulong gameObjectUUID, int value); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern int Rigidbody_GetBodyType(ulong gameObjectUUID); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_SetBodyType(ulong gameObjectUUID, int value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern float Rigidbody_GetMass(ulong gameObjectUUID); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_SetMass(ulong gameObjectUUID, float value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern float Rigidbody_GetLinearDamping(ulong gameObjectUUID); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_SetLinearDamping(ulong gameObjectUUID, float value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern float Rigidbody_GetAngularDamping(ulong gameObjectUUID); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_SetAngularDamping(ulong gameObjectUUID, float value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_GetLinearVelocity(ulong gameObjectUUID, out Vector3 velocity); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_SetLinearVelocity(ulong gameObjectUUID, ref Vector3 velocity); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_GetAngularVelocity(ulong gameObjectUUID, out Vector3 velocity); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_SetAngularVelocity(ulong gameObjectUUID, ref Vector3 velocity); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool Rigidbody_GetUseGravity(ulong gameObjectUUID); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_SetUseGravity(ulong gameObjectUUID, bool value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool Rigidbody_GetEnableCCD(ulong gameObjectUUID); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_SetEnableCCD(ulong gameObjectUUID, bool value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_AddForce(ulong gameObjectUUID, ref Vector3 force, int forceMode); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Rigidbody_ClearForces(ulong gameObjectUUID); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool Physics_Raycast( + ref Vector3 origin, + ref Vector3 direction, + float maxDistance, + out ulong hitGameObjectUUID, + out Vector3 hitPoint, + out Vector3 hitNormal, + out float hitDistance, + out int hitIsTrigger); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void Rendering_SetRenderPipelineAssetType(Type assetType); diff --git a/managed/XCEngine.ScriptCore/Physics.cs b/managed/XCEngine.ScriptCore/Physics.cs new file mode 100644 index 00000000..cfa1cbbf --- /dev/null +++ b/managed/XCEngine.ScriptCore/Physics.cs @@ -0,0 +1,60 @@ +namespace XCEngine +{ + public static class Physics + { + public static bool Raycast(Vector3 origin, Vector3 direction) + { + RaycastHit hit; + return Raycast(origin, direction, out hit, float.MaxValue); + } + + public static bool Raycast(Vector3 origin, Vector3 direction, float maxDistance) + { + RaycastHit hit; + return Raycast(origin, direction, out hit, maxDistance); + } + + public static bool Raycast(Vector3 origin, Vector3 direction, out RaycastHit hit) + { + return Raycast(origin, direction, out hit, float.MaxValue); + } + + public static bool Raycast(Vector3 origin, Vector3 direction, out RaycastHit hit, float maxDistance) + { + hit = default(RaycastHit); + + if (float.IsNaN(maxDistance) || maxDistance <= 0.0f) + { + return false; + } + + if (float.IsPositiveInfinity(maxDistance)) + { + maxDistance = float.MaxValue; + } + + bool hasHit = InternalCalls.Physics_Raycast( + ref origin, + ref direction, + maxDistance, + out ulong hitGameObjectUUID, + out Vector3 hitPoint, + out Vector3 hitNormal, + out float hitDistance, + out int hitIsTrigger); + + if (!hasHit) + { + return false; + } + + hit = new RaycastHit( + hitGameObjectUUID, + hitPoint, + hitNormal, + hitDistance, + hitIsTrigger != 0); + return true; + } + } +} diff --git a/managed/XCEngine.ScriptCore/PhysicsBodyType.cs b/managed/XCEngine.ScriptCore/PhysicsBodyType.cs new file mode 100644 index 00000000..4172cd22 --- /dev/null +++ b/managed/XCEngine.ScriptCore/PhysicsBodyType.cs @@ -0,0 +1,9 @@ +namespace XCEngine +{ + public enum PhysicsBodyType + { + Static = 0, + Dynamic = 1, + Kinematic = 2 + } +} diff --git a/managed/XCEngine.ScriptCore/RaycastHit.cs b/managed/XCEngine.ScriptCore/RaycastHit.cs new file mode 100644 index 00000000..5c29fbde --- /dev/null +++ b/managed/XCEngine.ScriptCore/RaycastHit.cs @@ -0,0 +1,51 @@ +namespace XCEngine +{ + public struct RaycastHit + { + private ulong m_gameObjectUUID; + private Vector3 m_point; + private Vector3 m_normal; + private float m_distance; + private bool m_isTrigger; + + internal RaycastHit( + ulong gameObjectUUID, + Vector3 point, + Vector3 normal, + float distance, + bool isTrigger) + { + m_gameObjectUUID = gameObjectUUID; + m_point = point; + m_normal = normal; + m_distance = distance; + m_isTrigger = isTrigger; + } + + public GameObject GameObject => m_gameObjectUUID != 0 ? new GameObject(m_gameObjectUUID) : null; + public GameObject gameObject => GameObject; + + public Transform Transform + { + get + { + GameObject hitObject = GameObject; + return hitObject != null ? hitObject.Transform : null; + } + } + + public Transform transform => Transform; + + public Vector3 Point => m_point; + public Vector3 point => Point; + + public Vector3 Normal => m_normal; + public Vector3 normal => Normal; + + public float Distance => m_distance; + public float distance => Distance; + + public bool IsTrigger => m_isTrigger; + public bool isTrigger => IsTrigger; + } +} diff --git a/managed/XCEngine.ScriptCore/Rigidbody.cs b/managed/XCEngine.ScriptCore/Rigidbody.cs new file mode 100644 index 00000000..c00f99aa --- /dev/null +++ b/managed/XCEngine.ScriptCore/Rigidbody.cs @@ -0,0 +1,129 @@ +namespace XCEngine +{ + public sealed class Rigidbody : Component + { + internal Rigidbody(ulong gameObjectUUID) + : base(gameObjectUUID) + { + } + + public PhysicsBodyType BodyType + { + get => (PhysicsBodyType)InternalCalls.Rigidbody_GetBodyType(GameObjectUUID); + set => InternalCalls.Rigidbody_SetBodyType(GameObjectUUID, (int)value); + } + + public PhysicsBodyType bodyType + { + get => BodyType; + set => BodyType = value; + } + + public float Mass + { + get => InternalCalls.Rigidbody_GetMass(GameObjectUUID); + set => InternalCalls.Rigidbody_SetMass(GameObjectUUID, value); + } + + public float mass + { + get => Mass; + set => Mass = value; + } + + public float LinearDamping + { + get => InternalCalls.Rigidbody_GetLinearDamping(GameObjectUUID); + set => InternalCalls.Rigidbody_SetLinearDamping(GameObjectUUID, value); + } + + public float linearDamping + { + get => LinearDamping; + set => LinearDamping = value; + } + + public float AngularDamping + { + get => InternalCalls.Rigidbody_GetAngularDamping(GameObjectUUID); + set => InternalCalls.Rigidbody_SetAngularDamping(GameObjectUUID, value); + } + + public float angularDamping + { + get => AngularDamping; + set => AngularDamping = value; + } + + public Vector3 LinearVelocity + { + get + { + InternalCalls.Rigidbody_GetLinearVelocity(GameObjectUUID, out Vector3 velocity); + return velocity; + } + set => InternalCalls.Rigidbody_SetLinearVelocity(GameObjectUUID, ref value); + } + + public Vector3 linearVelocity + { + get => LinearVelocity; + set => LinearVelocity = value; + } + + public Vector3 AngularVelocity + { + get + { + InternalCalls.Rigidbody_GetAngularVelocity(GameObjectUUID, out Vector3 velocity); + return velocity; + } + set => InternalCalls.Rigidbody_SetAngularVelocity(GameObjectUUID, ref value); + } + + public Vector3 angularVelocity + { + get => AngularVelocity; + set => AngularVelocity = value; + } + + public bool UseGravity + { + get => InternalCalls.Rigidbody_GetUseGravity(GameObjectUUID); + set => InternalCalls.Rigidbody_SetUseGravity(GameObjectUUID, value); + } + + public bool useGravity + { + get => UseGravity; + set => UseGravity = value; + } + + public bool EnableCCD + { + get => InternalCalls.Rigidbody_GetEnableCCD(GameObjectUUID); + set => InternalCalls.Rigidbody_SetEnableCCD(GameObjectUUID, value); + } + + public bool enableCCD + { + get => EnableCCD; + set => EnableCCD = value; + } + + public void AddForce(Vector3 force) + { + AddForce(force, ForceMode.Force); + } + + public void AddForce(Vector3 force, ForceMode mode) + { + InternalCalls.Rigidbody_AddForce(GameObjectUUID, ref force, (int)mode); + } + + public void ClearForces() + { + InternalCalls.Rigidbody_ClearForces(GameObjectUUID); + } + } +} diff --git a/tests/scripting/test_mono_script_runtime.cpp b/tests/scripting/test_mono_script_runtime.cpp index 35b2aae5..7e3c5dd3 100644 --- a/tests/scripting/test_mono_script_runtime.cpp +++ b/tests/scripting/test_mono_script_runtime.cpp @@ -592,6 +592,105 @@ TEST_F(MonoScriptRuntimeTest, SceneRuntimeDispatchesManagedCollisionMessagesWith sceneRuntime.Stop(); } +TEST_F(MonoScriptRuntimeTest, ManagedRigidbodyWrapperAndPhysicsRaycastOperateAgainstRuntimePhysicsWorld) { + if (!XCEngine::Physics::PhysicsWorld::IsPhysXAvailable()) { + GTEST_SKIP() << "PhysX is not available in this configuration."; + } + + Scene* runtimeScene = CreateScene("MonoRuntimeScene"); + GameObject* host = runtimeScene->CreateGameObject("Host"); + host->AddComponent(); + ScriptComponent* component = AddScript(host, "Gameplay", "PhysicsApiProbe"); + + GameObject* rayTarget = runtimeScene->CreateGameObject("RayTarget"); + rayTarget->GetTransform()->SetPosition(XCEngine::Math::Vector3(0.0f, 3.0f, -4.0f)); + BoxColliderComponent* targetCollider = rayTarget->AddComponent(); + targetCollider->SetSize(XCEngine::Math::Vector3(2.0f, 2.0f, 0.5f)); + + SceneRuntime sceneRuntime; + sceneRuntime.Start(runtimeScene); + sceneRuntime.Update(0.016f); + sceneRuntime.FixedUpdate(0.02f); + + bool initialHasRigidbody = true; + bool addedRigidbody = false; + bool hasRigidbodyAfterAdd = false; + bool rigidbodyLookupSucceeded = false; + int32_t observedBodyType = -1; + float observedMass = 0.0f; + float observedLinearDamping = 0.0f; + float observedAngularDamping = 0.0f; + bool observedUseGravity = true; + bool observedEnableCCD = false; + XCEngine::Math::Vector3 observedLinearVelocity; + XCEngine::Math::Vector3 observedAngularVelocity; + bool raycastHitDetected = false; + std::string raycastHitName; + float raycastHitDistance = 0.0f; + bool raycastHitWasTrigger = true; + XCEngine::Math::Vector3 raycastHitPoint; + XCEngine::Math::Vector3 raycastHitNormal; + + EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasRigidbody", initialHasRigidbody)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedRigidbody", addedRigidbody)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasRigidbodyAfterAdd", hasRigidbodyAfterAdd)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "RigidbodyLookupSucceeded", rigidbodyLookupSucceeded)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedBodyType", observedBodyType)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMass", observedMass)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLinearDamping", observedLinearDamping)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAngularDamping", observedAngularDamping)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUseGravity", observedUseGravity)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEnableCCD", observedEnableCCD)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLinearVelocity", observedLinearVelocity)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAngularVelocity", observedAngularVelocity)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitDetected", raycastHitDetected)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitName", raycastHitName)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitDistance", raycastHitDistance)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitWasTrigger", raycastHitWasTrigger)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitPoint", raycastHitPoint)); + EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitNormal", raycastHitNormal)); + + EXPECT_FALSE(initialHasRigidbody); + EXPECT_TRUE(addedRigidbody); + EXPECT_TRUE(hasRigidbodyAfterAdd); + EXPECT_TRUE(rigidbodyLookupSucceeded); + EXPECT_EQ(observedBodyType, static_cast(XCEngine::Physics::PhysicsBodyType::Dynamic)); + EXPECT_FLOAT_EQ(observedMass, 2.5f); + EXPECT_FLOAT_EQ(observedLinearDamping, 0.25f); + EXPECT_FLOAT_EQ(observedAngularDamping, 0.5f); + EXPECT_FALSE(observedUseGravity); + EXPECT_TRUE(observedEnableCCD); + ExpectVector3Near(observedLinearVelocity, XCEngine::Math::Vector3(0.0f, 0.0f, 0.0f)); + ExpectVector3Near(observedAngularVelocity, XCEngine::Math::Vector3(0.0f, 1.0f, 0.0f)); + + EXPECT_TRUE(raycastHitDetected); + EXPECT_EQ(raycastHitName, "RayTarget"); + EXPECT_NEAR(raycastHitDistance, 3.75f, 0.05f); + EXPECT_FALSE(raycastHitWasTrigger); + ExpectVector3Near(raycastHitPoint, XCEngine::Math::Vector3(0.0f, 3.0f, -3.75f), 0.05f); + ExpectVector3Near(raycastHitNormal, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f), 0.01f); + + EXPECT_NE(sceneRuntime.GetPhysicsWorld(), nullptr); + EXPECT_EQ(sceneRuntime.GetPhysicsWorld()->GetTrackedRigidbodyCount(), 1u); + + RigidbodyComponent* rigidbody = host->GetComponent(); + ASSERT_NE(rigidbody, nullptr); + EXPECT_EQ(host->GetComponents().size(), 1u); + EXPECT_EQ(rigidbody->GetBodyType(), XCEngine::Physics::PhysicsBodyType::Dynamic); + EXPECT_FLOAT_EQ(rigidbody->GetMass(), 2.5f); + EXPECT_FLOAT_EQ(rigidbody->GetLinearDamping(), 0.25f); + EXPECT_FLOAT_EQ(rigidbody->GetAngularDamping(), 0.5f); + EXPECT_FALSE(rigidbody->GetUseGravity()); + EXPECT_TRUE(rigidbody->GetEnableCCD()); + EXPECT_GT(rigidbody->GetLinearVelocity().x, 0.0f); + + const XCEngine::Math::Vector3 hostPosition = host->GetTransform()->GetPosition(); + EXPECT_GT(hostPosition.x, 0.01f); + EXPECT_NEAR(hostPosition.y, 0.0f, 0.01f); + + sceneRuntime.Stop(); +} + TEST_F(MonoScriptRuntimeTest, ManagedInputApiReadsCurrentNativeInputManagerState) { XCEngine::Input::InputManager& inputManager = XCEngine::Input::InputManager::Get(); inputManager.Initialize(nullptr);