123 lines
3.6 KiB
C#
123 lines
3.6 KiB
C#
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
using Engine.Core;
|
|
|
|
namespace Engine.Integration.MonoGame;
|
|
|
|
public class MonoGameCamera2D : Behaviour, ICamera2D, IFirstFrameUpdate, ILastFrameUpdate, IPreDraw
|
|
{
|
|
public Event<MonoGameCamera2D> OnViewMatrixChanged { get; } = new();
|
|
public Event<MonoGameCamera2D> OnProjectionMatrixChanged { get; } = new();
|
|
public Event<MonoGameCamera2D> OnViewportChanged { get; } = new();
|
|
public Event<MonoGameCamera2D> OnZoomChanged { get; } = new();
|
|
|
|
public GraphicsDeviceManager Graphics { get; private set; } = null!;
|
|
public ITransform2D Transform { get; private set; } = null!;
|
|
|
|
public Matrix4x4 ProjectionMatrix
|
|
{
|
|
get;
|
|
private set
|
|
{
|
|
if (field == value)
|
|
return;
|
|
|
|
field = value;
|
|
OnProjectionMatrixChanged.Invoke(this);
|
|
}
|
|
} = Matrix4x4.Identity;
|
|
|
|
public Matrix4x4 ViewMatrix
|
|
{
|
|
get;
|
|
private set
|
|
{
|
|
if (field == value)
|
|
return;
|
|
|
|
field = value;
|
|
OnViewMatrixChanged.Invoke(this);
|
|
}
|
|
} = Matrix4x4.Identity;
|
|
|
|
public Viewport Viewport
|
|
{
|
|
get;
|
|
set
|
|
{
|
|
if (field.Equals(value))
|
|
return;
|
|
|
|
field = value;
|
|
OnViewportChanged.Invoke(this);
|
|
}
|
|
} = default;
|
|
|
|
public float Zoom
|
|
{
|
|
get;
|
|
set
|
|
{
|
|
float newValue = Math.Max(0.1f, value);
|
|
|
|
if (field == newValue)
|
|
return;
|
|
|
|
field = newValue;
|
|
OnZoomChanged.Invoke(this);
|
|
}
|
|
} = 1f;
|
|
|
|
// TODO This causes delay since OnPreDraw calls assuming this is called in in Update
|
|
public Vector2D ScreenToWorldPosition(Vector2D screenPosition)
|
|
{
|
|
float x = 2f * screenPosition.X / Viewport.Width - 1f;
|
|
float y = 1f - 2f * screenPosition.Y / Viewport.Height;
|
|
Vector4D normalizedCoordinates = new(x, y, 0f, 1f);
|
|
|
|
Matrix4x4 invertedViewProjectionMatrix = (ProjectionMatrix * ViewMatrix).Inverse;
|
|
|
|
Vector4D worldPosition = invertedViewProjectionMatrix * normalizedCoordinates;
|
|
|
|
if (worldPosition.W != 0f)
|
|
worldPosition /= worldPosition.W;
|
|
|
|
return new(worldPosition.X, worldPosition.Y);
|
|
}
|
|
|
|
public Vector2D WorldToScreenPosition(Vector2D worldPosition)
|
|
{
|
|
Vector4D worldPosition4D = new(worldPosition.X, worldPosition.Y, 0f, 1f);
|
|
|
|
Matrix4x4 viewProjection = ProjectionMatrix * ViewMatrix;
|
|
Vector4D clip = viewProjection * worldPosition4D;
|
|
|
|
if (clip.W != 0f)
|
|
clip /= clip.W;
|
|
|
|
float screenX = (clip.X + 1f) * .5f * Viewport.Width;
|
|
float screenY = (1f - clip.Y) * .5f * Viewport.Height;
|
|
|
|
return new(screenX, screenY);
|
|
}
|
|
|
|
public void LastActiveFrame() => Transform = null!;
|
|
public void FirstActiveFrame()
|
|
{
|
|
Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>().Window.Graphics;
|
|
Viewport = Graphics.GraphicsDevice.Viewport;
|
|
Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>();
|
|
}
|
|
|
|
public void PreDraw()
|
|
{
|
|
ProjectionMatrix = Matrix4x4.CreateOrthographicViewCentered(Viewport.Width, Viewport.Height);
|
|
ViewMatrix =
|
|
Matrix4x4.CreateTranslation(new Vector3D(-Transform.Position.X, -Transform.Position.Y, 0f))
|
|
.ApplyRotationZ(Transform.Rotation * Math.DegreeToRadian)
|
|
.ApplyScale(Transform.Scale.X.Max(Transform.Scale.Y))
|
|
.ApplyScale(Zoom);
|
|
}
|
|
}
|