diff --git a/Engine.Core/Abstract/ICamera3D.cs b/Engine.Core/Abstract/ICamera3D.cs
index 01ce185..a0024a0 100644
--- a/Engine.Core/Abstract/ICamera3D.cs
+++ b/Engine.Core/Abstract/ICamera3D.cs
@@ -6,7 +6,32 @@ namespace Engine.Core;
public interface ICamera3D : IBehaviour3D
{
///
- /// Field of View (FOV) value of the camera
+ /// Event triggered when the near plane of the changes.
+ ///
+ Event OnNearPlaneChanged { get; }
+
+ ///
+ /// Event triggered when the far plane of the changes.
+ ///
+ Event OnFarPlaneChanged { get; }
+
+ ///
+ /// Event triggered when the field of view of the changes.
+ ///
+ Event OnFieldOfViewChanged { get; }
+
+ ///
+ /// Near plane distance of the camera.
+ ///
+ float NearPlane { get; set; }
+
+ ///
+ /// Far plane distance of the camera.
+ ///
+ float FarPlane { get; set; }
+
+ ///
+ /// Field of View (FOV) value of the camera in degrees.
///
float FieldOfView { get; set; }
@@ -23,4 +48,8 @@ public interface ICamera3D : IBehaviour3D
/// The position in world coordinates.
/// The position in screen coordinates.
Vector2D WorldToScreenPosition(Vector3D worldPosition);
+
+ readonly record struct NearPlaneChangedArguments(float PreviousNearPlane);
+ readonly record struct FarPlaneChangedArguments(float PreviousFarPlane);
+ readonly record struct FieldOfViewChangedArguments(float PreviousFieldOfView);
}
diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs
index 591ed3b..b43d873 100644
--- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs
+++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs
@@ -5,17 +5,23 @@ using Engine.Core;
namespace Engine.Integration.MonoGame;
-public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, IPreDraw
+public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, ILastFrameUpdate, IPreDraw
{
public Event OnViewChanged { get; } = new();
public Event OnProjectionChanged { get; } = new();
public Event OnViewportChanged { get; } = new();
- public Event OnFieldOfViewChanged { get; } = new();
+
+ public Event OnNearPlaneChanged { get; } = new();
+ public Event OnFarPlaneChanged { get; } = new();
+ public Event OnFieldOfViewChanged { get; } = new();
private Matrix _view = Matrix.Identity;
private Matrix _projection = Matrix.Identity;
private Viewport _viewport = default;
- private float _fieldOfView = 1f;
+ private float _nearPlane = 0.01f;
+ private float _farPlane = 100f;
+ private float _fieldOfView = Math.DegreeToRadian * 70f;
+ private bool isRecalculationNeeded = true;
public GraphicsDeviceManager Graphics { get; private set; } = null!;
public ITransform3D Transform { get; private set; } = null!;
@@ -63,27 +69,53 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, IPreDra
Viewport previousViewport = _viewport;
_viewport = value;
+ SetForRecalculation();
OnViewportChanged.Invoke(this, new(previousViewport));
}
}
+ public float NearPlane
+ {
+ get => _nearPlane;
+ set
+ {
+ float previousNearPlane = _nearPlane;
+ _nearPlane = value.Max(0.0001f);
+ SetForRecalculation();
+ OnNearPlaneChanged.Invoke(this, new(previousNearPlane));
+ }
+ }
+
+ public float FarPlane
+ {
+ get => _farPlane;
+ set
+ {
+ float previousFarPlane = _farPlane;
+ _farPlane = value.Max(NearPlane);
+ SetForRecalculation();
+ OnFarPlaneChanged.Invoke(this, new(previousFarPlane));
+ }
+ }
+
public float FieldOfView
{
- get => _fieldOfView;
+ get => _fieldOfView * Math.RadianToDegree;
set
{
- float newValue = Math.Max(0.1f, value);
+ value = value.Max(0.1f) * Math.DegreeToRadian;
- if (_fieldOfView == newValue)
+ if (_fieldOfView == value)
return;
float previousFieldOfView = _fieldOfView;
- _fieldOfView = newValue;
+ _fieldOfView = value;
+ SetForRecalculation();
OnFieldOfViewChanged.Invoke(this, new(previousFieldOfView));
}
}
- public Engine.Core.Quaternion Rotation
+ public Core.Quaternion Rotation
{
get => Transform.Rotation;
set => Transform.Rotation = value;
@@ -104,28 +136,36 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, IPreDra
public Vector2D WorldToScreenPosition(Vector3D worldPosition) => Viewport.Project(worldPosition.ToVector3(), _projection, _view, Matrix.Identity).ToVector3D();
+ public void LastActiveFrame() => Transform.OnTransformUpdated.RemoveListener(SetDirtyOnTransformUpdate);
public void FirstActiveFrame()
{
+ Transform = BehaviourController.GetRequiredBehaviour();
Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour().Window.Graphics;
Viewport = Graphics.GraphicsDevice.Viewport;
+
+ Transform.OnTransformUpdated.AddListener(SetDirtyOnTransformUpdate);
}
+ private void SetDirtyOnTransformUpdate(ITransform3D sender) => SetForRecalculation();
+ private void SetForRecalculation() => isRecalculationNeeded = true;
+
public void PreDraw()
{
+ if (!isRecalculationNeeded)
+ return;
+
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);
- }
+ Projection = Matrix.CreatePerspectiveFieldOfView(_fieldOfView, Viewport.AspectRatio, 0.1f, 100f);
- protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour();
- protected sealed override void FinalizeInternal() => Transform = null!;
+ isRecalculationNeeded = false;
+ }
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);
}