diff --git a/GlitchyEngine/src/World/Components.bf b/GlitchyEngine/src/World/Components.bf index 7e385c4..8d43483 100644 --- a/GlitchyEngine/src/World/Components.bf +++ b/GlitchyEngine/src/World/Components.bf @@ -297,12 +297,13 @@ namespace GlitchyEngine.World private String TypeName = null; - typealias CreateInstanceDelegate = function Instance(void* typeNamePtr, int32 typeNameLength, EcsEntity entity); - typealias InstanceDelegate = function void(Instance instance); + typealias CreateInstanceDelegate = function Instance(void* typeNamePtr, int32 typeNameLength, EcsEntity entity, void* scene); + typealias UpdateInstanceDelegate = function void(Instance instance, uint64 frameCount, TimeSpan totalTime, TimeSpan frameTime); + typealias DestroyInstanceDelegate = function void(Instance instance); public static CreateInstanceDelegate CreateInstanceFn; - public static InstanceDelegate UpdateInstanceFn; - public static InstanceDelegate DestroyInstanceFn; + public static UpdateInstanceDelegate UpdateInstanceFn; + public static DestroyInstanceDelegate DestroyInstanceFn; public const Self ss = Self(); @@ -311,14 +312,16 @@ namespace GlitchyEngine.World TypeName = new String(dotNetAssemblyQualifiedTypeName); } - internal void CreateInstance(EcsEntity entity) mut + internal void CreateInstance(EcsEntity entity, Scene scene) mut { - InstanceHandlePtr = CreateInstanceFn(TypeName.Ptr, (int32)TypeName.Length, entity); + void* scenePtr = Internal.UnsafeCastToPtr(scene); + + InstanceHandlePtr = CreateInstanceFn(TypeName.Ptr, (int32)TypeName.Length, entity, scenePtr); } - internal void UpdateInstance() + internal void UpdateInstance(GameTime gameTime) { - UpdateInstanceFn(InstanceHandlePtr); + UpdateInstanceFn(InstanceHandlePtr, gameTime.FrameCount, gameTime.TotalTime, gameTime.FrameTime); } internal void DestroyInstance() @@ -330,5 +333,23 @@ namespace GlitchyEngine.World { delete TypeName; } + + [Export(), LinkName("GE_DoStuff")] + public static int32 DoStuff() + { + return 69; + } + + [Export, LinkName("GE_DotNetScript_GetComponent")] + private static void* GetComponent(EcsEntity entity, void* scenePtr) + { + Scene scene = (Scene)Internal.UnsafeCastToObject(scenePtr); + + Entity e = .(entity, scene); + + TransformComponent* ptr = e.GetComponent(); + + return ptr; + } } } \ No newline at end of file diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index a48635f..b8b3a96 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -59,10 +59,10 @@ namespace GlitchyEngine.World { if (script.InstanceHandlePtr == 0) { - script.[Friend]CreateInstance(entity); + script.[Friend]CreateInstance(entity, this); } - script.[Friend]UpdateInstance(); // TODO: Gametime + script.[Friend]UpdateInstance(gameTime); } Camera* primaryCamera = null; diff --git a/GlitchyEngine/src/World/TransformComponent.bf b/GlitchyEngine/src/World/TransformComponent.bf index 747de1e..e928cdb 100644 --- a/GlitchyEngine/src/World/TransformComponent.bf +++ b/GlitchyEngine/src/World/TransformComponent.bf @@ -1,7 +1,9 @@ +using System; using GlitchyEngine.Math; namespace GlitchyEngine.World { + [Ordered] public struct TransformComponent { EcsEntity _parent = .InvalidEntity; diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/Components/TransformComponent.cs b/GlitchyEngineHelper/DotNetScriptingHelper/Components/TransformComponent.cs new file mode 100644 index 0000000..03fdbee --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/Components/TransformComponent.cs @@ -0,0 +1,229 @@ +using System.Numerics; +using System.Runtime.InteropServices; + +namespace DotNetScriptingHelper.Components; + +[GameComponent, StructLayout(LayoutKind.Sequential, Pack = 1)] +public struct LameTransformComponent +{ + private EcsEntity _parent; + + private Vector3 _position; + private Quaternion _rotation; + private Vector3 _scale; + + private Vector3 _editorRotationEuler; + + private Matrix4x4 _localTransform; + private bool _isDirty; + private Matrix4x4 _worldTransform; + + private UIntPtr _frame; + + public EcsEntity Parent + { + get => _parent; + set + { + if (_parent == value) + return; + + _parent = value; + _isDirty = true; + } + } + + public Matrix4x4 LocalTransform + { + get => _localTransform; + set + { + if (_localTransform == value) + return; + + _localTransform = value; + + Matrix4x4.Decompose(_localTransform, out _position, out _rotation, out _scale); + _isDirty = true; + } + } + // Todo: WorldTransform? + + public Vector3 Position + { + get => _position; + set + { + if (_position == value) + return; + + _position = value; + _isDirty = true; + } + } + + public Quaternion Rotation + { + get => _rotation; + set + { + if (_rotation == value) + return; + + _rotation = value; + _isDirty = true; + } + } + + public (Vector3 Axis, float Angle) RotationAxisAngle + { + get => _rotation.ToAxisAngle(); + set + { + Quaternion quat = Quaternion.CreateFromAxisAngle(value.Axis, value.Angle); + + if (_rotation == quat) + return; + + _rotation = quat; + _isDirty = true; + } + } + + public Vector3 RotationEuler + { + get => _rotation.ToEulerAngles(); + set => Rotation = Quaternion.CreateFromYawPitchRoll(value.Y, value.X, value.Z); + } + + public Vector3 Scale + { + get => _scale; + set + { + if (_scale == value) + return; + + _scale = value; + _isDirty = true; + } + } +} + +public unsafe struct TransformComponent +{ + [GameComponent, StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct TransformComponentData + { + internal EcsEntity _parent; + + internal Vector3 _position; + internal Quaternion _rotation; + internal Vector3 _scale; + + internal Vector3 _editorRotationEuler; + + internal Matrix4x4 _localTransform; + internal bool _isDirty; + internal Matrix4x4 _worldTransform; + + internal UIntPtr _frame; + } + + private TransformComponentData* _component; + + internal TransformComponent(IntPtr component) + { + _component = (TransformComponentData*)component; + } + + public EcsEntity Parent + { + get => _component->_parent; + set + { + if (_component->_parent == value) + return; + + _component->_parent = value; + _component->_isDirty = true; + } + } + + public Matrix4x4 LocalTransform + { + get => _component->_localTransform; + set + { + if (_component->_localTransform == value) + return; + + _component->_localTransform = value; + + Matrix4x4.Decompose(_component->_localTransform, + out _component->_position, out _component->_rotation, out _component->_scale); + _component->_isDirty = true; + } + } + // Todo: WorldTransform? + + public Vector3 Position + { + get => _component->_position; + set + { + if (_component->_position == value) + return; + + _component->_position = value; + _component->_isDirty = true; + } + } + + public Quaternion Rotation + { + get => _component->_rotation; + set + { + if (_component->_rotation == value) + return; + + _component->_rotation = value; + _component->_isDirty = true; + } + } + + public (Vector3 Axis, float Angle) RotationAxisAngle + { + get => _component->_rotation.ToAxisAngle(); + set + { + Quaternion quat = Quaternion.CreateFromAxisAngle(value.Axis, value.Angle); + + if (_component->_rotation == quat) + return; + + _component->_rotation = quat; + _component->_isDirty = true; + } + } + + public Vector3 RotationEuler + { + get => _component->_rotation.ToEulerAngles(); + set => Rotation = Quaternion.CreateFromYawPitchRoll(value.Y, value.X, value.Z); + } + + public Vector3 Scale + { + get => _component->_scale; + set + { + if (_component->_scale == value) + return; + + _component->_scale = value; + _component->_isDirty = true; + } + } +} diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/EcsEntity.cs b/GlitchyEngineHelper/DotNetScriptingHelper/EcsEntity.cs index 0a6fd2f..9f7331d 100644 --- a/GlitchyEngineHelper/DotNetScriptingHelper/EcsEntity.cs +++ b/GlitchyEngineHelper/DotNetScriptingHelper/EcsEntity.cs @@ -20,4 +20,14 @@ public readonly struct EcsEntity public bool IsValid => Index != InvalidEntity.Index; public static readonly EcsEntity InvalidEntity = new(uint.MaxValue, 0); + + public static bool operator ==(EcsEntity left, EcsEntity right) + { + return left._id == right._id; + } + + public static bool operator !=(EcsEntity left, EcsEntity right) + { + return left._id != right._id; + } } diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/Entity.cs b/GlitchyEngineHelper/DotNetScriptingHelper/Entity.cs index f54eac1..03d5259 100644 --- a/GlitchyEngineHelper/DotNetScriptingHelper/Entity.cs +++ b/GlitchyEngineHelper/DotNetScriptingHelper/Entity.cs @@ -2,14 +2,15 @@ public struct Entity { - public EcsEntity Handle { get; private set; } - // TODO: Scene + public EcsEntity Handle { get; } + public IntPtr Scene { get; } public bool IsValid => Handle.IsValid; - public Entity(EcsEntity handle) + public Entity(EcsEntity handle, IntPtr scene) { Handle = handle; + Scene = scene; } // TODO: Get Parent, Children diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/GameTime.cs b/GlitchyEngineHelper/DotNetScriptingHelper/GameTime.cs new file mode 100644 index 0000000..08739e4 --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/GameTime.cs @@ -0,0 +1,23 @@ +namespace DotNetScriptingHelper; + +public class GameTime +{ + private ulong _frameCount; + + private TimeSpan _totalTime; + private TimeSpan _frameTime; + + public ulong FrameCount => _frameCount; + public TimeSpan TotalTime => _totalTime; + public TimeSpan FrameTime => _frameTime; + + public float DeltaTime => (float)_frameTime.TotalSeconds; + public float TotalSeconds => (float)_totalTime.TotalSeconds; + + internal GameTime(ulong frameCount, TimeSpan totalTime, TimeSpan frameTime) + { + _frameCount = frameCount; + _totalTime = totalTime; + _frameTime = frameTime; + } +} diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/IGameComponent.cs b/GlitchyEngineHelper/DotNetScriptingHelper/IGameComponent.cs new file mode 100644 index 0000000..67bb42f --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/IGameComponent.cs @@ -0,0 +1,14 @@ +namespace DotNetScriptingHelper; + +public interface IGameComponent +{ + static int I { get; } +} + +public class GameComponentAttribute : Attribute +{ + public GameComponentAttribute() + { + + } +} diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/QuaternionExtensions.cs b/GlitchyEngineHelper/DotNetScriptingHelper/QuaternionExtensions.cs new file mode 100644 index 0000000..6b8195a --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/QuaternionExtensions.cs @@ -0,0 +1,68 @@ +using System.Numerics; + +namespace DotNetScriptingHelper; + +public static class QuaternionExtensions +{ + public static (Vector3 Axis, float Angle) ToAxisAngle(this Quaternion quat) + { + // scalar part = cos(θ/2) + // So, we can extract the angle directly. + float angle = 2.0f * MathF.Acos(quat.W); + + // vector part = axis * sin(θ/2) + // In other words, the vector part is the axis, but with length of sin(θ/2). + // We assume quaternion is unit length, so subtracting w^2 gives us length of just vector part (aka sin(θ/2)). + float length = MathF.Sqrt(1.0f - (quat.W * quat.W)); + + Vector3 axis; + + // Normalize vector part to get the axis! + if (length == 0) + { + axis = Vector3.Zero; + } + else + { + length = 1.0f / length; + axis.X = quat.X * length; + axis.Y = quat.Y * length; + axis.Z = quat.Z * length; + } + + return (axis, angle); + } + + public static Vector3 ToEulerAngles(this Quaternion q) + { + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/ + + Vector3 result; + + float sqw = q.W * q.W; + float sqx = q.X * q.X; + float sqy = q.Y * q.Y; + float sqz = q.Z * q.Z; + float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor + float test = q.X * q.Y + q.Z * q.W; + if (test > 0.4999f * unit) + { // singularity at north pole + result.Y = 2.0f * MathF.Atan2(q.X, q.W); + result.Z = MathF.PI / 2.0f; + result.X = 0.0f; + return result; + } + if (test < -0.4999f * unit) + { // singularity at south pole + result.Y = -2.0f * MathF.Atan2(q.X, q.W); + result.Z = -MathF.PI / 2.0f; + result.X = 0.0f; + return result; + } + result.Y = MathF.Atan2(2 * q.Y * q.W - 2 * q.X * q.Z, sqx - sqy - sqz + sqw); + result.Z = MathF.Asin(2 * test / unit); + result.X = MathF.Atan2(2 * q.X * q.W - 2 * q.Y * q.Z, -sqx + sqy - sqz + sqw); + + return result; + } +} diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/ScriptableEntity.cs b/GlitchyEngineHelper/DotNetScriptingHelper/ScriptableEntity.cs index f5ea15f..3a2c257 100644 --- a/GlitchyEngineHelper/DotNetScriptingHelper/ScriptableEntity.cs +++ b/GlitchyEngineHelper/DotNetScriptingHelper/ScriptableEntity.cs @@ -1,5 +1,7 @@ using System.Diagnostics; +using System.Reflection; using System.Runtime.InteropServices; +using DotNetScriptingHelper.Components; namespace DotNetScriptingHelper; @@ -9,10 +11,10 @@ public abstract class ScriptableEntity public virtual void OnCreate() { } protected virtual void OnDestroy() { } - protected virtual void OnUpdate() { } + protected virtual void OnUpdate(GameTime gameTime) { } [UnmanagedCallersOnly] - public static IntPtr CreateInstance(IntPtr typeNamePtr, int typeNameLength, EcsEntity entity) + public static IntPtr CreateInstance(IntPtr typeNamePtr, int typeNameLength, EcsEntity entity, IntPtr scene) { try { @@ -31,7 +33,7 @@ public abstract class ScriptableEntity if (instance is not ScriptableEntity scriptableEntity) return IntPtr.Zero; - scriptableEntity.Entity = new Entity(entity); + scriptableEntity.Entity = new Entity(entity, scene); scriptableEntity.OnCreate(); GCHandle handle = GCHandle.Alloc(instance); @@ -58,13 +60,39 @@ public abstract class ScriptableEntity } [UnmanagedCallersOnly] - public static void UpdateEntity(IntPtr entityPtr) + public static void UpdateEntity(IntPtr entityPtr, ulong frameCount, TimeSpan totalTime, TimeSpan frameTime) { GCHandle entityHandle = GCHandle.FromIntPtr(entityPtr); if (entityHandle.Target is ScriptableEntity entity) { - entity.OnUpdate(); + GameTime gameTime = new GameTime(frameCount, totalTime, frameTime); + + entity.OnUpdate(gameTime); } } + + [DllImport("GlitchyEditor.exe", EntryPoint = "GE_DoStuff")] + static extern int DoStuff(); + + [DllImport("GlitchyEditor.exe", EntryPoint = "GE_DotNetScript_GetComponent")] + private static extern IntPtr GetComponent(EcsEntity entity, IntPtr scenePtr); + + protected TransformComponent GetTransform()// where T : struct + { + IntPtr component = GetComponent(Entity.Handle, Entity.Scene); + + return new TransformComponent(component); + + //unsafe + //{ + // TransformComponent* transform = (TransformComponent*)component; + + // return ref *transform; + //} + + //GameComponentAttribute? attribute = typeof(T).GetCustomAttribute(); + + //Debug.Assert(attribute != null, $"Requested type {typeof(T)} is not a component."); + } } diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/TestEntityScript.cs b/GlitchyEngineHelper/DotNetScriptingHelper/TestEntityScript.cs index 2f3bdac..80d56c4 100644 --- a/GlitchyEngineHelper/DotNetScriptingHelper/TestEntityScript.cs +++ b/GlitchyEngineHelper/DotNetScriptingHelper/TestEntityScript.cs @@ -1,13 +1,39 @@ -using System.Diagnostics; +using System.Numerics; +using DotNetScriptingHelper.Components; namespace DotNetScriptingHelper; public class TestEntityScript : ScriptableEntity { - public int Inty = 1337; + private TransformComponent _transform; - protected override void OnUpdate() + public override void OnCreate() { - Debug.WriteLine($"My Number is {Inty++}"); + _transform = GetTransform(); + } + + protected override void OnUpdate(GameTime gameTime) + { + float f = (float)Math.Sin(gameTime.TotalSeconds); + + Vector3 pos = _transform.Position; + + pos.Y = f; + + _transform.Position = pos; + + float fx = MathF.Sin(gameTime.TotalSeconds * 2) / 2 + 1; + float fy = MathF.Cos(gameTime.TotalSeconds * 2) / 2 + 1; + + Vector3 scl = _transform.Scale; + + scl.X = fx; + scl.Y = fy; + + _transform.Scale = scl; + + float fr = MathF.Cos(gameTime.TotalSeconds / 4) * MathF.PI * 10; + + _transform.Rotation = Quaternion.CreateFromYawPitchRoll(0, 0, fr); } }