feat: 3D camera added

This commit is contained in:
2025-10-19 00:28:40 +03:00
parent 16344dccc7
commit 61ff0887e2
2 changed files with 157 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
namespace Engine.Core;
/// <summary>
/// Represents a 3D camera in the engine.
/// </summary>
public interface ICamera3D : IBehaviour3D
{
/// <summary>
/// Field of View (FOV) value of the camera
/// </summary>
float FieldOfView { get; set; }
/// <summary>
/// Converts a position from screen coordinates to a <see cref="Ray3D"/>.
/// </summary>
/// <param name="screenPosition">The position in screen coordinates.</param>
/// <returns>The <see cref="Ray3D"/> originating from the camera to the screen position in world coordinates.</returns>
Ray3D ScreenToWorldRay(Vector2D screenPosition);
/// <summary>
/// Converts a position from world coordinates to screen coordinates.
/// </summary>
/// <param name="worldPosition">The position in world coordinates.</param>
/// <returns>The position in screen coordinates.</returns>
Vector2D WorldToScreenPosition(Vector3D worldPosition);
}

View File

@@ -0,0 +1,131 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Engine.Core;
namespace Engine.Integration.MonoGame;
public class MonoGameCamera3D : BehaviourBase, ICamera3D, IFirstFrameUpdate, IPreDraw
{
public Event<MonoGameCamera3D, ViewChangedArguments> OnViewChanged { get; } = new();
public Event<MonoGameCamera3D, ProjectionChangedArguments> OnProjectionChanged { get; } = new();
public Event<MonoGameCamera3D, ViewportChangedArguments> OnViewportChanged { get; } = new();
public Event<MonoGameCamera3D, FieldOfViewChangedArguments> OnFieldOfViewChanged { get; } = new();
private Matrix _view = Matrix.Identity;
private Matrix _projection = Matrix.Identity;
private Viewport _viewport = default;
private float _fieldOfView = 1f;
public GraphicsDeviceManager Graphics { get; private set; } = null!;
public ITransform3D Transform { get; private set; } = null!;
public Matrix View
{
get => _view;
set
{
if (_view == value)
return;
Matrix previousView = _view;
_view = value;
OnViewChanged.Invoke(this, new(previousView));
}
}
public Matrix Projection
{
get => _projection;
set
{
if (_projection == value)
return;
Matrix previousProjection = _projection;
_projection = value;
OnProjectionChanged.Invoke(this, new(previousProjection));
}
}
public Vector3D Position
{
get => Transform.Position;
set => Transform.Position = value;
}
public Viewport Viewport
{
get => _viewport;
set
{
if (_viewport.Equals(value))
return;
Viewport previousViewport = _viewport;
_viewport = value;
OnViewportChanged.Invoke(this, new(previousViewport));
}
}
public float FieldOfView
{
get => _fieldOfView;
set
{
float newValue = Math.Max(0.1f, value);
if (_fieldOfView == newValue)
return;
float previousFieldOfView = _fieldOfView;
_fieldOfView = newValue;
OnFieldOfViewChanged.Invoke(this, new(previousFieldOfView));
}
}
public Engine.Core.Quaternion Rotation
{
get => Transform.Rotation;
set => Transform.Rotation = value;
}
// TODO This causes delay since OnPreDraw calls assuming this is called in in Update
public Ray3D ScreenToWorldRay(Vector2D screenPosition)
{
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);
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 void FirstActiveFrame()
{
Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>().Window.Graphics;
Viewport = Graphics.GraphicsDevice.Viewport;
}
public void PreDraw()
{
Vector3 cameraPosition = Position.ToVector3();
View = Matrix.CreateLookAt(
cameraPosition,
Transform.Forward.ToVector3() + cameraPosition,
Transform.Up.ToVector3()
);
Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Viewport.AspectRatio, 0.1f, 100f);
}
protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour<ITransform3D>();
protected sealed override void FinalizeInternal() => Transform = null!;
public readonly record struct ViewChangedArguments(Matrix PreviousView);
public readonly record struct ProjectionChangedArguments(Matrix PreviousProjection);
public readonly record struct ViewportChangedArguments(Viewport PreviousViewport);
public readonly record struct FieldOfViewChangedArguments(float PreviousFieldOfView);
}