From 60b45828c2542b5c7a3d2c4e214a40e06ce648dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Mon, 15 Jan 2024 01:22:52 +0100 Subject: [PATCH] ScriptGlue: Added Camera component and throwing mono exceptions from beef --- GlitchyEngine/src/Scripting/ScriptGlue.bf | 219 +++++++++++++++++++++- GlitchyEngineHelper/src/Mono/Mono.bf | 13 ++ ScriptCore/Core/Camera.cs | 162 ++++++++++++++++ ScriptCore/ScriptGlue.cs | 51 +++++ 4 files changed, 439 insertions(+), 6 deletions(-) create mode 100644 ScriptCore/Core/Camera.cs diff --git a/GlitchyEngine/src/Scripting/ScriptGlue.bf b/GlitchyEngine/src/Scripting/ScriptGlue.bf index aca39a8..1227e86 100644 --- a/GlitchyEngine/src/Scripting/ScriptGlue.bf +++ b/GlitchyEngine/src/Scripting/ScriptGlue.bf @@ -69,6 +69,7 @@ static class ScriptGlue RegisterComponent("GlitchyEngine.Core.Transform"); RegisterComponent("GlitchyEngine.Physics.Rigidbody2D"); + RegisterComponent("GlitchyEngine.Core.Camera"); } [RegisterMethod] @@ -106,7 +107,66 @@ static class ScriptGlue Log.EngineLogger.AssertDebug(managedType != null, scope $"No C# component with name \"{className}\" found for Beef type \"{typeof(T)}\""); } } + + /// Gets the component of the specified type that is attached to the given entity. Or null, if the entity doesn't exist or doesn't have the specified component. + static T* GetComponentSafe(UUID entityId) where T: struct, new + { + Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); + if (foundEntity case .Ok(let entity)) + { + if (entity.TryGetComponent(let component)) + { + return component; + } + else + { + ThrowArgumentException(null, scope $"The entity has no component of type {typeof(T)} or it was deleted."); + } + } + else + { + ThrowArgumentException(null, "The entity doesn't exist or was deleted."); + } + } + + /// Gets the component of the specified type that is attached to the given entity. Or null, if the entity doesn't exist or doesn't have the specified component. + static bool TryGetComponentSafe(UUID entityId, out T* component) where T: struct, new + { + Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); + + if (foundEntity case .Ok(let entity)) + { + if (entity.TryGetComponent(out component)) + { + return true; + } + else + { + Log.ClientLogger.Error($"Entity {entityId} has no {nameof(T)}."); + } + } + else + { + Log.ClientLogger.Error($"No entity with ID {entityId} found."); + } + + component = null; + return false; + } + +#region Exception Helpers + + /// Throws an argument exception. + [NoReturn] + static void ThrowArgumentException(char8* argument, char8* message) + { + MonoException* exception = Mono.mono_get_exception_argument(argument, message); + Mono.mono_raise_exception(exception); + } + +#endregion + #region Log [RegisterCall("ScriptGlue::Log_LogMessage")] @@ -229,9 +289,19 @@ static class ScriptGlue { MonoType* type = Mono.mono_reflection_type_get_type(componentType); - Entity entity = ScriptEngine.Context.GetEntityByID(entityId); - if (s_HasComponentMethods.TryGetValue(type, let hasMethod)) - return hasMethod(entity); + Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); + + if (foundEntity case .Ok(let entity)) + { + if (s_HasComponentMethods.TryGetValue(type, let hasMethod)) + return hasMethod(entity); + } + else + { + Log.ClientLogger.Warning($"No entity found with the given id \"{entityId}\"."); + + return false; + } Log.EngineLogger.AssertDebug(false, "No managed component with the given type registered."); @@ -319,11 +389,16 @@ static class ScriptGlue [RegisterCall("ScriptGlue::Entity_GetName")] static MonoString* Entity_GetName(UUID entityId) { - Entity entity = ScriptEngine.Context.GetEntityByID(entityId); + Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); - MonoString* name = Mono.mono_string_new_len(ScriptEngine.[Friend]s_AppDomain, entity.Name.Ptr, (.)entity.Name.Length); + if (foundEntity case .Ok(let entity)) + { + MonoString* name = Mono.mono_string_new_len(ScriptEngine.[Friend]s_AppDomain, entity.Name.Ptr, (.)entity.Name.Length); - return name; + return name; + } + + return null; } [RegisterCall("ScriptGlue::Entity_SetName")] @@ -472,6 +547,138 @@ static class ScriptGlue #endregion Rigidbody2D +#region Camera + + [RegisterCall("ScriptGlue::Camera_GetProjectionType")] + static void Camera_GetProjectionType(UUID entityId, out SceneCamera.ProjectionType projectionType) + { + CameraComponent* camera = GetComponentSafe(entityId); + projectionType = camera.Camera.ProjectionType; + } + + [RegisterCall("ScriptGlue::Camera_SetProjectionType")] + static void Camera_SetProjectionType(UUID entityId, in SceneCamera.ProjectionType projectionType) + { + CameraComponent* camera = GetComponentSafe(entityId); + camera.Camera.ProjectionType = projectionType; + } + + // Native Implementierungen in Beef für die Kamera-Properties + + [RegisterCall("ScriptGlue::Camera_GetPerspectiveFovY")] + static void Camera_GetPerspectiveFovY(UUID entityId, out float fovY) + { + CameraComponent* camera = GetComponentSafe(entityId); + fovY = camera.Camera.PerspectiveFovY; + } + + [RegisterCall("ScriptGlue::Camera_SetPerspectiveFovY")] + static void Camera_SetPerspectiveFovY(UUID entityId, float fovY) + { + CameraComponent* camera = GetComponentSafe(entityId); + camera.Camera.PerspectiveFovY = fovY; + } + + [RegisterCall("ScriptGlue::Camera_GetPerspectiveNearPlane")] + static void Camera_GetPerspectiveNearPlane(UUID entityId, out float nearPlane) + { + CameraComponent* camera = GetComponentSafe(entityId); + nearPlane = camera.Camera.PerspectiveNearPlane; + } + + [RegisterCall("ScriptGlue::Camera_SetPerspectiveNearPlane")] + static void Camera_SetPerspectiveNearPlane(UUID entityId, float nearPlane) + { + CameraComponent* camera = GetComponentSafe(entityId); + camera.Camera.PerspectiveNearPlane = nearPlane; + } + + [RegisterCall("ScriptGlue::Camera_GetPerspectiveFarPlane")] + static void Camera_GetPerspectiveFarPlane(UUID entityId, out float farPlane) + { + CameraComponent* camera = GetComponentSafe(entityId); + farPlane = camera.Camera.PerspectiveFarPlane; + } + + [RegisterCall("ScriptGlue::Camera_SetPerspectiveFarPlane")] + static void Camera_SetPerspectiveFarPlane(UUID entityId, float farPlane) + { + CameraComponent* camera = GetComponentSafe(entityId); + camera.Camera.PerspectiveFarPlane = farPlane; + } + + [RegisterCall("ScriptGlue::Camera_GetOrthographicHeight")] + static void Camera_GetOrthographicHeight(UUID entityId, out float height) + { + CameraComponent* camera = GetComponentSafe(entityId); + height = camera.Camera.OrthographicHeight; + } + + [RegisterCall("ScriptGlue::Camera_SetOrthographicHeight")] + static void Camera_SetOrthographicHeight(UUID entityId, float height) + { + CameraComponent* camera = GetComponentSafe(entityId); + camera.Camera.OrthographicHeight = height; + } + + [RegisterCall("ScriptGlue::Camera_SetOrthographicNearPlane")] + static void Camera_SetOrthographicNearPlane(UUID entityId, float nearPlane) + { + CameraComponent* camera = GetComponentSafe(entityId); + camera.Camera.OrthographicNearPlane = nearPlane; + } + + [RegisterCall("ScriptGlue::Camera_GetOrthographicNearPlane")] + static void Camera_GetOrthographicNearPlane(UUID entityId, out float nearPlane) + { + CameraComponent* camera = GetComponentSafe(entityId); + nearPlane = camera.Camera.OrthographicNearPlane; + } + + [RegisterCall("ScriptGlue::Camera_SetOrthographicFarPlane")] + static void Camera_SetOrthographicFarPlane(UUID entityId, float farPlane) + { + CameraComponent* camera = GetComponentSafe(entityId); + camera.Camera.OrthographicFarPlane = farPlane; + } + + [RegisterCall("ScriptGlue::Camera_GetOrthographicFarPlane")] + static void Camera_GetOrthographicFarPlane(UUID entityId, out float farPlane) + { + CameraComponent* camera = GetComponentSafe(entityId); + farPlane = camera.Camera.OrthographicFarPlane; + } + + [RegisterCall("ScriptGlue::Camera_SetAspectRatio")] + static void Camera_SetAspectRatio(UUID entityId, float aspectRatio) + { + CameraComponent* camera = GetComponentSafe(entityId); + camera.Camera.AspectRatio = aspectRatio; + } + + [RegisterCall("ScriptGlue::Camera_GetAspectRatio")] + static void Camera_GetAspectRatio(UUID entityId, out float aspectRatio) + { + CameraComponent* camera = GetComponentSafe(entityId); + aspectRatio = camera.Camera.AspectRatio; + } + + [RegisterCall("ScriptGlue::Camera_SetFixedAspectRatio")] + static void Camera_SetFixedAspectRatio(UUID entityId, bool fixedAspectRatio) + { + CameraComponent* camera = GetComponentSafe(entityId); + camera.Camera.FixedAspectRatio = fixedAspectRatio; + } + + [RegisterCall("ScriptGlue::Camera_GetFixedAspectRatio")] + static void Camera_GetFixedAspectRatio(UUID entityId, out bool fixedAspectRatio) + { + CameraComponent* camera = GetComponentSafe(entityId); + fixedAspectRatio = camera.Camera.FixedAspectRatio; + } + +#endregion + #region Physics2D // TODO: We need a wrapper class! diff --git a/GlitchyEngineHelper/src/Mono/Mono.bf b/GlitchyEngineHelper/src/Mono/Mono.bf index b8a9f8c..48cf8c4 100644 --- a/GlitchyEngineHelper/src/Mono/Mono.bf +++ b/GlitchyEngineHelper/src/Mono/Mono.bf @@ -279,8 +279,21 @@ static class Mono #endregion +#region Exception + + [LinkName(.C), NoReturn] + public static extern void mono_raise_exception(MonoException *ex); + [LinkName(.C)] public static extern char8* mono_exception_get_managed_backtrace(MonoException* exc); + + [LinkName(.C)] + public static extern MonoException* mono_exception_from_name_msg(MonoImage *image, char8* name_space, char8* name, char8* msg); + + [LinkName(.C)] + public static extern MonoException* mono_get_exception_argument(char8* arg, char8* msg); + +#endregion [LinkName(.C)] public static extern MonoClass* mono_object_get_class(MonoObject* obj); diff --git a/ScriptCore/Core/Camera.cs b/ScriptCore/Core/Camera.cs new file mode 100644 index 0000000..ba2b631 --- /dev/null +++ b/ScriptCore/Core/Camera.cs @@ -0,0 +1,162 @@ +using System; + +namespace GlitchyEngine.Core; + +/// +/// The type of projection used by a camera. +/// +public enum ProjectionType : byte +{ + /// + /// An orthographic projection. + /// + Orthographic = 0, + /// + /// A perspective projection. + /// + Perspective = 1, + /// + /// A perspective projection where the far plane is infinitely far away from the camera. + /// + InfinitePerspective = 2 +} + +/// +/// A component representing a camera that is attached to the entity. +/// +public class Camera : Component +{ + /// + /// The projection type of the . + /// + public ProjectionType ProjectionType + { + get + { + ScriptGlue.Camera_GetProjectionType(_uuid, out ProjectionType projectionType); + + return projectionType; + } + set => ScriptGlue.Camera_SetProjectionType(_uuid, value); + } + + /// + /// Gets or sets the Y field of view in perspective mode. + /// + public float PerspectiveFovY + { + get + { + ScriptGlue.Camera_GetPerspectiveFovY(_uuid, out float fovY); + return fovY; + } + set => ScriptGlue.Camera_SetPerspectiveFovY(_uuid, value); + } + + /// + /// Gets or sets the X field of view in perspective mode. + /// + public float PerspectiveFovX + { + get => PerspectiveFovY * AspectRatio; + set => PerspectiveFovY = value / AspectRatio; + } + + /// + /// Gets or sets the near plane distance in perspective mode. + /// + public float PerspectiveNearPlane + { + get + { + ScriptGlue.Camera_GetPerspectiveNearPlane(_uuid, out float nearPlane); + return nearPlane; + } + set => ScriptGlue.Camera_SetPerspectiveNearPlane(_uuid, value); + } + + /// + /// Gets or sets the far plane distance in perspective mode. + /// + public float PerspectiveFarPlane + { + get + { + ScriptGlue.Camera_GetPerspectiveFarPlane(_uuid, out float farPlane); + return farPlane; + } + set => ScriptGlue.Camera_SetPerspectiveFarPlane(_uuid, value); + } + + /// + /// Gets or sets the orthographic height. + /// + public float OrthographicHeight + { + get + { + ScriptGlue.Camera_GetOrthographicHeight(_uuid, out float height); + return height; + } + set => ScriptGlue.Camera_SetOrthographicHeight(_uuid, value); + } + + /// + /// Gets or sets the near plane distance in orthographic mode. + /// + public float OrthographicNearPlane + { + get + { + ScriptGlue.Camera_GetOrthographicNearPlane(_uuid, out float nearPlane); + return nearPlane; + } + set => ScriptGlue.Camera_SetOrthographicNearPlane(_uuid, value); + } + + /// + /// Gets or sets the far plane distance in orthographic mode. + /// + public float OrthographicFarPlane + { + get + { + ScriptGlue.Camera_GetOrthographicFarPlane(_uuid, out float farPlane); + return farPlane; + } + set => ScriptGlue.Camera_SetOrthographicFarPlane(_uuid, value); + } + + /// + /// Gets or sets the aspect ratio. + /// + /// The setter throws an , when is zero. + public float AspectRatio + { + get + { + ScriptGlue.Camera_GetAspectRatio(_uuid, out float aspectRatio); + return aspectRatio; + } + set + { + if (value == 0) + throw new ArgumentException($"{AspectRatio} must not be zero."); + + ScriptGlue.Camera_SetAspectRatio(_uuid, value); + } + } + + /// + /// Gets or sets a value indicating whether the aspect ratio is fixed. + /// + public bool FixedAspectRatio + { + get + { + ScriptGlue.Camera_GetFixedAspectRatio(_uuid, out bool fixedAspectRatio); + return fixedAspectRatio; + } + set => ScriptGlue.Camera_SetFixedAspectRatio(_uuid, value); + } +} diff --git a/ScriptCore/ScriptGlue.cs b/ScriptCore/ScriptGlue.cs index e0b9f83..edbf095 100644 --- a/ScriptCore/ScriptGlue.cs +++ b/ScriptCore/ScriptGlue.cs @@ -109,6 +109,57 @@ internal static class ScriptGlue #endregion RigidBody2D +#region Camera + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_GetProjectionType(UUID entityId, out ProjectionType projectionType); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_SetProjectionType(UUID entityId, in ProjectionType projectionType); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_GetPerspectiveFovY(UUID entityId, out float fovY); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_SetPerspectiveFovY(UUID entityId, float fovY); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_GetPerspectiveNearPlane(UUID entityId, out float nearPlane); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_SetPerspectiveNearPlane(UUID entityId, float nearPlane); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_GetPerspectiveFarPlane(UUID entityId, out float farPlane); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_SetPerspectiveFarPlane(UUID entityId, float farPlane); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_GetOrthographicHeight(UUID entityId, out float height); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_SetOrthographicHeight(UUID entityId, float height); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_GetOrthographicNearPlane(UUID entityId, out float nearPlane); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_SetOrthographicNearPlane(UUID entityId, float nearPlane); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_GetOrthographicFarPlane(UUID entityId, out float farPlane); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_SetOrthographicFarPlane(UUID entityId, float farPlane); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_GetAspectRatio(UUID entityId, out float aspectRatio); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_SetAspectRatio(UUID entityId, float aspectRatio); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_GetFixedAspectRatio(UUID entityId, out bool fixedAspectRatio); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Camera_SetFixedAspectRatio(UUID entityId, bool fixedAspectRatio); + + +#endregion + #region Physics2D [MethodImpl(MethodImplOptions.InternalCall)]