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 System.Diagnostics; using System.IO; using System.Linq; using static GlitchyEngine.Scripting.ScriptGlue; using static GlitchyEngine.Renderer.Text.FontRenderer; namespace GlitchyEngine.Scripting; using internal GlitchyEngine.Scripting; using internal GlitchyEngine.Content; // TODO: Move to logger? 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; } } [AttributeUsage(.Method)] struct RegisterCallAttribute : Attribute { public bool EngineResultAsBool { get; set mut; } public bool IsExtension { get; set mut; } } struct TypeTranslationTemplate { public String StartCode; public String EndCode; public String PrettyCSharpParamType; public this(String startCode, String endCode, String prettyCSharpParamType) { StartCode = startCode; EndCode = endCode; PrettyCSharpParamType = prettyCSharpParamType; } } [AttributeUsage(.Parameter)] struct GlueParamAttribute : Attribute { public String WrapperType; public this(String wrapperType) { WrapperType = wrapperType; } } [AttributeUsage(.Struct)] struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply { private static String GetTypeInfo(Type type, RegisterCallAttribute? callAttribute = null) { String value = new .(); type.ToString(value); if (type.IsEnum) { // Pass the underlying integer type for enums. value.AppendF($" : {type.UnderlyingType}"); } else if (!type.HasCustomAttribute() && !type.IsPrimitive && !type.IsPointer && !(type is RefType)) { // For CDecl Functions beef ecpects non CRepr struct to be passed as readonly ref. // Thus we add in to the type name. However primitive types are passed by value! value.Insert(0, "in "); } // EngineResult will usually just be converted to exceptions -> script method returns void // But sometimes we want to be able to also return a boolean value. if (type == typeof(EngineResult) && callAttribute.HasValue) { if (callAttribute.Value.EngineResultAsBool) { value.Append(" as bool"); } else { value.Append(" as void"); } } return value; } private static void GenerateJsonInfo(MethodInfo method, RegisterCallAttribute registerCallAttribute, String outString) { String parameters = new String(); for (int i < method.ParamCount) { if (i != 0) parameters.Append(", "); parameters.AppendF($""" {{ "name": "{method.GetParamName(i)}", "type": "{GetTypeInfo(method.GetParamType(i))}" }} """); } outString.AppendF($""" {{ "name": "{method.Name}", "return_type": "{GetTypeInfo(method.ReturnType, registerCallAttribute)}", "parameters": [{parameters} ] }}, """); } [Comptime] public void ApplyToType(Type self) { String jsonOutput = new String("["); for (MethodInfo methodInfo in typeof(ScriptGlue).GetMethods(.Static | .NonPublic)) { if (methodInfo.GetCustomAttribute() case .Ok(let attribute)) { String parameters = new String(); for (int i < methodInfo.ParamCount) { if (i != 0) parameters.Append(", "); parameters.AppendF($"{methodInfo.GetParamType(i)}"); } String line = scope $"public function [CallingConvention(.Cdecl)] {methodInfo.ReturnType}({parameters}) {methodInfo.Name};\n"; Compiler.EmitTypeBody(self, line); GenerateJsonInfo(methodInfo, attribute, jsonOutput); } } if (jsonOutput.EndsWith(",")) { jsonOutput.RemoveFromEnd(1); } jsonOutput.Append(']'); Directory.CreateDirectory("../generated"); if (File.WriteAllText("../generated/ScriptGlue.json", jsonOutput) case .Err) { Runtime.FatalError("Failed to write file"); } } } [EngineFunctionsGenerator] struct EngineFunctions { static Self _functions; [RegisterMethod] public static void FillEngineFunctions() { OnRegisterNativeCalls.Invoke(); ScriptGlue.[Friend]_setEngineFunctions(&_functions); } } /* Adding this attribute to a method will log method entry and returned Result errors */ [AttributeUsage(.Method | .Constructor)] public struct RegisterMethodAttribute : Attribute, IOnMethodInit { public bool IsExtension = false; [Comptime] public void OnMethodInit(MethodInfo method, Self* prev) { String functionContent = new .(); int i = 0; BindingFlags flags = .Static; if (IsExtension) { flags |= .DeclaredOnly; } for (var methodInfo in typeof(ScriptGlue).GetMethods(flags)) { if (methodInfo.GetCustomAttribute() not case .Ok(let attribute)) continue; if (IsExtension && attribute.IsExtension) { functionContent.AppendF($"EngineFunctions.[Friend]_functions.{methodInfo.Name} = => ScriptGlue.{methodInfo.Name};\n"); i++; } else if (!IsExtension && !attribute.IsExtension) { functionContent.AppendF($"_functions.{methodInfo.Name} = => ScriptGlue.[Friend]{methodInfo.Name};\n"); i++; } } //functionContent.Insert(0, scope $"EngineFunctions functions = .();\n"); Compiler.EmitMethodEntry(method, functionContent); } } static class ScriptGlue { public static Event OnRegisterNativeCalls ~ _.Dispose(); private function void SetEngineFunctions(EngineFunctions* engineFunctions); private static SetEngineFunctions _setEngineFunctions; private static String _lastExceptionMessage = new .() ~ delete _; public static void Init() { if (_setEngineFunctions == null) { CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "SetEngineFunctions", out _setEngineFunctions); } RegisterCalls(); RegisterManagedComponents(); } public static void RegisterManagedComponents() { Debug.Profiler.ProfileFunction!(); RegisterComponent(); RegisterComponent(); RegisterComponent(); RegisterComponent(); RegisterComponent(); RegisterComponent(); RegisterComponent(); RegisterComponent(); } private static void RegisterCalls() { EngineFunctions.FillEngineFunctions(); } private static void RegisterComponent() where T : struct, new { String fullComponentTypeName = scope String(); typeof(T).GetFullName(fullComponentTypeName); CoreClrHelper.RegisterComponent(fullComponentTypeName..EnsureNullTerminator(), addComponent: (entityId) => { if (GetEntitySafe(entityId) case .Ok(Entity entity)) { entity.AddComponent(); return .Ok; } return .EntityNotFound; }, hasComponent: (entityId) => { if (GetEntitySafe(entityId) case .Ok(Entity entity)) { return entity.HasComponent() ? .Ok : .False; } return .EntityNotFound; }, removeComponent: (entityId) => { if (GetEntitySafe(entityId) case .Ok(Entity entity)) { entity.RemoveComponent(); return .Ok; } return .EntityNotFound; }); } /// Gets the entity with the given id. Throws a mono exception, if the entity doesn't exist. static Result GetEntitySafe(UUID entityId) { if (ScriptEngine.Context.GetEntityByID(entityId) case .Ok(let entity)) { return entity; } return .Err; } static mixin GetEntityOrReturnError(UUID entityId) { if (GetEntitySafe(entityId) not case .Ok(let entity)) { return EngineResult.EntityNotFound; } entity } /// 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 Result 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 { return .Err(.EntityDoesntHaveComponent); } } else { return .Err(.EntityNotFound); } } static mixin GetComponentOrReturn(UUID entityId) where T: struct, new { Result result = GetComponentSafe(entityId); if (result case .Err(EngineResult error)) { return error; } result.Value } /// 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; } public enum EngineResult : int32 { case Ok = 0; // Success, for boolean return value means True case False = 1; // Success, for boolean return value means False case Error = -1; case NotImplemented = -2; case ArgumentError = -3; // Entity Errors: case EntityNotFound = -4; // The entity doesn't exist or was deleted. case EntityDoesntHaveComponent = -5; // The entity has no component of type {typeof(T)} or it was deleted. case AssetNotFound = -6; // No asset exists for AssetHandle \"{assetId}\". case CustomError = 1 << 31; public static EngineResult CustomError(int32 payload) { return CustomError | payload; } } [RegisterCall, CallingConvention(.Cdecl)] static StringView GetLastExceptionMessage() { return _lastExceptionMessage; } #region Log [RegisterCall, CallingConvention(.Cdecl)] static void Log_LogMessage(LogLevel logLevel, StringView messagePtr, StringView fileNamePtr, int lineNumber) { String escapedMessage = new:ScopedAlloc! String(messagePtr); // Escape the message, because the editor logger does a string-format escapedMessage.Replace("{", "{{"); escapedMessage.Replace("}", "}}"); if (!fileNamePtr.IsEmpty) { String fileName = new:ScopedAlloc! String(fileNamePtr); MessageOrigin messageOrigin = scope MessageOrigin(fileName, lineNumber); Log.ClientLogger.Log((LogLevel)logLevel, escapedMessage, messageOrigin); } else { Log.ClientLogger.Log((LogLevel)logLevel, escapedMessage); } } [RegisterCall, CallingConvention(.Cdecl)] static void Log_LogException(UUID entityId, StringView fullExceptionClassName, StringView exceptionMessage, StringView stackTrace) { ScriptException exception = new ScriptException(entityId, fullExceptionClassName, exceptionMessage, stackTrace); ScriptEngine.LogScriptException(exception, entityId); } #endregion #region Input [RegisterCall, CallingConvention(.Cdecl)] static bool Input_IsKeyPressed(Key key) => Input.IsKeyPressed(key); [RegisterCall, CallingConvention(.Cdecl)] static bool Input_IsKeyReleased(Key key) => Input.IsKeyReleased(key); [RegisterCall, CallingConvention(.Cdecl)] static bool Input_IsKeyToggled(Key key) => Input.IsKeyToggled(key); [RegisterCall, CallingConvention(.Cdecl)] static bool Input_IsKeyPressing(Key key) => Input.IsKeyPressing(key); [RegisterCall, CallingConvention(.Cdecl)] static bool Input_IsKeyReleasing(Key key) => Input.IsKeyReleasing(key); [RegisterCall, CallingConvention(.Cdecl)] static bool Input_IsMouseButtonPressed(MouseButton mouseButton) => Input.IsMouseButtonPressed(mouseButton); [RegisterCall, CallingConvention(.Cdecl)] static bool Input_IsMouseButtonReleased(MouseButton mouseButton) => Input.IsMouseButtonReleased(mouseButton); [RegisterCall, CallingConvention(.Cdecl)] static bool Input_IsMouseButtonPressing(MouseButton mouseButton) => Input.IsMouseButtonPressing(mouseButton); [RegisterCall, CallingConvention(.Cdecl)] static bool Input_IsMouseButtonReleasing(MouseButton mouseButton) => Input.IsMouseButtonReleasing(mouseButton); #endregion Input #region Scene/Entity stuff [RegisterCall, CallingConvention(.Cdecl)] static void Entity_Create(StringView entityName, out UUID entityId) { Entity entity = ScriptEngine.Context.CreateEntity(entityName); entityId = entity.UUID; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Entity_Destroy(UUID entityId) { Entity entity = GetEntityOrReturnError!(entityId); ScriptEngine.Context.DestroyEntityDeferred(entity); return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Entity_CreateInstance(UUID entityId, out UUID newEntityId) { newEntityId = ?; Entity entity = GetEntityOrReturnError!(entityId); Entity newEntity = ScriptEngine.Context.CreateInstance(entity); newEntityId = newEntity.UUID; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static void Entity_FindEntityWithName(StringView entityName, out UUID outUuid) { outUuid = UUID(0); Result entityResult = ScriptEngine.Context.GetEntityByName(entityName); if (entityResult case .Ok(let entity)) outUuid = entity.UUID; } // TODO: Do we need this? [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Entity_GetScriptInstance(UUID entityId, out void* instance) { instance = ?; return .NotImplemented; //instance = ScriptEngine.GetManagedInstance(entityId); } [RegisterCall(EngineResultAsBool = true), CallingConvention(.Cdecl)] static EngineResult Entity_SetScript(UUID entityId, StringView fullScriptTypeName) { Entity entity = GetEntityOrReturnError!(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; scriptComponent.ScriptClassName = fullScriptTypeName; // Initializes the created instance // TODO: this returns false, if no script with ScriptClassName exists, we have to handle this case correctly I think. if (!ScriptEngine.InitializeInstance(entity, scriptComponent)) { return .ArgumentError; } //return scriptComponent.Instance.MonoInstance; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Entity_RemoveScript(UUID entityId) { ScriptComponent* scriptComponent = GetComponentOrReturn!(entityId); ScriptEngine.Context.DestroyScriptDeferred(scriptComponent.Instance, true); return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static StringView Entity_GetName(UUID entityId) { Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); if (foundEntity case .Ok(let entity)) { return entity.Name; } return null; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Entity_SetName(UUID entityId, StringView name) { GetEntityOrReturnError!(entityId).Name = name; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static void Entity_GetEditorFlags(UUID entityId, out EditorFlags editorFlags) { editorFlags = .Default; #if GE_EDITOR if (TryGetEntitySafe(entityId, let entity)) editorFlags = entity.EditorFlags; #endif } [RegisterCall, CallingConvention(.Cdecl)] static void Entity_SetEditorFlags(UUID entityId, EditorFlags editorFlags) { #if GE_EDITOR if (TryGetEntitySafe(entityId, let entity)) entity.EditorFlags = editorFlags; #endif } #endregion #region TransformComponen [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_GetParent(UUID entityId, out UUID parentId) { parentId = .Zero; Entity entity = GetEntityOrReturnError!(entityId); parentId = entity.Parent?.UUID ?? .Zero; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_SetParent(UUID entityId, UUID newParentId) { Entity entity = GetEntityOrReturnError!(entityId); Entity? newParent = null; if (newParentId != .Zero) { GetEntityOrReturnError!(newParentId); } entity.Parent = newParent; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_GetTranslation(UUID entityId, out float3 translation) { translation = ?; Entity entity = GetEntityOrReturnError!(entityId); translation = entity.Transform.Position; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_SetTranslation(UUID entityId, in float3 translation) { Entity entity = GetEntityOrReturnError!(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... return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_GetWorldTranslation(UUID entityId, out float3 translationWorld) { translationWorld = ?; Entity entity = GetEntityOrReturnError!(entityId); TransformComponent* transform = entity.Transform; translationWorld = transform.WorldTransform.Translation; //(float4(entity.Transform.Position, 1.0f) * entity.Transform.WorldTransform).XYZ; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_SetWorldTranslation(UUID entityId, in float3 translation) { Log.ClientLogger.Warning("Transform_SetWorldTranslation is not implemented!"); return .NotImplemented; // 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, CallingConvention(.Cdecl)] static EngineResult Transform_TransformPointToWorld(UUID entityId, float3 point, out float3 pointWorld) { pointWorld = ?; Entity entity = GetEntityOrReturnError!(entityId); float3 localPoint = entity.Transform.Position; pointWorld = (float4(localPoint, 1.0f) * entity.Transform.WorldTransform).XYZ; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_GetRotation(UUID entityId, out Quaternion rotation) { rotation = ?; Entity entity = GetEntityOrReturnError!(entityId); rotation = entity.Transform.Rotation; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_SetRotation(UUID entityId, in Quaternion rotation) { Entity entity = GetEntityOrReturnError!(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... return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_GetRotationEuler(UUID entityId, out float3 rotationEuler) { rotationEuler = ?; Entity entity = GetEntityOrReturnError!(entityId); rotationEuler = entity.Transform.RotationEuler; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_SetRotationEuler(UUID entityId, in float3 rotationEuler) { Entity entity = GetEntityOrReturnError!(entityId); entity.Transform.RotationEuler = rotationEuler; if (entity.TryGetComponent(let rigidbody2D)) { rigidbody2D.SetAngle(rotationEuler.Z); } return .Ok; } [Packed] public struct AxisAngle { public float3 Axis; public float Angle; public this((float3 Axis, float Angle) axisAngle) { Axis = axisAngle.Axis; Angle = axisAngle.Angle; } } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_GetRotationAxisAngle(UUID entityId, out AxisAngle rotationAxisAngle) { rotationAxisAngle = ?; Entity entity = GetEntityOrReturnError!(entityId); rotationAxisAngle = AxisAngle(entity.Transform.RotationAxisAngle); return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_SetRotationAxisAngle(UUID entityId, AxisAngle rotationAxisAngle) { Entity entity = GetEntityOrReturnError!(entityId); entity.Transform.RotationAxisAngle = (rotationAxisAngle.Axis, rotationAxisAngle.Angle); if (entity.TryGetComponent(let rigidbody2D)) { rigidbody2D.SetAngle(entity.Transform.RotationEuler.Z); } return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_GetScale(UUID entityId, out float3 scale) { scale = ?; Entity entity = GetEntityOrReturnError!(entityId); scale = entity.Transform.Scale; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Transform_SetScale(UUID entityId, float3 scale) { Entity entity = GetEntityOrReturnError!(entityId); entity.Transform.Scale = scale; if (entity.TryGetComponent(let rigidbody2D)) { // TODO: we need to scale and reposition the colliders, this is a mess... } return .Ok; } #endregion TransformComponent #region Rigidbody2 [RegisterCall, CallingConvention(.Cdecl)] 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, CallingConvention(.Cdecl)] static void Rigidbody2D_ApplyForceToCenter(UUID entityId, in float2 force, bool wakeUp) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); Box2D.Body.ApplyForceToCenter(rigidBody.[Friend]RuntimeBody, force, wakeUp); } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_SetPosition(UUID entityId, in float2 position) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.SetPosition(position); } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_GetPosition(UUID entityId, out float2 position) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); position = rigidBody.GetPosition(); } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_SetRotation(UUID entityId, float rotation) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.SetAngle(rotation); } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_GetRotation(UUID entityId, out float rotation) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rotation = rigidBody.GetAngle(); } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_GetLinearVelocity(UUID entityId, out float2 velocity) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); velocity = rigidBody.GetLinearVelocity(); } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_SetLinearVelocity(UUID entityId, in float2 velocity) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.SetLinearVelocity(velocity); } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_GetAngularVelocity(UUID entityId, out float velocity) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); velocity = rigidBody.GetAngularVelocity(); } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_SetAngularVelocity(UUID entityId, float velocity) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.SetAngularVelocity(velocity); } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_GetBodyType(UUID entityId, out Rigidbody2DComponent.BodyType bodyType) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); bodyType = rigidBody.BodyType; } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_SetBodyType(UUID entityId, Rigidbody2DComponent.BodyType bodyType) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.BodyType = bodyType; } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_IsFixedRotation(UUID entityId, out bool isFixedRotation) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); isFixedRotation = rigidBody.FixedRotation; } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_SetFixedRotation(UUID entityId, bool isFixedRotation) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.FixedRotation = isFixedRotation; } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_GetGravityScale(UUID entityId, out float gravityScale) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); gravityScale = rigidBody.GravityScale; } [RegisterCall, CallingConvention(.Cdecl)] static void Rigidbody2D_SetGravityScale(UUID entityId, float gravityScale) { Rigidbody2DComponent* rigidBody = GetComponentSafe(entityId); rigidBody.GravityScale = gravityScale; } #endregion Rigidbody2D #region Camer [RegisterCall, CallingConvention(.Cdecl)] static void Camera_GetProjectionType(UUID entityId, out SceneCamera.ProjectionType projectionType) { CameraComponent* camera = GetComponentSafe(entityId); projectionType = camera.Camera.ProjectionType; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_SetProjectionType(UUID entityId, SceneCamera.ProjectionType projectionType) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.ProjectionType = projectionType; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_GetPerspectiveFovY(UUID entityId, out float fovY) { CameraComponent* camera = GetComponentSafe(entityId); fovY = camera.Camera.PerspectiveFovY; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_SetPerspectiveFovY(UUID entityId, float fovY) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.PerspectiveFovY = fovY; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_GetPerspectiveNearPlane(UUID entityId, out float nearPlane) { CameraComponent* camera = GetComponentSafe(entityId); nearPlane = camera.Camera.PerspectiveNearPlane; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_SetPerspectiveNearPlane(UUID entityId, float nearPlane) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.PerspectiveNearPlane = nearPlane; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_GetPerspectiveFarPlane(UUID entityId, out float farPlane) { CameraComponent* camera = GetComponentSafe(entityId); farPlane = camera.Camera.PerspectiveFarPlane; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_SetPerspectiveFarPlane(UUID entityId, float farPlane) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.PerspectiveFarPlane = farPlane; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_GetOrthographicHeight(UUID entityId, out float height) { CameraComponent* camera = GetComponentSafe(entityId); height = camera.Camera.OrthographicHeight; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_SetOrthographicHeight(UUID entityId, float height) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.OrthographicHeight = height; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_SetOrthographicNearPlane(UUID entityId, float nearPlane) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.OrthographicNearPlane = nearPlane; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_GetOrthographicNearPlane(UUID entityId, out float nearPlane) { CameraComponent* camera = GetComponentSafe(entityId); nearPlane = camera.Camera.OrthographicNearPlane; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_SetOrthographicFarPlane(UUID entityId, float farPlane) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.OrthographicFarPlane = farPlane; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_GetOrthographicFarPlane(UUID entityId, out float farPlane) { CameraComponent* camera = GetComponentSafe(entityId); farPlane = camera.Camera.OrthographicFarPlane; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_SetAspectRatio(UUID entityId, float aspectRatio) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.AspectRatio = aspectRatio; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_GetAspectRatio(UUID entityId, out float aspectRatio) { CameraComponent* camera = GetComponentSafe(entityId); aspectRatio = camera.Camera.AspectRatio; } [RegisterCall, CallingConvention(.Cdecl)] static void Camera_SetFixedAspectRatio(UUID entityId, bool fixedAspectRatio) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.FixedAspectRatio = fixedAspectRatio; } [RegisterCall, CallingConvention(.Cdecl)] 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, CallingConvention(.Cdecl)] static void Physics2D_GetGravity(out float2 gravity) { Scene scene = ScriptEngine.Context; gravity = scene.Physics2DSettings.Gravity; } [RegisterCall, CallingConvention(.Cdecl)] static void Physics2D_SetGravity(in float2 gravity) { Scene scene = ScriptEngine.Context; scene.Physics2DSettings.Gravity = gravity; } #endregion Physics2D // TODO: Do we even need the circle renderer? #region CircleRenderer [RegisterCall, CallingConvention(.Cdecl)] static void CircleRenderer_GetColor(UUID entityId, out ColorRGBA color) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); color = circleRenderer.Color; } [RegisterCall, CallingConvention(.Cdecl)] static void CircleRenderer_SetColor(UUID entityId, ColorRGBA color) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); circleRenderer.Color = color; } [RegisterCall, CallingConvention(.Cdecl)] static void CircleRenderer_GetUvTransform(UUID entityId, out float4 uvTransform) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); uvTransform = circleRenderer.UvTransform; } [RegisterCall, CallingConvention(.Cdecl)] static void CircleRenderer_SetUvTransform(UUID entityId, float4 uvTransform) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); circleRenderer.UvTransform = uvTransform; } [RegisterCall, CallingConvention(.Cdecl)] static void CircleRenderer_GetInnerRadius(UUID entityId, out float innerRadius) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); innerRadius = circleRenderer.InnerRadius; } [RegisterCall, CallingConvention(.Cdecl)] static void CircleRenderer_SetInnerRadius(UUID entityId, float innerRadius) { CircleRendererComponent* circleRenderer = GetComponentSafe(entityId); circleRenderer.InnerRadius = innerRadius; } #endregion #region SpriteRenderer [RegisterCall, CallingConvention(.Cdecl)] static EngineResult SpriteRenderer_GetColor(UUID entityId, out ColorRGBA color) { color = ?; SpriteRendererComponent* spriteRenderer = GetComponentOrReturn!(entityId); color = spriteRenderer.Color; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult SpriteRenderer_SetColor(UUID entityId, ColorRGBA color) { SpriteRendererComponent* spriteRenderer = GetComponentOrReturn!(entityId); spriteRenderer.Color = color; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult SpriteRenderer_GetUvTransform(UUID entityId, out float4 uvTransform) { uvTransform = ?; SpriteRendererComponent* spriteRenderer = GetComponentOrReturn!(entityId); uvTransform = spriteRenderer.UvTransform; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult SpriteRenderer_SetUvTransform(UUID entityId, float4 uvTransform) { SpriteRendererComponent* spriteRenderer = GetComponentOrReturn!(entityId); spriteRenderer.UvTransform = uvTransform; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult SpriteRenderer_GetMaterial(UUID entityId, out AssetHandle assetId) { assetId = ?; SpriteRendererComponent* spriteRenderer = GetComponentOrReturn!(entityId); Material material = GetAssetOrReturn!((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; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult SpriteRenderer_SetMaterial(UUID entityId, AssetHandle assetId) { SpriteRendererComponent* spriteRenderer = GetComponentOrReturn!(entityId); spriteRenderer.Material = assetId; return .Ok; } #endregion #region TextRenderer [RegisterCall(EngineResultAsBool = true), CallingConvention(.Cdecl)] static EngineResult TextRenderer_GetIsRichText(UUID entityId) { TextRendererComponent* textComponent = GetComponentOrReturn!(entityId); return textComponent.IsRichText ? .Ok : .False; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult TextRenderer_SetIsRichText(UUID entityId, bool isRichText) { TextRendererComponent* textComponent = GetComponentOrReturn!(entityId); textComponent.IsRichText = isRichText; textComponent.NeedsRebuild = true; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult TextRenderer_GetText(UUID entityId, out StringView text) { text = ?; TextRendererComponent* textComponent = GetComponentOrReturn!(entityId); text = textComponent.Text; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult TextRenderer_SetText(UUID entityId, StringView text) { TextRendererComponent* textComponent = GetComponentOrReturn!(entityId); textComponent.Text = text; textComponent.NeedsRebuild = true; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult TextRenderer_GetColor(UUID entityId, out ColorRGBA color) { color = ?; TextRendererComponent* textComponent = GetComponentOrReturn!(entityId); color = textComponent.Color; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult TextRenderer_SetColor(UUID entityId, ColorRGBA color) { TextRendererComponent* textComponent = GetComponentOrReturn!(entityId); textComponent.Color = color; textComponent.NeedsRebuild = true; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult TextRenderer_GetHorizontalAlignment(UUID entityId, [GlueParam("out HorizontalTextAlignment")] out HorizontalTextAlignment horizontalAlignment) { horizontalAlignment = ?; TextRendererComponent* textComponent = GetComponentOrReturn!(entityId); horizontalAlignment = textComponent.HorizontalAlignment; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult TextRenderer_SetHorizontalAlignment(UUID entityId, [GlueParam("HorizontalTextAlignment")] HorizontalTextAlignment horizontalAlignment) { TextRendererComponent* textComponent = GetComponentOrReturn!(entityId); textComponent.HorizontalAlignment = horizontalAlignment; textComponent.NeedsRebuild = true; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult TextRenderer_GetFontSize(UUID entityId, out float fontSize) { fontSize = ?; TextRendererComponent* textComponent = GetComponentOrReturn!(entityId); fontSize = textComponent.FontSize; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult TextRenderer_SetFontSize(UUID entityId, float fontSize) { TextRendererComponent* textComponent = GetComponentOrReturn!(entityId); textComponent.FontSize = fontSize; textComponent.NeedsRebuild = true; return .Ok; } #endregion #region Mesh #endregion #region MeshRenderer [RegisterCall, CallingConvention(.Cdecl)] static EngineResult MeshRenderer_GetMaterial(UUID entityId, out AssetHandle assetId) { assetId = ?; MeshRendererComponent* meshRenderer = GetComponentOrReturn!(entityId); Material material = GetAssetOrReturn!((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; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult MeshRenderer_GetSharedMaterial(UUID entityId, out AssetHandle assetId) { assetId = ?; MeshRendererComponent* meshRenderer = GetComponentOrReturn!(entityId); Material material = GetAssetOrReturn!((AssetHandle)meshRenderer.Material); if (material.IsRuntimeInstance) { assetId = material.Parent.Handle; } else { assetId = meshRenderer.Material; } return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult MeshRenderer_SetMaterial(UUID entityId, AssetHandle assetId) { MeshRendererComponent* meshRenderer = GetComponentOrReturn!(entityId); meshRenderer.Material = assetId; return .Ok; } #endregion #region Math [RegisterCall, CallingConvention(.Cdecl)] static float Math_ModfFloat(float x, out float integerPart) { return modf(x, out integerPart); } [RegisterCall, CallingConvention(.Cdecl)] static float2 Math_ModfFloat2(float2 x, out float2 integerPart) { return modf(x, out integerPart); } [RegisterCall, CallingConvention(.Cdecl)] static float3 Math_ModfFloat3(float3 x, out float3 integerPart) { return modf(x, out integerPart); } [RegisterCall, CallingConvention(.Cdecl)] static float4 Math_ModfFloat4(float4 x, out float4 integerPart) { return modf(x, out integerPart); } #endregion [RegisterCall, CallingConvention(.Cdecl)] static void UUID_Create(out UUID id) { id = UUID.Create(); } #region Application [RegisterCall, CallingConvention(.Cdecl)] static bool Application_IsEditor() { return ScriptEngine.ApplicationInfo.IsEditor; } [RegisterCall, CallingConvention(.Cdecl)] static bool Application_IsPlayer() { return ScriptEngine.ApplicationInfo.IsPlayer; } [RegisterCall, CallingConvention(.Cdecl)] static bool Application_IsInEditMode() { return ScriptEngine.ApplicationInfo.IsInEditMode; } [RegisterCall, CallingConvention(.Cdecl)] static bool Application_IsInPlayMode() { return ScriptEngine.ApplicationInfo.IsInPlayMode; } #endregion #region Serialization [RegisterCall, CallingConvention(.Cdecl)] static void Serialization_SerializeField(void* serializationContext, SerializationType type, StringView fieldName, void* valueObject, StringView fullTypeName) { Debug.Profiler.ProfileFunction!(); SerializedObject context = Internal.UnsafeCastToObject(serializationContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); context.AddField(fieldName, type, valueObject, fullTypeName); } [RegisterCall, CallingConvention(.Cdecl)] static void Serialization_CreateObject(void* currentContext, bool isStatic, StringView typeName, out void* newContext, out UUID newId) { Debug.Profiler.ProfileFunction!(); SerializedObject context = Internal.UnsafeCastToObject(currentContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); SerializedObject newObject = new SerializedObject(context.Serializer, isStatic, typeName); newContext = Internal.UnsafeCastToPtr(newObject); newId = newObject.Id; } [RegisterCall, CallingConvention(.Cdecl)] static void Serialization_DeserializeField(void* internalContext, SerializationType expectedType, StringView fieldName, uint8* target, out SerializationType actualType) { SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); context.GetField(fieldName, expectedType, target, out actualType); } [RegisterCall, CallingConvention(.Cdecl)] 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, CallingConvention(.Cdecl)] static void Serialization_GetObjectTypeName(void* internalContext, out StringView fullTypeName) { SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); fullTypeName = 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 void SetExceptionMessage(StringView message) { _lastExceptionMessage.Set(message); } static mixin GetAssetOrReturn(UUID assetId) where T : Asset { AssetHandle handle = .(assetId); T asset = Content.GetAsset(handle, blocking: true); if (asset == null) { SetExceptionMessage(scope $"Asset (Id: {assetId}) not found."); return EngineResult.AssetNotFound; } asset } static mixin GetAssetOrReturn(AssetHandle assetHandle) where T : Asset { T asset = Content.GetAsset(assetHandle, blocking: true); if (asset == null) { SetExceptionMessage(scope $"Asset (Handle: {assetHandle}) not found."); return EngineResult.AssetNotFound; } asset } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Asset_GetIdentifier(UUID assetId, out StringView identifier) { identifier = ?; Asset asset = GetAssetOrReturn!(assetId); identifier = asset.Identifier; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Asset_SetIdentifier(UUID assetId, StringView identifier) { return .NotImplemented; //AssetHandle handle = .(assetId); //Asset asset = Content.GetAsset(handle, blocking: true); //ThrowNotImplementedException("Asset.GetIdentifier is not implemented."); /* // TODO: I think it's not THAT easy. asset.Identifier = StringView(rawText); */ } #endregion #region Material [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Material_SetVariable(AssetHandle assetHandle, StringView variableName, ShaderVariableType elementType, int32 rows, int32 columns, int32 arrayLength, void* rawData, int32 dataLength) { Material material = GetAssetOrReturn!(assetHandle); if (material.[Friend]SetVariableRaw(variableName, elementType, rows, columns , arrayLength, Span((uint8*)rawData, dataLength)) case .Err(let error)) { switch (error) { case .VariableNotFound: SetExceptionMessage(scope $"Material \"{material.Identifier}\" has no variable \"{variableName}\"."); return .ArgumentError; case .ElementTypeMismatch: SetExceptionMessage(scope $"Variable \"{variableName}\" has incompatible element type."); return .ArgumentError; case .MatrixDimensionMismatch: SetExceptionMessage(scope $"Variable \"{variableName}\" has incompatible matrix dimension type."); return .ArgumentError; case .ProvidedBufferTooShort: SetExceptionMessage(scope $"The provided data buffer for variable \"{variableName}\" is not large enough."); } return .Error; } return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Material_ResetVariable(AssetHandle assetHandle, StringView variableName) { Material material = GetAssetOrReturn!(assetHandle); material.ResetVariable(variableName); return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Material_SetTexture(AssetHandle materialHandle, StringView variableName, AssetHandle textureHandle) { Material material = GetAssetOrReturn!(materialHandle); material.SetTexture(variableName, textureHandle); return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Material_GetTexture(AssetHandle materialHandle, StringView variableName, out AssetHandle textureHandle) { textureHandle = ?; Material material = GetAssetOrReturn!(materialHandle); var v = material.GetTexture(variableName, ?); if (v case .Err(let err)) { return .Error; } textureHandle = v.Value; return .Ok; } [RegisterCall, CallingConvention(.Cdecl)] static EngineResult Material_ResetTexture(AssetHandle materialHandle, StringView variableName) { Material material = GetAssetOrReturn!(materialHandle); material.ResetTexture(variableName); return .Ok; } #endregion }