Compare commits
5 Commits
main
...
developmen
Author | SHA1 | Date | |
---|---|---|---|
981732ff75 | |||
4bdd32b808 | |||
cac69f5f35 | |||
1660915678 | |||
1d02b0eba2 |
@@ -6,7 +6,32 @@ namespace Engine.Core;
|
|||||||
public interface ICamera3D : IBehaviour3D
|
public interface ICamera3D : IBehaviour3D
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Field of View (FOV) value of the camera
|
/// Event triggered when the near plane of the <see cref="ICamera3D"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
Event<ICamera3D, NearPlaneChangedArguments> OnNearPlaneChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the far plane of the <see cref="ICamera3D"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
Event<ICamera3D, FarPlaneChangedArguments> OnFarPlaneChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the field of view of the <see cref="ICamera3D"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
Event<ICamera3D, FieldOfViewChangedArguments> OnFieldOfViewChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Near plane distance of the camera.
|
||||||
|
/// </summary>
|
||||||
|
float NearPlane { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Far plane distance of the camera.
|
||||||
|
/// </summary>
|
||||||
|
float FarPlane { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Field of View (FOV) value of the camera in degrees.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
float FieldOfView { get; set; }
|
float FieldOfView { get; set; }
|
||||||
|
|
||||||
@@ -23,4 +48,8 @@ public interface ICamera3D : IBehaviour3D
|
|||||||
/// <param name="worldPosition">The position in world coordinates.</param>
|
/// <param name="worldPosition">The position in world coordinates.</param>
|
||||||
/// <returns>The position in screen coordinates.</returns>
|
/// <returns>The position in screen coordinates.</returns>
|
||||||
Vector2D WorldToScreenPosition(Vector3D worldPosition);
|
Vector2D WorldToScreenPosition(Vector3D worldPosition);
|
||||||
|
|
||||||
|
readonly record struct NearPlaneChangedArguments(float PreviousNearPlane);
|
||||||
|
readonly record struct FarPlaneChangedArguments(float PreviousFarPlane);
|
||||||
|
readonly record struct FieldOfViewChangedArguments(float PreviousFieldOfView);
|
||||||
}
|
}
|
||||||
|
@@ -110,6 +110,13 @@ public static class Math
|
|||||||
/// <returns>The sine of <paramref name="x"/>.</returns>
|
/// <returns>The sine of <paramref name="x"/>.</returns>
|
||||||
public static float Sin(float x) => MathF.Sin(x);
|
public static float Sin(float x) => MathF.Sin(x);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the tangent of a number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">The angle, in radians.</param>
|
||||||
|
/// <returns>The tangent of <paramref name="x"/>.</returns>
|
||||||
|
public static float Tan(float x) => MathF.Tan(x);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the arccosine of a number.
|
/// Returns the arccosine of a number.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -124,6 +131,13 @@ public static class Math
|
|||||||
/// <returns>The arcsine of <paramref name="x"/>.</returns>
|
/// <returns>The arcsine of <paramref name="x"/>.</returns>
|
||||||
public static float Asin(float x) => MathF.Asin(x);
|
public static float Asin(float x) => MathF.Asin(x);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the angle whose tangent is the specified number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">The tangent value.</param>
|
||||||
|
/// <returns>The angle, in radians.</returns>
|
||||||
|
public static float Atan(float x) => MathF.Atan(x);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the angle whose tangent is the quotient of two specified numbers.
|
/// Returns the angle whose tangent is the quotient of two specified numbers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -32,12 +32,18 @@ public static class MathExtensions
|
|||||||
/// <inheritdoc cref="Math.Sin(float)" />
|
/// <inheritdoc cref="Math.Sin(float)" />
|
||||||
public static float Sin(this float x) => Math.Sin(x);
|
public static float Sin(this float x) => Math.Sin(x);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Math.Tan(float)" />
|
||||||
|
public static float Tan(this float x) => Math.Tan(x);
|
||||||
|
|
||||||
/// <inheritdoc cref="Math.Acos(float)" />
|
/// <inheritdoc cref="Math.Acos(float)" />
|
||||||
public static float Acos(this float x) => Math.Acos(x);
|
public static float Acos(this float x) => Math.Acos(x);
|
||||||
|
|
||||||
/// <inheritdoc cref="Math.Asin(float)" />
|
/// <inheritdoc cref="Math.Asin(float)" />
|
||||||
public static float Asin(this float x) => Math.Asin(x);
|
public static float Asin(this float x) => Math.Asin(x);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Math.Atan(float)" />
|
||||||
|
public static float Atan(this float x) => Math.Atan(x);
|
||||||
|
|
||||||
/// <inheritdoc cref="Math.Atan2(float, float)" />
|
/// <inheritdoc cref="Math.Atan2(float, float)" />
|
||||||
public static float Atan2(this float y, float x) => Math.Atan2(y, x);
|
public static float Atan2(this float y, float x) => Math.Atan2(y, x);
|
||||||
|
|
||||||
|
@@ -156,6 +156,27 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
|
|||||||
/// <returns>The normalized <see cref="Quaternion"/>.</returns>
|
/// <returns>The normalized <see cref="Quaternion"/>.</returns>
|
||||||
public static Quaternion Normalize(Quaternion quaternion) => quaternion / Length(quaternion);
|
public static Quaternion Normalize(Quaternion quaternion) => quaternion / Length(quaternion);
|
||||||
|
|
||||||
|
public static Quaternion LookAt(Vector3D origin, Vector3D target, Vector3D up) => LookAt(target - origin, up);
|
||||||
|
public static Quaternion LookAt(Vector3D target, Vector3D up)
|
||||||
|
{
|
||||||
|
Vector3D forward = target.Normalized;
|
||||||
|
|
||||||
|
if (forward.LengthSquared() < 1e-6f)
|
||||||
|
return Identity;
|
||||||
|
|
||||||
|
Vector3D right = up.Cross(forward).Normalized;
|
||||||
|
Vector3D newUp = forward.Cross(right);
|
||||||
|
|
||||||
|
System.Numerics.Matrix4x4 rot = new(
|
||||||
|
right.X, right.Y, right.Z, 0f,
|
||||||
|
newUp.X, newUp.Y, newUp.Z, 0f,
|
||||||
|
forward.X, forward.Y, forward.Z, 0f,
|
||||||
|
0f, 0f, 0f, 1f
|
||||||
|
);
|
||||||
|
|
||||||
|
return FromRotationMatrix4x4(rot);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rotates a <see cref="Quaternion"/> around a axis by the specified angle (in radians).
|
/// Rotates a <see cref="Quaternion"/> around a axis by the specified angle (in radians).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -212,8 +233,8 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
|
|||||||
dot = -dot;
|
dot = -dot;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dot > 0.9995f)
|
if (dot > 0.999999f)
|
||||||
return Lerp(from, to, t);
|
return to;
|
||||||
|
|
||||||
float angle = Math.Acos(dot);
|
float angle = Math.Acos(dot);
|
||||||
float sinAngle = Math.Sin(angle);
|
float sinAngle = Math.Sin(angle);
|
||||||
@@ -278,8 +299,7 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the <see cref="System.Numerics.Matrix4x4"/> from given <see cref="Quaternion"/>.
|
/// Calculates the <see cref="System.Numerics.Matrix4x4"/> from given <see cref="Quaternion"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="axis">The axis of the rotation in <see cref="Vector3D"/>.</param>
|
/// <param name="quaternion">The rotation <see cref="Quaternion"/>.</param>
|
||||||
/// <param name="angle">The angle in radians.</param>
|
|
||||||
/// <returns>The rotation <see cref="System.Numerics.Matrix4x4"/> calculated by the given <see cref="Quaternion"/>.</returns>
|
/// <returns>The rotation <see cref="System.Numerics.Matrix4x4"/> calculated by the given <see cref="Quaternion"/>.</returns>
|
||||||
public static System.Numerics.Matrix4x4 ToRotationMatrix4x4(Quaternion quaternion)
|
public static System.Numerics.Matrix4x4 ToRotationMatrix4x4(Quaternion quaternion)
|
||||||
{
|
{
|
||||||
@@ -311,6 +331,52 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the <see cref="Quaternion"/> from given <see cref="System.Numerics.Matrix4x4"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="martix">The rotation <see cref="System.Numerics.Matrix4x4"/>.</param>
|
||||||
|
/// <returns>The rotation <see cref="Quaternion"/> calculated by the given <see cref="System.Numerics.Matrix4x4"/>.</returns>
|
||||||
|
public static Quaternion FromRotationMatrix4x4(System.Numerics.Matrix4x4 martix)
|
||||||
|
{
|
||||||
|
float trace = martix.M11 + martix.M22 + martix.M33;
|
||||||
|
float w, x, y, z;
|
||||||
|
|
||||||
|
if (trace > 0)
|
||||||
|
{
|
||||||
|
float s = Math.Sqrt(trace + 1.0f) * 2f;
|
||||||
|
w = .25f * s;
|
||||||
|
x = (martix.M23 - martix.M32) / s;
|
||||||
|
y = (martix.M31 - martix.M13) / s;
|
||||||
|
z = (martix.M12 - martix.M21) / s;
|
||||||
|
}
|
||||||
|
else if ((martix.M11 > martix.M22) && (martix.M11 > martix.M33))
|
||||||
|
{
|
||||||
|
float s = Math.Sqrt(1.0f + martix.M11 - martix.M22 - martix.M33) * 2f;
|
||||||
|
w = (martix.M23 - martix.M32) / s;
|
||||||
|
x = .25f * s;
|
||||||
|
y = (martix.M12 + martix.M21) / s;
|
||||||
|
z = (martix.M31 + martix.M13) / s;
|
||||||
|
}
|
||||||
|
else if (martix.M22 > martix.M33)
|
||||||
|
{
|
||||||
|
float s = Math.Sqrt(1.0f + martix.M22 - martix.M11 - martix.M33) * 2f;
|
||||||
|
w = (martix.M31 - martix.M13) / s;
|
||||||
|
x = (martix.M12 + martix.M21) / s;
|
||||||
|
y = .25f * s;
|
||||||
|
z = (martix.M23 + martix.M32) / s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float s = Math.Sqrt(1.0f + martix.M33 - martix.M11 - martix.M22) * 2f;
|
||||||
|
w = (martix.M12 - martix.M21) / s;
|
||||||
|
x = (martix.M31 + martix.M13) / s;
|
||||||
|
y = (martix.M23 + martix.M32) / s;
|
||||||
|
z = .25f * s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new(x, y, z, w);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if two <see cref="Quaternion"/>s are approximately equal within a specified epsilon range.
|
/// Checks if two <see cref="Quaternion"/>s are approximately equal within a specified epsilon range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -5,17 +5,23 @@ using Engine.Core;
|
|||||||
|
|
||||||
namespace Engine.Integration.MonoGame;
|
namespace Engine.Integration.MonoGame;
|
||||||
|
|
||||||
public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, IPreDraw
|
public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, ILastFrameUpdate, IPreDraw
|
||||||
{
|
{
|
||||||
public Event<MonoGameCamera3D, ViewChangedArguments> OnViewChanged { get; } = new();
|
public Event<MonoGameCamera3D, ViewChangedArguments> OnViewChanged { get; } = new();
|
||||||
public Event<MonoGameCamera3D, ProjectionChangedArguments> OnProjectionChanged { get; } = new();
|
public Event<MonoGameCamera3D, ProjectionChangedArguments> OnProjectionChanged { get; } = new();
|
||||||
public Event<MonoGameCamera3D, ViewportChangedArguments> OnViewportChanged { get; } = new();
|
public Event<MonoGameCamera3D, ViewportChangedArguments> OnViewportChanged { get; } = new();
|
||||||
public Event<MonoGameCamera3D, FieldOfViewChangedArguments> OnFieldOfViewChanged { get; } = new();
|
|
||||||
|
public Event<ICamera3D, ICamera3D.NearPlaneChangedArguments> OnNearPlaneChanged { get; } = new();
|
||||||
|
public Event<ICamera3D, ICamera3D.FarPlaneChangedArguments> OnFarPlaneChanged { get; } = new();
|
||||||
|
public Event<ICamera3D, ICamera3D.FieldOfViewChangedArguments> OnFieldOfViewChanged { get; } = new();
|
||||||
|
|
||||||
private Matrix _view = Matrix.Identity;
|
private Matrix _view = Matrix.Identity;
|
||||||
private Matrix _projection = Matrix.Identity;
|
private Matrix _projection = Matrix.Identity;
|
||||||
private Viewport _viewport = default;
|
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 GraphicsDeviceManager Graphics { get; private set; } = null!;
|
||||||
public ITransform3D Transform { 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 previousViewport = _viewport;
|
||||||
_viewport = value;
|
_viewport = value;
|
||||||
|
SetForRecalculation();
|
||||||
OnViewportChanged.Invoke(this, new(previousViewport));
|
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
|
public float FieldOfView
|
||||||
{
|
{
|
||||||
get => _fieldOfView;
|
get => _fieldOfView * Math.RadianToDegree;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
float newValue = Math.Max(0.1f, value);
|
value = value.Max(0.1f) * Math.DegreeToRadian;
|
||||||
|
|
||||||
if (_fieldOfView == newValue)
|
if (_fieldOfView == value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float previousFieldOfView = _fieldOfView;
|
float previousFieldOfView = _fieldOfView;
|
||||||
_fieldOfView = newValue;
|
_fieldOfView = value;
|
||||||
|
SetForRecalculation();
|
||||||
OnFieldOfViewChanged.Invoke(this, new(previousFieldOfView));
|
OnFieldOfViewChanged.Invoke(this, new(previousFieldOfView));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Engine.Core.Quaternion Rotation
|
public Core.Quaternion Rotation
|
||||||
{
|
{
|
||||||
get => Transform.Rotation;
|
get => Transform.Rotation;
|
||||||
set => Transform.Rotation = value;
|
set => Transform.Rotation = value;
|
||||||
@@ -104,28 +136,61 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, IPreDra
|
|||||||
|
|
||||||
public Vector2D WorldToScreenPosition(Vector3D worldPosition) => Viewport.Project(worldPosition.ToVector3(), _projection, _view, Matrix.Identity).ToVector3D();
|
public Vector2D WorldToScreenPosition(Vector3D worldPosition) => Viewport.Project(worldPosition.ToVector3(), _projection, _view, Matrix.Identity).ToVector3D();
|
||||||
|
|
||||||
|
public void LastActiveFrame() => Transform.OnTransformUpdated.RemoveListener(SetDirtyOnTransformUpdate);
|
||||||
public void FirstActiveFrame()
|
public void FirstActiveFrame()
|
||||||
{
|
{
|
||||||
|
Transform = BehaviourController.GetRequiredBehaviour<ITransform3D>();
|
||||||
Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>().Window.Graphics;
|
Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>().Window.Graphics;
|
||||||
Viewport = Graphics.GraphicsDevice.Viewport;
|
Viewport = Graphics.GraphicsDevice.Viewport;
|
||||||
|
|
||||||
|
Transform.OnTransformUpdated.AddListener(SetDirtyOnTransformUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetDirtyOnTransformUpdate(ITransform3D sender) => SetForRecalculation();
|
||||||
|
private void SetForRecalculation() => isRecalculationNeeded = true;
|
||||||
|
|
||||||
public void PreDraw()
|
public void PreDraw()
|
||||||
{
|
{
|
||||||
Vector3 cameraPosition = Position.ToVector3();
|
if (!isRecalculationNeeded)
|
||||||
View = Matrix.CreateLookAt(
|
return;
|
||||||
cameraPosition,
|
|
||||||
Transform.Forward.ToVector3() + cameraPosition,
|
CalculateView();
|
||||||
Transform.Up.ToVector3()
|
CalculateProjection();
|
||||||
);
|
isRecalculationNeeded = false;
|
||||||
Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Viewport.AspectRatio, 0.1f, 100f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour<ITransform3D>();
|
private void CalculateView()
|
||||||
protected sealed override void FinalizeInternal() => Transform = null!;
|
{
|
||||||
|
Vector3 forward = Vector3.Normalize(Transform.Forward.ToVector3());
|
||||||
|
Vector3 up = Vector3.Normalize(Transform.Up.ToVector3());
|
||||||
|
Vector3 right = Vector3.Normalize(Transform.Right.ToVector3());
|
||||||
|
Vector3 position = Position.ToVector3();
|
||||||
|
|
||||||
|
View = new Matrix(
|
||||||
|
right.X, up.X, forward.X, 0,
|
||||||
|
right.Y, up.Y, forward.Y, 0,
|
||||||
|
right.Z, up.Z, forward.Z, 0,
|
||||||
|
-Vector3.Dot(right, position),
|
||||||
|
-Vector3.Dot(up, position),
|
||||||
|
-Vector3.Dot(forward, position),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CalculateProjection()
|
||||||
|
{
|
||||||
|
float yScale = 1f / (float)Math.Tan(_fieldOfView / 2f);
|
||||||
|
float xScale = yScale / Viewport.AspectRatio;
|
||||||
|
|
||||||
|
Projection = new Matrix(
|
||||||
|
xScale, 0, 0, 0,
|
||||||
|
0, yScale, 0, 0,
|
||||||
|
0, 0, _farPlane / (_farPlane - _nearPlane), 1,
|
||||||
|
0, 0, -_nearPlane * _farPlane / (_farPlane - _nearPlane), 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public readonly record struct ViewChangedArguments(Matrix PreviousView);
|
public readonly record struct ViewChangedArguments(Matrix PreviousView);
|
||||||
public readonly record struct ProjectionChangedArguments(Matrix PreviousProjection);
|
public readonly record struct ProjectionChangedArguments(Matrix PreviousProjection);
|
||||||
public readonly record struct ViewportChangedArguments(Viewport PreviousViewport);
|
public readonly record struct ViewportChangedArguments(Viewport PreviousViewport);
|
||||||
public readonly record struct FieldOfViewChangedArguments(float PreviousFieldOfView);
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user