diff --git a/Engine.Core/Abstract/ICamera.cs b/Engine.Core/Abstract/ICamera.cs new file mode 100644 index 0000000..850bce9 --- /dev/null +++ b/Engine.Core/Abstract/ICamera.cs @@ -0,0 +1,17 @@ +namespace Engine.Core; + +/// +/// Represents a camera with view and projections matrices in the engine. +/// +public interface ICamera +{ + /// + /// View of the . + /// + Matrix4x4 ViewMatrix { get; } + + /// + /// Projection of the . + /// + Matrix4x4 ProjectionMatrix { get; } +} diff --git a/Engine.Core/Abstract/ICamera2D.cs b/Engine.Core/Abstract/ICamera2D.cs index 4c12105..941fcd5 100644 --- a/Engine.Core/Abstract/ICamera2D.cs +++ b/Engine.Core/Abstract/ICamera2D.cs @@ -3,7 +3,7 @@ namespace Engine.Core; /// /// Represents a 2D camera in the engine. /// -public interface ICamera2D : IBehaviour2D +public interface ICamera2D : ICamera, IBehaviour2D { /// /// The zoom level of the camera. diff --git a/Engine.Core/Abstract/ICamera3D.cs b/Engine.Core/Abstract/ICamera3D.cs index a0024a0..b2346bb 100644 --- a/Engine.Core/Abstract/ICamera3D.cs +++ b/Engine.Core/Abstract/ICamera3D.cs @@ -3,7 +3,7 @@ namespace Engine.Core; /// /// Represents a 3D camera in the engine. /// -public interface ICamera3D : IBehaviour3D +public interface ICamera3D : ICamera, IBehaviour3D { /// /// Event triggered when the near plane of the changes. diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs index 6811eb9..7b0a180 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs @@ -7,31 +7,39 @@ namespace Engine.Integration.MonoGame; public class MonoGameCamera2D : Behaviour, ICamera2D, IFirstFrameUpdate, ILastFrameUpdate, IPreDraw { - public Event OnMatrixTransformChanged { get; } = new(); + public Event OnViewMatrixChanged { get; } = new(); + public Event OnProjectionMatrixChanged { get; } = new(); public Event OnViewportChanged { get; } = new(); public Event OnZoomChanged { get; } = new(); public GraphicsDeviceManager Graphics { get; private set; } = null!; public ITransform2D Transform { get; private set; } = null!; - public Matrix MatrixTransform + public Matrix4x4 ProjectionMatrix { get; - set + private set { if (field == value) return; field = value; - OnMatrixTransformChanged.Invoke(this); + OnProjectionMatrixChanged.Invoke(this); } - } = Matrix.Identity; + } = Matrix4x4.Identity; - public Vector2D Position + public Matrix4x4 ViewMatrix { - get => Transform.Position; - set => Transform.Position = value; - } + get; + private set + { + if (field == value) + return; + + field = value; + OnViewMatrixChanged.Invoke(this); + } + } = Matrix4x4.Identity; public Viewport Viewport { @@ -61,21 +69,15 @@ public class MonoGameCamera2D : Behaviour, ICamera2D, IFirstFrameUpdate, ILastFr } } = 1f; - public float Rotation - { - get => Transform.Rotation; - set => Transform.Rotation = value; - } - // TODO This causes delay since OnPreDraw calls assuming this is called in in Update public Vector2D ScreenToWorldPosition(Vector2D screenPosition) { - Vector2D worldPosition = Vector2.Transform(screenPosition.ToVector2(), Matrix.Invert(MatrixTransform)).ToVector2D(); + Vector2D worldPosition = Vector2.Transform(screenPosition.ToVector2(), ViewMatrix.Inverse.ToXnaMatrix()).ToVector2D(); return worldPosition.Scale(EngineConverterExtensions.screenScale); } public Vector2D WorldToScreenPosition(Vector2D worldPosition) { - Vector2D screenPosition = Vector2.Transform(worldPosition.ToVector2(), MatrixTransform).ToVector2D(); + Vector2D screenPosition = Vector2.Transform(worldPosition.ToVector2(), ViewMatrix.ToXnaMatrix()).ToVector2D(); return screenPosition.Scale(EngineConverterExtensions.screenScale); } @@ -89,11 +91,12 @@ public class MonoGameCamera2D : Behaviour, ICamera2D, IFirstFrameUpdate, ILastFr public void PreDraw() { - MatrixTransform = - Matrix.CreateTranslation(new Vector3(-Position.X, Position.Y, 0f)) * - Matrix.CreateRotationZ(Rotation * Math.DegreeToRadian) * - Matrix.CreateScale(Transform.Scale.X.Max(Transform.Scale.Y)) * - Matrix.CreateScale(Zoom) * - Matrix.CreateTranslation(new Vector3(Viewport.Width * .5f, Viewport.Height * .5f, 0f)); + ProjectionMatrix = Matrix.CreateOrthographicOffCenter(Viewport.X, Viewport.Width, Viewport.Height, Viewport.Y, 0, 1).FromXnaMatrix(); + ViewMatrix = + Matrix4x4.CreateTranslation(new Vector3D(-Transform.Position.X, Transform.Position.Y, 0f)) * + Matrix4x4.CreateRotationZ(Transform.Rotation * Math.DegreeToRadian) * + Matrix4x4.CreateScale(Transform.Scale.X.Max(Transform.Scale.Y)) * + Matrix4x4.CreateScale(Zoom) * + Matrix4x4.CreateTranslation(new Vector3D(Viewport.Width * .5f, Viewport.Height * .5f, 0f)); } } diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs index 5516551..1784c85 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs @@ -21,7 +21,7 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, ILastFr public GraphicsDeviceManager Graphics { get; private set; } = null!; public ITransform3D Transform { get; private set; } = null!; - public Matrix View + public Matrix4x4 ViewMatrix { get; set @@ -29,13 +29,13 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, ILastFr if (field == value) return; - Matrix previousView = field; + Matrix4x4 previousView = field; field = value; OnViewChanged.Invoke(this, new(previousView)); } - } = Matrix.Identity; + } = Matrix4x4.Identity; - public Matrix Projection + public Matrix4x4 ProjectionMatrix { get; set @@ -43,11 +43,11 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, ILastFr if (field == value) return; - Matrix previousProjection = field; + Matrix4x4 previousProjection = field; field = value; OnProjectionChanged.Invoke(this, new(previousProjection)); } - } = Matrix.Identity; + } = Matrix4x4.Identity; public Viewport Viewport { @@ -111,14 +111,17 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, ILastFr Vector3 nearPoint = new(screenPosition.X, screenPosition.Y, 0f); Vector3 farPoint = new(screenPosition.X, screenPosition.Y, 1f); - Vector3 worldNear = Viewport.Unproject(nearPoint, Projection, View, Matrix.Identity); - Vector3 worldFar = Viewport.Unproject(farPoint, Projection, View, Matrix.Identity); + Matrix projection = ProjectionMatrix.ToXnaMatrix(); + Matrix view = ViewMatrix.ToXnaMatrix(); + + Vector3 worldNear = Viewport.Unproject(nearPoint, projection, view, Matrix.Identity); + Vector3 worldFar = Viewport.Unproject(farPoint, projection, view, Matrix.Identity); Vector3 direction = Vector3.Normalize(worldFar - worldNear); return new(worldNear.ToVector3D(), direction.ToVector3D()); } - public Vector2D WorldToScreenPosition(Vector3D worldPosition) => Viewport.Project(worldPosition.ToVector3(), Projection, View, Matrix.Identity).ToVector3D(); + public Vector2D WorldToScreenPosition(Vector3D worldPosition) => Viewport.Project(worldPosition.ToVector3(), ProjectionMatrix.ToXnaMatrix(), ViewMatrix.ToXnaMatrix(), Matrix.Identity).ToVector3D(); public void LastActiveFrame() => Transform.OnTransformUpdated.RemoveListener(SetDirtyOnTransformUpdate); public void FirstActiveFrame() @@ -150,7 +153,7 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, ILastFr Vector3 right = Vector3.Normalize(Transform.Right.ToVector3()); Vector3 position = Transform.Position.ToVector3(); - View = new Matrix( + ViewMatrix = new Matrix4x4( right.X, up.X, forward.X, 0, right.Y, up.Y, forward.Y, 0, right.Z, up.Z, forward.Z, 0, @@ -166,7 +169,7 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, ILastFr float yScale = 1f / (float)Math.Tan(fieldOfView / 2f); float xScale = yScale / Viewport.AspectRatio; - Projection = new Matrix( + ProjectionMatrix = new Matrix4x4( xScale, 0, 0, 0, 0, yScale, 0, 0, 0, 0, FarPlane / (FarPlane - NearPlane), 1, @@ -174,7 +177,7 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, ILastFr ); } - public readonly record struct ViewChangedArguments(Matrix PreviousView); - public readonly record struct ProjectionChangedArguments(Matrix PreviousProjection); + public readonly record struct ViewChangedArguments(Matrix4x4 PreviousView); + public readonly record struct ProjectionChangedArguments(Matrix4x4 PreviousProjection); public readonly record struct ViewportChangedArguments(Viewport PreviousViewport); } diff --git a/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs b/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs index 354b196..f30cc0c 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs @@ -49,6 +49,14 @@ public static class EngineConverterExtensions m.M41, m.M42, m.M43, m.M44 ); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix4x4 FromXnaMatrix(this Matrix m) => new( + m.M11, m.M12, m.M13, m.M14, + m.M21, m.M22, m.M23, m.M24, + m.M31, m.M32, m.M33, m.M34, + m.M41, m.M42, m.M43, m.M44 + ); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Microsoft.Xna.Framework.Quaternion ToXnaQuaternion(this Core.Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);