using Mono; using System; using GlitchLog; using System.Reflection; using GlitchyEngine.Events; using GlitchyEngine.Math; using GlitchyEngine.World; using GlitchyEngine.Core; using System.Collections; using Box2D; using GlitchyEngine.Scripting; using GlitchyEngine.Serialization; using GlitchyEngine.Editor; using GlitchyEngine.World.Components; using GlitchyEngine.Content; using GlitchyEngine.Renderer; using static GlitchyEngine.Renderer.Text.FontRenderer; namespace GlitchyEngine.Scripting; using internal GlitchyEngine.Scripting; using internal GlitchyEngine.Content; class MessageOrigin { private String _fileName ~ delete:append _; private int _lineNumber; public StringView FileName => _fileName; public int LineNumber => _lineNumber; [AllowAppend] public this(StringView fileName, int lineNumber) { String file = append String(fileName); _fileName = file; _lineNumber = lineNumber; } } static class ScriptGlue { private static Dictionary s_AddComponentMethods = new .() ~ delete _; private static Dictionary s_HasComponentMethods = new .() ~ delete _; private static Dictionary s_RemoveComponentMethods = new .() ~ delete _; struct RegisterCallAttribute : Attribute { public String MethodName; public this(String methodName) { MethodName = methodName; } } /* Adding this attribute to a method will log method entry and returned Result errors */ [AttributeUsage(.Method)] struct RegisterMethodAttribute : Attribute, IOnMethodInit { [Comptime] public void OnMethodInit(MethodInfo method, Self* prev) { for (var methodInfo in typeof(ScriptGlue).GetMethods(.Static)) { if (methodInfo.GetCustomAttribute() case .Ok(let attribute)) { String functionType = scope $"{methodInfo.ReturnType}({methodInfo.GetParamsDecl(.. scope .())})"; String line = scope $"RegisterCall(\"{attribute.MethodName}\", => {methodInfo.Name});\n"; Compiler.EmitMethodEntry(method, line); } } } } public static void Init() { RegisterCalls(); RegisterMathFunctions(); } public static void RegisterManagedComponents() { Debug.Profiler.ProfileFunction!(); s_AddComponentMethods.Clear(); s_HasComponentMethods.Clear(); s_RemoveComponentMethods.Clear(); RegisterComponent("GlitchyEngine.Core.Transform"); RegisterComponent("GlitchyEngine.Physics.Rigidbody2D"); RegisterComponent("GlitchyEngine.Core.Camera"); RegisterComponent("GlitchyEngine.Graphics.SpriteRenderer"); RegisterComponent("GlitchyEngine.Graphics.CircleRenderer"); RegisterComponent("GlitchyEngine.Graphics.Text.TextRenderer"); RegisterComponent("GlitchyEngine.Graphics.Mesh"); RegisterComponent("GlitchyEngine.Graphics.MeshRenderer"); } [RegisterMethod] private static void RegisterCalls() { } private static void RegisterComponent(StringView cSharpClassName = "") where T : struct, new { String className; if (cSharpClassName.IsWhiteSpace) { className = scope:: $"GlitchyEngine."; typeof(T).GetName(className); } else { className = scope:: String(cSharpClassName); } className.EnsureNullTerminator(); MonoType* managedType = Mono.mono_reflection_type_from_name(className.CStr(), ScriptEngine.[Friend]s_CoreAssemblyImage); if (managedType != null) { s_AddComponentMethods[managedType] = (entity) => entity.AddComponent(); s_HasComponentMethods[managedType] = (entity) => entity.HasComponent(); s_RemoveComponentMethods[managedType] = (entity) => entity.RemoveComponent(); } else { Log.EngineLogger.AssertDebug(managedType != null, scope $"No C# component with name \"{className}\" found for Beef type \"{typeof(T)}\""); } } /// Gets the entity with the given id. Throws a mono exception, if the entity doesn't exist. static Entity GetEntitySafe(UUID entityId) { Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); if (foundEntity case .Ok(let entity)) { return foundEntity; } 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 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; } /// Gets the entity. Returns true, if the entity was found. static bool TryGetEntitySafe(UUID entityId, out Entity entity) { entity = default; Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); if (foundEntity case .Ok(out entity)) { return true; } Log.ClientLogger.Error($"No entity with ID {entityId} found."); return false; } #region Exception Helpers /// Throws an ArgumentException in the mono runtime. [NoReturn] static void ThrowArgumentException(char8* argument, char8* message) { MonoException* exception = Mono.mono_get_exception_argument(argument, message); Mono.mono_raise_exception(exception); } /// Throws an InvalidOperationException in the mono runtime. [NoReturn] static void ThrowInvalidOperationException(char8* message) { MonoException* exception = Mono.mono_get_exception_invalid_operation(message); Mono.mono_raise_exception(exception); } /// Throws an NotImplementedException in the mono runtime. [NoReturn] static void ThrowNotImplementedException(char8* message) { MonoException* exception = Mono.mono_get_exception_invalid_operation(message); Mono.mono_raise_exception(exception); } #endregion #region Log [RegisterCall("ScriptGlue::Log_LogMessage")] static void Log_LogMessage(int32 logLevel, MonoString* message, MonoString* fileName, int lineNumber) { char8* utfMessage = Mono.mono_string_to_utf8(message); String escapedMessage = scope String(StringView(utfMessage)); escapedMessage.Replace("{", "{{"); escapedMessage.Replace("}", "}}"); if (fileName != null) { char8* utfFileName = Mono.mono_string_to_utf8(fileName); MessageOrigin messageOrigin = new MessageOrigin(StringView(utfFileName), lineNumber); Log.ClientLogger.Log((LogLevel)logLevel, escapedMessage, messageOrigin); Mono.mono_free(utfFileName); } else { Log.ClientLogger.Log((LogLevel)logLevel, escapedMessage); } Mono.mono_free(utfMessage); } [RegisterCall("ScriptGlue::Log_LogException")] static void Log_LogException(MonoException* exception, UUID entityId) { ScriptEngine.HandleMonoException(exception, entityId); } #endregion #region Input [RegisterCall("Input::IsKeyPressed")] static bool Input_IsKeyPressed(Key key) => Input.IsKeyPressed(key); [RegisterCall("Input::IsKeyReleased")] static bool Input_IsKeyReleased(Key key) => Input.IsKeyReleased(key); [RegisterCall("Input::IsKeyToggled")] static bool Input_IsKeyToggled(Key key) => Input.IsKeyToggled(key); [RegisterCall("Input::IsKeyPressing")] static bool Input_IsKeyPressing(Key key) => Input.IsKeyPressing(key); [RegisterCall("Input::IsKeyReleasing")] static bool Input_IsKeyReleasing(Key key) => Input.IsKeyReleasing(key); [RegisterCall("Input::IsMouseButtonPressed")] static bool Input_IsMouseButtonPressed(MouseButton mouseButton) => Input.IsMouseButtonPressed(mouseButton); [RegisterCall("Input::IsMouseButtonReleased")] static bool Input_IsMouseButtonReleased(MouseButton mouseButton) => Input.IsMouseButtonReleased(mouseButton); [RegisterCall("Input::IsMouseButtonPressing")] static bool Input_IsMouseButtonPressing(MouseButton mouseButton) => Input.IsMouseButtonPressing(mouseButton); [RegisterCall("Input::IsMouseButtonReleasing")] static bool Input_IsMouseButtonReleasing(MouseButton mouseButton) => Input.IsMouseButtonReleasing(mouseButton); #endregion Input #region Scene/Entity stuff [RegisterCall("ScriptGlue::Entity_Create")] static void Entity_Create(MonoObject* scriptInstance, MonoString* monoEntityName, MonoArray* componentTypes, out UUID entityId) { char8* entityName = Mono.mono_string_to_utf8(monoEntityName); Entity entity = ScriptEngine.Context.CreateEntity(StringView(entityName)); Mono.mono_free(entityName); entityId = entity.UUID; // TODO: Script instance if (componentTypes != null) { Entity_AddComponents(entityId, componentTypes); } } [RegisterCall("ScriptGlue::Entity_Destroy")] static void Entity_Destroy(UUID entityId) { Entity entity = GetEntitySafe(entityId); ScriptEngine.Context.DestroyEntityDeferred(entity); } [RegisterCall("ScriptGlue::Entity_CreateInstance")] static void Entity_CreateInstance(UUID entityId, out UUID newEntityId) { Entity entity = GetEntitySafe(entityId); Entity newEntity = ScriptEngine.Context.CreateInstance(entity); newEntityId = newEntity.UUID; } [RegisterCall("ScriptGlue::Entity_AddComponents")] static void Entity_AddComponents(UUID entityId, MonoArray* componentTypes) { if (componentTypes == null) return; uint length = Mono.mono_array_length(componentTypes); for (uint i < length) { MonoReflectionType* reflectionType = Mono.mono_array_get(componentTypes, i); if (reflectionType != null) Entity_AddComponent(entityId, reflectionType); } } [RegisterCall("ScriptGlue::Entity_AddComponent")] static void Entity_AddComponent(UUID entityId, MonoReflectionType* componentType) { MonoType* type = Mono.mono_reflection_type_get_type(componentType); Entity entity = GetEntitySafe(entityId); if (s_AddComponentMethods.TryGetValue(type, let addMethod)) addMethod(entity); else Log.EngineLogger.AssertDebug(false, "No managed component with the given type registered."); } [RegisterCall("ScriptGlue::Entity_HasComponent")] static bool Entity_HasComponent(UUID entityId, MonoReflectionType* componentType) { MonoType* type = Mono.mono_reflection_type_get_type(componentType); 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."); return false; } [RegisterCall("ScriptGlue::Entity_RemoveComponent")] static void Entity_RemoveComponent(UUID entityId, MonoReflectionType* componentType) { MonoType* type = Mono.mono_reflection_type_get_type(componentType); Entity entity = GetEntitySafe(entityId); if (s_RemoveComponentMethods.TryGetValue(type, let removeMethod)) removeMethod(entity); else Log.EngineLogger.AssertDebug(false, "No managed component with the given type registered."); } [RegisterCall("ScriptGlue::Entity_FindEntityWithName")] static void Entity_FindEntityWithName(MonoString* monoName, UUID* outUuid) { *outUuid = UUID(0); char8* entityName = Mono.mono_string_to_utf8(monoName); StringView nameString = StringView(entityName); Result entityResult = ScriptEngine.Context.GetEntityByName(nameString); Mono.mono_free(entityName); if (entityResult case .Ok(let entity)) *outUuid = entity.UUID; } [RegisterCall("ScriptGlue::Entity_GetScriptInstance")] static void Entity_GetScriptInstance(UUID entityId, out MonoObject* instance) { instance = ScriptEngine.GetManagedInstance(entityId); } [RegisterCall("ScriptGlue::Entity_SetScript")] static MonoObject* Entity_SetScript(UUID entityId, MonoReflectionType* scriptType) { Entity entity = GetEntitySafe(entityId); ScriptComponent* scriptComponent = null; if (!entity.HasComponent()) { scriptComponent = entity.AddComponent(); } else { scriptComponent = entity.GetComponent(); } if (scriptComponent.Instance != null) ScriptEngine.Context.DestroyScriptDeferred(scriptComponent.Instance, false); scriptComponent.Instance = null; MonoType* type = Mono.mono_reflection_type_get_type(scriptType); scriptComponent.ScriptClassName = StringView(Mono.mono_type_full_name(type)); // Initializes the created instance // TODO: this returns false, if no script with ScriptClassName exists, we have to handle this case correctly I think. ScriptEngine.InitializeInstance(entity, scriptComponent); return scriptComponent.Instance.MonoInstance; } [RegisterCall("ScriptGlue::Entity_RemoveScript")] static void Entity_RemoveScript(UUID entityId) { ScriptComponent* scriptComponent = GetComponentSafe(entityId); ScriptEngine.Context.DestroyScriptDeferred(scriptComponent.Instance, true); } [RegisterCall("ScriptGlue::Entity_GetName")] static MonoString* Entity_GetName(UUID entityId) { Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); 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 null; } [RegisterCall("ScriptGlue::Entity_SetName")] static void Entity_SetName(UUID entityId, MonoString* name) { Entity entity = ScriptEngine.Context.GetEntityByID(entityId); char8* rawName = Mono.mono_string_to_utf8(name); entity.Name = StringView(rawName); Mono.mono_free(rawName); } [RegisterCall("ScriptGlue::Entity_GetEditorFlags")] static void Entity_GetEditorFlags(UUID entityId, out EditorFlags editorFlags) { editorFlags = .Default; #if GE_EDITOR if (TryGetEntitySafe(entityId, let entity)) editorFlags = entity.EditorFlags; #endif } [RegisterCall("ScriptGlue::Entity_SetEditorFlags")] static void Entity_SetEditorFlags(UUID entityId, EditorFlags editorFlags) { #if GE_EDITOR if (TryGetEntitySafe(entityId, let entity)) entity.EditorFlags = editorFlags; #endif } #endregion #region TransformComponent [RegisterCall("ScriptGlue::Transform_GetParent")] static void Transform_GetParent(UUID entityId, out UUID parentId) { Entity entity = GetEntitySafe(entityId); parentId = entity.Parent?.UUID ?? .Zero; } [RegisterCall("ScriptGlue::Transform_SetParent")] static void Transform_SetParent(UUID entityId, in UUID parentId) { Entity entity = GetEntitySafe(entityId); Entity? parent = GetEntitySafe(parentId); entity.Parent = parent; } [RegisterCall("ScriptGlue::Transform_GetTranslation")] static void Transform_GetTranslation(UUID entityId, out float3 translation) { Entity entity = GetEntitySafe(entityId); translation = entity.Transform.Position; } [RegisterCall("ScriptGlue::Transform_SetTranslation")] static void Transform_SetTranslation(UUID entityId, in float3 translation) { Entity entity = GetEntitySafe(entityId); entity.Transform.Position = translation; // if necessary reposition Rigidbody2D if (entity.TryGetComponent(let rigidbody2D)) { rigidbody2D.SetPosition(translation.XY); } // TODO: we need to handle repositioning of colliders that are children of the entity with rigidbody... } [RegisterCall("ScriptGlue::Transform_GetWorldTranslation")] static void Transform_GetWorldTranslation(UUID entityId, out float3 translationWorld) { Entity entity = GetEntitySafe(entityId); TransformComponent* transform = entity.Transform; translationWorld = transform.WorldTransform.Translation; //(float4(entity.Transform.Position, 1.0f) * entity.Transform.WorldTransform).XYZ; } [RegisterCall("ScriptGlue::Transform_SetWorldTranslation")] static void Transform_SetWorldTranslation(UUID entityId, in float3 translation) { Log.ClientLogger.Warning("Transform_SetWorldTranslation is not implemented!"); // Entity entity = GetEntitySafe(entityId); // entity.Transform.Position = translation; // // if necessary reposition Rigidbody2D // if (entity.TryGetComponent(let rigidbody2D)) // { // rigidbody2D.SetPosition(translation.XY); // } // TODO: we need to handle repositioning of colliders that are children of the entity with rigidbody... } [RegisterCall("ScriptGlue::Transform_TransformPointToWorld")] static void Transform_TransformPointToWorld(UUID entityId, float3 point, out float3 pointWorld) { Entity entity = GetEntitySafe(entityId); float3 localPoint = entity.Transform.Position; pointWorld = (float4(localPoint, 1.0f) * entity.Transform.WorldTransform).XYZ; } [RegisterCall("ScriptGlue::Transform_GetRotation")] static void Transform_GetRotation(UUID entityId, out Quaternion rotation) { Entity entity = GetEntitySafe(entityId); rotation = entity.Transform.Rotation; } [RegisterCall("ScriptGlue::Transform_SetRotation")] static void Transform_SetRotation(UUID entityId, in Quaternion rotation) { Entity entity = GetEntitySafe(entityId); entity.Transform.Rotation = rotation; if (entity.TryGetComponent(let rigidbody2D)) { rigidbody2D.SetAngle(entity.Transform.RotationEuler.Z); } // TODO: When rotating around the X- or Y-axis we need to deform and reposition the colliders accordingly... } [RegisterCall("ScriptGlue::Transform_GetRotationEuler")] static void Transform_GetRotationEuler(UUID entityId, out float3 rotationEuler) { Entity entity = GetEntitySafe(entityId); rotationEuler = entity.Transform.RotationEuler; } [RegisterCall("ScriptGlue::Transform_SetRotationEuler")] static void Transform_SetRotationEuler(UUID entityId, in float3 rotationEuler) { Entity entity = GetEntitySafe(entityId); entity.Transform.RotationEuler = rotationEuler; if (entity.TryGetComponent(let rigidbody2D)) { rigidbody2D.SetAngle(rotationEuler.Z); } } [Packed] struct AxisAngle { public float3 Axis; public float Angle; public this((float3 Axis, float Angle) axisAngle) { Axis = axisAngle.Axis; Angle = axisAngle.Angle; } } [RegisterCall("ScriptGlue::Transform_GetRotationAxisAngle")] static void Transform_GetRotationAxisAngle(UUID entityId, out AxisAngle rotationAxisAngle) { Entity entity = GetEntitySafe(entityId); rotationAxisAngle = AxisAngle(entity.Transform.RotationAxisAngle); } [RegisterCall("ScriptGlue::Transform_SetRotationAxisAngle")] static void Transform_SetRotationAxisAngle(UUID entityId, AxisAngle rotationAxisAngle) { Entity entity = GetEntitySafe(entityId); entity.Transform.RotationAxisAngle = (rotationAxisAngle.Axis, rotationAxisAngle.Angle); if (entity.TryGetComponent(let rigidbody2D)) { rigidbody2D.SetAngle(entity.Transform.RotationEuler.Z); } } [RegisterCall("ScriptGlue::Transform_GetScale")] static void Transform_GetScale(UUID entityId, out float3 scale) { Entity entity = GetEntitySafe(entityId); scale = entity.Transform.Scale; } [RegisterCall("ScriptGlue::Transform_SetScale")] static void Transform_SetScale(UUID entityId, float3 scale) { Entity entity = GetEntitySafe(entityId); entity.Transform.Scale = scale; if (entity.TryGetComponent(let rigidbody2D)) { // TODO: we need to scale and reposition the colliders, this is a mess... } } #endregion TransformComponent #region Rigidbody2D [RegisterCall("ScriptGlue::Rigidbody2D_ApplyForce")] static void Rigidbody2D_ApplyForce(UUID entityId, in float2 force, in float2 point, bool wakeUp) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); Box2D.Body.ApplyForce(rigidBody.[Friend]RuntimeBody, force, point, wakeUp); } [RegisterCall("ScriptGlue::Rigidbody2D_ApplyForceToCenter")] static void Rigidbody2D_ApplyForceToCenter(UUID entityId, in float2 force, bool wakeUp) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); Box2D.Body.ApplyForceToCenter(rigidBody.[Friend]RuntimeBody, force, wakeUp); } [RegisterCall("ScriptGlue::Rigidbody2D_SetPosition")] static void Rigidbody2D_SetPosition(UUID entityId, in float2 position) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.SetPosition(position); } [RegisterCall("ScriptGlue::Rigidbody2D_GetPosition")] static void Rigidbody2D_GetPosition(UUID entityId, out float2 position) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); position = rigidBody.GetPosition(); } [RegisterCall("ScriptGlue::Rigidbody2D_SetRotation")] static void Rigidbody2D_SetRotation(UUID entityId, in float rotation) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.SetAngle(rotation); } [RegisterCall("ScriptGlue::Rigidbody2D_GetRotation")] static void Rigidbody2D_GetRotation(UUID entityId, out float rotation) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rotation = rigidBody.GetAngle(); } [RegisterCall("ScriptGlue::Rigidbody2D_GetLinearVelocity")] static void Rigidbody2D_GetLinearVelocity(UUID entityId, out float2 velocity) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); velocity = rigidBody.GetLinearVelocity(); } [RegisterCall("ScriptGlue::Rigidbody2D_SetLinearVelocity")] static void Rigidbody2D_SetLinearVelocity(UUID entityId, in float2 velocity) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.SetLinearVelocity(velocity); } [RegisterCall("ScriptGlue::Rigidbody2D_GetAngularVelocity")] static void Rigidbody2D_GetAngularVelocity(UUID entityId, out float velocity) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); velocity = rigidBody.GetAngularVelocity(); } [RegisterCall("ScriptGlue::Rigidbody2D_SetAngularVelocity")] static void Rigidbody2D_SetAngularVelocity(UUID entityId, in float velocity) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.SetAngularVelocity(velocity); } [RegisterCall("ScriptGlue::Rigidbody2D_GetBodyType")] static void Rigidbody2D_GetBodyType(UUID entityId, out Rigidbody2DComponent.BodyType bodyType) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); bodyType = rigidBody.BodyType; } [RegisterCall("ScriptGlue::Rigidbody2D_SetBodyType")] static void Rigidbody2D_SetBodyType(UUID entityId, in Rigidbody2DComponent.BodyType bodyType) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.BodyType = bodyType; } [RegisterCall("ScriptGlue::Rigidbody2D_IsFixedRotation")] static void Rigidbody2D_IsFixedRotation(UUID entityId, out bool isFixedRotation) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); isFixedRotation = rigidBody.FixedRotation; } [RegisterCall("ScriptGlue::Rigidbody2D_SetFixedRotation")] static void Rigidbody2D_SetFixedRotation(UUID entityId, in bool isFixedRotation) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.FixedRotation = isFixedRotation; } [RegisterCall("ScriptGlue::Rigidbody2D_GetGravityScale")] static void Rigidbody2D_GetGravityScale(UUID entityId, out float gravityScale) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); gravityScale = rigidBody.GravityScale; } [RegisterCall("ScriptGlue::Rigidbody2D_SetGravityScale")] static void Rigidbody2D_SetGravityScale(UUID entityId, in float gravityScale) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.GravityScale = gravityScale; } #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; } [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! [RegisterCall("ScriptGlue::Physics2D_GetGravity")] static void Physics2D_GetGravity(out float2 gravity) { Scene scene = ScriptEngine.Context; gravity = scene.Physics2DSettings.Gravity; } [RegisterCall("ScriptGlue::Physics2D_SetGravity")] static void Physics2D_SetGravity(in float2 gravity) { Scene scene = ScriptEngine.Context; scene.Physics2DSettings.Gravity = gravity; } #endregion Physics2D #region CircleRenderer [RegisterCall("ScriptGlue::CircleRenderer_GetColor")] static void CircleRenderer_GetColor(UUID entityId, out ColorRGBA color) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); color = circleRenderer.Color; } [RegisterCall("ScriptGlue::CircleRenderer_SetColor")] static void CircleRenderer_SetColor(UUID entityId, ColorRGBA color) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); circleRenderer.Color = color; } [RegisterCall("ScriptGlue::CircleRenderer_GetUvTransform")] static void CircleRenderer_GetUvTransform(UUID entityId, out float4 uvTransform) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); uvTransform = circleRenderer.UvTransform; } [RegisterCall("ScriptGlue::CircleRenderer_SetUvTransform")] static void CircleRenderer_SetUvTransform(UUID entityId, float4 uvTransform) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); circleRenderer.UvTransform = uvTransform; } [RegisterCall("ScriptGlue::CircleRenderer_GetInnerRadius")] static void CircleRenderer_GetInnerRadius(UUID entityId, out float innerRadius) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); innerRadius = circleRenderer.InnerRadius; } [RegisterCall("ScriptGlue::CircleRenderer_GetInnerRadius")] static void CircleRenderer_GetInnerRadius(UUID entityId, float innerRadius) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); circleRenderer.InnerRadius = innerRadius; } #endregion #region SpriteRenderer [RegisterCall("ScriptGlue::SpriteRenderer_GetColor")] static void SpriteRenderer_GetColor(UUID entityId, out ColorRGBA color) { SpriteRendererComponent* spriteRenderer = GetComponentSafe(entityId); color = spriteRenderer.Color; } [RegisterCall("ScriptGlue::SpriteRenderer_SetColor")] static void SpriteRenderer_SetColor(UUID entityId, ColorRGBA color) { SpriteRendererComponent* spriteRenderer = GetComponentSafe(entityId); spriteRenderer.Color = color; } [RegisterCall("ScriptGlue::SpriteRenderer_GetUvTransform")] static void SpriteRenderer_GetUvTransform(UUID entityId, out float4 uvTransform) { SpriteRendererComponent* spriteRenderer = GetComponentSafe(entityId); uvTransform = spriteRenderer.UvTransform; } [RegisterCall("ScriptGlue::SpriteRenderer_SetUvTransform")] static void SpriteRenderer_SetUvTransform(UUID entityId, float4 uvTransform) { SpriteRendererComponent* spriteRenderer = GetComponentSafe(entityId); spriteRenderer.UvTransform = uvTransform; } [RegisterCall("ScriptGlue::SpriteRenderer_GetMaterial")] static void SpriteRenderer_GetMaterial(UUID entityId, out AssetHandle assetId) { SpriteRendererComponent* spriteRenderer = GetComponentSafe(entityId); Material material = GetAssetOrThrow!((AssetHandle)spriteRenderer.Material); if (!material.IsRuntimeInstance) { using (material = new Material(material, true)) { material.Identifier = scope $"(Instance) {material.Identifier}"; //Content.ManageAsset(material); spriteRenderer.Material = material.Handle; } } assetId = spriteRenderer.Material; } [RegisterCall("ScriptGlue::SpriteRenderer_SetMaterial")] static void SpriteRenderer_SetMaterial(UUID entityId, AssetHandle assetId) { SpriteRendererComponent* spriteRenderer = GetComponentSafe(entityId); spriteRenderer.Material = assetId; } #endregion #region TextRenderer [RegisterCall("ScriptGlue::TextRenderer_SetIsRichText")] static bool TextRenderer_SetIsRichText(UUID entityId) { return GetComponentSafe(entityId).IsRichText; } [RegisterCall("ScriptGlue::TextRenderer_SetIsRichText")] static void TextRenderer_SetIsRichText(UUID entityId, bool isRichText) { TextRendererComponent* textComponent = GetComponentSafe(entityId); textComponent.IsRichText = isRichText; textComponent.NeedsRebuild = true; } [RegisterCall("ScriptGlue::TextRenderer_GetText")] static void TextRenderer_GetText(UUID entityId, out MonoString* text) { TextRendererComponent* textComponent = GetComponentSafe(entityId); text = Mono.mono_string_new_len(ScriptEngine.[Friend]s_AppDomain, textComponent.Text.Ptr, (uint32)textComponent.Text.Length); } [RegisterCall("ScriptGlue::TextRenderer_SetText")] static void TextRenderer_SetText(UUID entityId, MonoString* text) { TextRendererComponent* textComponent = GetComponentSafe(entityId); char8* rawText = Mono.mono_string_to_utf8(text); textComponent.Text = StringView(rawText); textComponent.NeedsRebuild = true; Mono.mono_free(rawText); } [RegisterCall("ScriptGlue::TextRenderer_GetColor")] static void TextRenderer_GetColor(UUID entityId, out ColorRGBA color) { TextRendererComponent* textComponent = GetComponentSafe(entityId); color = textComponent.Color; } [RegisterCall("ScriptGlue::TextRenderer_SetColor")] static void TextRenderer_SetColor(UUID entityId, ColorRGBA color) { TextRendererComponent* textComponent = GetComponentSafe(entityId); textComponent.Color = color; textComponent.NeedsRebuild = true; } [RegisterCall("ScriptGlue::TextRenderer_GetHorizontalAlignment")] static void TextRenderer_GetHorizontalAlignment(UUID entityId, out HorizontalTextAlignment horizontalAlignment) { TextRendererComponent* textComponent = GetComponentSafe(entityId); horizontalAlignment = textComponent.HorizontalAlignment; } [RegisterCall("ScriptGlue::TextRenderer_SetHorizontalAlignment")] static void TextRenderer_SetHorizontalAlignment(UUID entityId, HorizontalTextAlignment horizontalAlignment) { TextRendererComponent* textComponent = GetComponentSafe(entityId); textComponent.HorizontalAlignment = horizontalAlignment; textComponent.NeedsRebuild = true; } [RegisterCall("ScriptGlue::TextRenderer_GetFontSize")] static void TextRenderer_GetFontSize(UUID entityId, out float fontSize) { TextRendererComponent* textComponent = GetComponentSafe(entityId); fontSize = textComponent.FontSize; } [RegisterCall("ScriptGlue::TextRenderer_SetFontSize")] static void TextRenderer_SetFontSize(UUID entityId, float fontSize) { TextRendererComponent* textComponent = GetComponentSafe(entityId); textComponent.FontSize = fontSize; textComponent.NeedsRebuild = true; } #endregion #region Mesh #endregion #region MeshRenderer [RegisterCall("ScriptGlue::MeshRenderer_GetMaterial")] static void MeshRenderer_GetMaterial(UUID entityId, out AssetHandle assetId) { MeshRendererComponent* meshRenderer = GetComponentSafe(entityId); Material material = GetAssetOrThrow!((AssetHandle)meshRenderer.Material); if (!material.IsRuntimeInstance) { using (material = new Material(material, true)) { material.Identifier = scope $"(Instance) {material.Identifier}"; //Content.ManageAsset(material); meshRenderer.Material = material.Handle; } } assetId = meshRenderer.Material; } [RegisterCall("ScriptGlue::MeshRenderer_SetMaterial")] static void MeshRenderer_SetMaterial(UUID entityId, AssetHandle assetId) { MeshRendererComponent* meshRenderer = GetComponentSafe(entityId); meshRenderer.Material = assetId; } #endregion #region Math private static void RegisterMathFunctions() { RegisterCall("ScriptGlue::modf_float", => GlitchyEngine.Math.modf); RegisterCall("ScriptGlue::modf_float2", => GlitchyEngine.Math.modf); RegisterCall("ScriptGlue::modf_float3", => GlitchyEngine.Math.modf); RegisterCall("ScriptGlue::modf_float4", => GlitchyEngine.Math.modf); RegisterHalfFunctions(); } private static void RegisterHalfFunctions() { RegisterCall("Math.Half::FromFloat32", (value, halfValue) => halfValue = GlitchyEngine.Math.half.FromFloat32(value)); RegisterCall("Math.Half::ToFloat32", (value, floatValue) => floatValue = GlitchyEngine.Math.half.ToFloat32(value)); RegisterCall("Math.Half::LessThan_Impl", (left, right, result) => result = left < right); RegisterCall("Math.Half::LessThanOrEqual_Impl", (left, right, result) => result = left <= right); RegisterCall("Math.Half::GreaterThan_Impl", (left, right, result) => result = left > right); RegisterCall("Math.Half::GreaterThanOrEqual_Impl", (left, right, result) => result = left >= right); RegisterCall("Math.Half::Add_Impl", (left, right, result) => result = left + right); RegisterCall("Math.Half::Subtract_Impl", (left, right, result) => result = left - right); RegisterCall("Math.Half::Multiply_Impl", (left, right, result) => result = left * right); RegisterCall("Math.Half::Divide_Impl", (left, right, result) => result = left / right); RegisterCall("Math.Half::Modulo_Impl", (left, right, result) => result = left % right); RegisterCall("Math.Half::Negate_Impl", (value, result) => result = -value); RegisterCall("Math.Half::Increment_Impl", (value, result) => result = ++value); RegisterCall("Math.Half::Decrement_Impl", (value, result) => result = --value); RegisterCall("Math.Half::IsNegative_Impl", (value) => value.IsNegative); RegisterCall("Math.Half::IsFinite_Impl", (value) => value.IsFinite); RegisterCall("Math.Half::IsInfinity_Impl", (value) => value.IsInfinity); RegisterCall("Math.Half::IsNan_Impl", (value) => value.IsNaN); RegisterCall("Math.Half::IsSubnormal_Impl", (value) => value.IsSubnormal); } #endregion [RegisterCall("ScriptGlue::UUID_CreateNew")] static void UUID_Create(out UUID id) { id = UUID.Create(); } #region Application [RegisterCall("ScriptGlue::Application_IsEditor")] static bool Application_IsEditor() { return ScriptEngine.ApplicationInfo.IsEditor; } [RegisterCall("ScriptGlue::Application_IsPlayer")] static bool Application_IsPlayer() { return ScriptEngine.ApplicationInfo.IsPlayer; } [RegisterCall("ScriptGlue::Application_IsInEditMode")] static bool Application_IsInEditMode() { return ScriptEngine.ApplicationInfo.IsInEditMode; } [RegisterCall("ScriptGlue::Application_IsInPlayMode")] static bool Application_IsInPlayMode() { return ScriptEngine.ApplicationInfo.IsInPlayMode; } #endregion #region Serialization [RegisterCall("ScriptGlue::Serialization_SerializeField")] static void Serialization_SerializeField(void* serializationContext, SerializationType type, MonoString* nameObject, MonoObject* valueObject, MonoString* fullTypeName) { SerializedObject context = Internal.UnsafeCastToObject(serializationContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); char8* name = Mono.mono_string_to_utf8(nameObject); context.AddField(StringView(name), type, valueObject, fullTypeName); Mono.mono_free(name); } [RegisterCall("ScriptGlue::Serialization_CreateObject")] static void Serialization_CreateObject(void* currentContext, bool isStatic, MonoString* typeName, out void* newContext, out UUID newId) { SerializedObject context = Internal.UnsafeCastToObject(currentContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); char8* rawTypeName = Mono.mono_string_to_utf8(typeName); SerializedObject newObject = new SerializedObject(context.Serializer, isStatic, StringView(rawTypeName)); Mono.mono_free(rawTypeName); newContext = Internal.UnsafeCastToPtr(newObject); newId = newObject.Id; } [RegisterCall("ScriptGlue::Serialization_DeserializeField")] public static void Serialization_DeserializeField(void* internalContext, SerializationType expectedType, MonoString* fieldName, uint8* target, out SerializationType actualType) { SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); char8* name = Mono.mono_string_to_utf8(fieldName); context.GetField(StringView(name), expectedType, target, out actualType); Mono.mono_free(name); } [RegisterCall("ScriptGlue::Serialization_GetObject")] public static void Serialization_GetObject(void* internalContext, UUID id, out void* objectContext) { SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); objectContext = null; Log.EngineLogger.AssertDebug(context.Serializer.ContainsId(context.Id)); if (!context.Serializer.TryGetSerializedObject(id, let foundObject)) return; objectContext = Internal.UnsafeCastToPtr(foundObject); } [RegisterCall("ScriptGlue::Serialization_GetObjectTypeName")] public static void Serialization_GetObjectTypeName(void* internalContext, out MonoString* fullTypeName) { SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); fullTypeName = Mono.mono_string_new(ScriptEngine.[Friend]s_AppDomain, context.TypeName); } #endregion #region Asset /*static mixin GetAssetOrThrow(UUID assetId) { AssetHandle handle = .(assetId); Asset asset = Content.GetAsset(handle, blocking: true); if (asset == null) { ThrowInvalidOperationException(scope $"No asset exists for AssetHandle \"{assetId}\"."); } asset }*/ static mixin GetAssetOrThrow(UUID assetId) where T : Asset { AssetHandle handle = .(assetId); T asset = Content.GetAsset(handle, blocking: true); if (asset == null) { ThrowInvalidOperationException(scope $"No asset exists for AssetHandle \"{assetId}\"."); } asset } static mixin GetAssetOrThrow(AssetHandle assetHandle) where T : Asset { T asset = Content.GetAsset(assetHandle, blocking: true); if (asset == null) { ThrowInvalidOperationException(scope $"No asset exists for AssetHandle \"{assetHandle}\"."); } asset } [RegisterCall("ScriptGlue::Asset_GetIdentifier")] static void Asset_GetIdentifier(UUID assetId, out MonoString* text) { Asset asset = GetAssetOrThrow!(assetId); text = Mono.mono_string_new_len(ScriptEngine.[Friend]s_AppDomain, asset.Identifier.Ptr, (uint32)asset.Identifier.Length); } [RegisterCall("ScriptGlue::Asset_SetIdentifier")] static void Asset_GetIdentifier(UUID assetId, MonoString* text) { ThrowNotImplementedException("Asset.GetIdentifier is not implemented."); /*AssetHandle handle = .(assetId); Asset asset = Content.GetAsset(handle, blocking: true); char8* rawText = Mono.mono_string_to_utf8(text); // TODO: I think it's not THAT easy. asset.Identifier = StringView(rawText); Mono.mono_free(rawText);*/ } #endregion #region Material [RegisterCall("ScriptGlue::Material_SetVariable")] static void Material_SetVariable(AssetHandle assetHandle, MonoString* managedVariableName, ShaderVariableType elementType, int32 rows, int32 columns, int32 arrayLength, uint8* rawData, int32 dataLength) { Material material = GetAssetOrThrow!(assetHandle); char8* rawVariableName = Mono.mono_string_to_utf8(managedVariableName); material.[Friend]SetVariableRaw(StringView(rawVariableName), elementType, rows, columns , arrayLength, Span(rawData, dataLength)); Mono.mono_free(rawVariableName); } [RegisterCall("ScriptGlue::Material_ResetVariable")] static void Material_ResetVariable(AssetHandle assetHandle, MonoString* managedVariableName) { Material material = GetAssetOrThrow!(assetHandle); char8* rawVariableName = Mono.mono_string_to_utf8(managedVariableName); material.ResetVariable(StringView(rawVariableName)); Mono.mono_free(rawVariableName); } #endregion #region ImGui Extension [RegisterCall("ScriptGlue::ImGuiExtension_ListElementGrabber")] static void ImGuiExtension_ListElementGrabber() { ImGui.ImGui.ListElementGrabber(); } #endregion private static void RegisterCall(String name, T method) where T : var { Mono.mono_add_internal_call(scope $"GlitchyEngine.{name}", (void*)method); } }