Files
Syntriax.Engine/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs
Syntriax 988a6f67f2 BREAKING CHANGE: renamed original Behaviour class to BehaviourInternal, and replaced it with BehaviourBase
Original Behaviour was using old methods for detecting entering/exiting universe,
they are now all under the same hood and the original is kept for UniverseEntranceManager
because it needs to enter the universe without itself. The internal behaviour kept under
a subnamespace of "Core.Internal" for the purpose that it might come in handy for other use cases.
2025-10-22 16:50:19 +03:00

132 lines
4.3 KiB
C#

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Engine.Core;
namespace Engine.Integration.MonoGame;
public class MonoGameCamera3D : Behaviour, 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);
}