feat: 3D transforms added
This commit is contained in:
6
Engine.Core/Abstract/IBehaviour3D.cs
Normal file
6
Engine.Core/Abstract/IBehaviour3D.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
public interface IBehaviour3D : IBehaviour
|
||||||
|
{
|
||||||
|
ITransform3D Transform { get; }
|
||||||
|
}
|
105
Engine.Core/Abstract/ITransform3D.cs
Normal file
105
Engine.Core/Abstract/ITransform3D.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the transformation properties of an object such as position, scale, and rotation in 3D space.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITransform3D : IBehaviour
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform3D"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
Event<ITransform3D, PositionChangedArguments> OnPositionChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform3D"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
Event<ITransform3D, ScaleChangedArguments> OnScaleChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform3D"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
Event<ITransform3D, RotationChangedArguments> OnRotationChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when any of the properties of the <see cref="ITransform3D"/> gets updated.
|
||||||
|
/// </summary>
|
||||||
|
Event<ITransform3D> OnTransformUpdated { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Up { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Down { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Left { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Right { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing forwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Forward { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing backwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Backward { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The world position of the <see cref="ITransform3D"/> in 3D space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Position { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The world scale of the <see cref="ITransform3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Scale { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The world rotation of the <see cref="ITransform3D"/> in degrees.
|
||||||
|
/// </summary>
|
||||||
|
Quaternion Rotation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local position of the <see cref="ITransform3D"/> in 3D space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D LocalPosition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local scale of the <see cref="ITransform3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D LocalScale { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local rotation of the <see cref="ITransform3D"/> in degrees.
|
||||||
|
/// </summary>
|
||||||
|
Quaternion LocalRotation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments for the event triggered when the <see cref="ITransform3D"/>'s rotation changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="PreviousPosition">The previous <see cref="Position"/> of the <see cref="ITransform3D"/>.</param>
|
||||||
|
readonly record struct PositionChangedArguments(Vector3D PreviousPosition);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments for the event triggered when the <see cref="ITransform3D"/>'s rotation changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="PreviousScale">The previous <see cref="Scale"/> of the <see cref="ITransform3D"/>.</param>
|
||||||
|
readonly record struct ScaleChangedArguments(Vector3D PreviousScale);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments for the event triggered when the <see cref="ITransform3D"/>'s rotation changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="PreviousRotation">The previous <see cref="Rotation"/> of the <see cref="ITransform3D"/>.</param>
|
||||||
|
readonly record struct RotationChangedArguments(Quaternion PreviousRotation);
|
||||||
|
}
|
@@ -14,4 +14,17 @@ public static class TransformExtensions
|
|||||||
if (localScale.HasValue) transform.LocalScale = localScale.Value;
|
if (localScale.HasValue) transform.LocalScale = localScale.Value;
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ITransform3D SetTransform(this ITransform3D transform,
|
||||||
|
Vector3D? position = null, Quaternion? rotation = null, Vector3D? scale = null,
|
||||||
|
Vector3D? localPosition = null, Quaternion? localRotation = null, Vector3D? localScale = null)
|
||||||
|
{
|
||||||
|
if (position.HasValue) transform.Position = position.Value;
|
||||||
|
if (rotation.HasValue) transform.Rotation = rotation.Value;
|
||||||
|
if (scale.HasValue) transform.Scale = scale.Value;
|
||||||
|
if (localPosition.HasValue) transform.LocalPosition = localPosition.Value;
|
||||||
|
if (localRotation.HasValue) transform.LocalRotation = localRotation.Value;
|
||||||
|
if (localScale.HasValue) transform.LocalScale = localScale.Value;
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
285
Engine.Core/Transform3D.cs
Normal file
285
Engine.Core/Transform3D.cs
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
using Engine.Core.Serialization;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("Name: {UniverseObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
|
||||||
|
public class Transform3D : Behaviour, ITransform3D
|
||||||
|
{
|
||||||
|
public Event<ITransform3D, ITransform3D.PositionChangedArguments> OnPositionChanged { get; } = new();
|
||||||
|
public Event<ITransform3D, ITransform3D.ScaleChangedArguments> OnScaleChanged { get; } = new();
|
||||||
|
public Event<ITransform3D, ITransform3D.RotationChangedArguments> OnRotationChanged { get; } = new();
|
||||||
|
public Event<ITransform3D> OnTransformUpdated { get; } = new();
|
||||||
|
|
||||||
|
private Vector3D _position = Vector3D.Zero;
|
||||||
|
private Vector3D _scale = Vector3D.One;
|
||||||
|
private Quaternion _rotation = Quaternion.Identity;
|
||||||
|
|
||||||
|
[Serialize] private Vector3D _localPosition = Vector3D.Zero;
|
||||||
|
[Serialize] private Vector3D _localScale = Vector3D.One;
|
||||||
|
[Serialize] private Quaternion _localRotation = Quaternion.Identity;
|
||||||
|
|
||||||
|
private ITransform3D? parentTransform = null;
|
||||||
|
|
||||||
|
public Vector3D Up => Quaternion.RotateVector(Vector3D.Up, Rotation);
|
||||||
|
public Vector3D Down => Quaternion.RotateVector(Vector3D.Down, Rotation);
|
||||||
|
public Vector3D Left => Quaternion.RotateVector(Vector3D.Left, Rotation);
|
||||||
|
public Vector3D Right => Quaternion.RotateVector(Vector3D.Right, Rotation);
|
||||||
|
public Vector3D Forward => Quaternion.RotateVector(Vector3D.Forward, Rotation);
|
||||||
|
public Vector3D Backward => Quaternion.RotateVector(Vector3D.Backward, Rotation);
|
||||||
|
|
||||||
|
public Vector3D Position
|
||||||
|
{
|
||||||
|
get => _position;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _position)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector3D previousPosition = _position;
|
||||||
|
_position = value;
|
||||||
|
|
||||||
|
UpdateLocalPosition();
|
||||||
|
OnPositionChanged.Invoke(this, new(previousPosition));
|
||||||
|
OnTransformUpdated.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3D Scale
|
||||||
|
{
|
||||||
|
get => _scale;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _scale)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector3D previousScale = _scale;
|
||||||
|
_scale = value;
|
||||||
|
|
||||||
|
UpdateLocalScale();
|
||||||
|
OnScaleChanged.Invoke(this, new(previousScale));
|
||||||
|
OnTransformUpdated.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quaternion Rotation
|
||||||
|
{
|
||||||
|
get => _rotation;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _rotation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Quaternion previousRotation = _rotation;
|
||||||
|
_rotation = value;
|
||||||
|
|
||||||
|
UpdateLocalRotation();
|
||||||
|
OnRotationChanged.Invoke(this, new(previousRotation));
|
||||||
|
OnTransformUpdated.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3D LocalPosition
|
||||||
|
{
|
||||||
|
get => _localPosition;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _localPosition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector3D previousPosition = _position;
|
||||||
|
_localPosition = value;
|
||||||
|
|
||||||
|
UpdatePosition();
|
||||||
|
OnPositionChanged.Invoke(this, new(previousPosition));
|
||||||
|
OnTransformUpdated.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3D LocalScale
|
||||||
|
{
|
||||||
|
get => _localScale;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _localScale)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector3D previousScale = _scale;
|
||||||
|
Vector3D previousPosition = _position;
|
||||||
|
_localScale = value;
|
||||||
|
|
||||||
|
UpdateScale();
|
||||||
|
UpdatePosition();
|
||||||
|
OnScaleChanged.Invoke(this, new(previousScale));
|
||||||
|
OnPositionChanged.Invoke(this, new(previousPosition));
|
||||||
|
OnTransformUpdated.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quaternion LocalRotation
|
||||||
|
{
|
||||||
|
get => _localRotation;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _localRotation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Quaternion previousRotation = _rotation;
|
||||||
|
_localRotation = value;
|
||||||
|
|
||||||
|
UpdateRotation();
|
||||||
|
OnRotationChanged.Invoke(this, new(previousRotation));
|
||||||
|
OnTransformUpdated.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecalculatePosition(ITransform3D _, ITransform3D.PositionChangedArguments args)
|
||||||
|
{
|
||||||
|
if (parentTransform is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UpdatePosition();
|
||||||
|
|
||||||
|
OnPositionChanged.Invoke(this, args);
|
||||||
|
OnTransformUpdated.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecalculateScale(ITransform3D _, ITransform3D.ScaleChangedArguments args)
|
||||||
|
{
|
||||||
|
if (parentTransform is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector3D previousPosition = _position;
|
||||||
|
|
||||||
|
UpdateScale();
|
||||||
|
UpdatePosition();
|
||||||
|
|
||||||
|
OnScaleChanged.Invoke(this, args);
|
||||||
|
OnPositionChanged.Invoke(this, new(previousPosition));
|
||||||
|
OnTransformUpdated.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecalculateRotation(ITransform3D _, ITransform3D.RotationChangedArguments args)
|
||||||
|
{
|
||||||
|
if (parentTransform is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector3D previousPosition = Position;
|
||||||
|
|
||||||
|
UpdateRotation();
|
||||||
|
UpdatePosition();
|
||||||
|
|
||||||
|
OnRotationChanged.Invoke(this, args);
|
||||||
|
OnPositionChanged.Invoke(this, new(previousPosition));
|
||||||
|
OnTransformUpdated.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLocalPosition()
|
||||||
|
{
|
||||||
|
if (parentTransform is null)
|
||||||
|
_localPosition = Position;
|
||||||
|
else
|
||||||
|
_localPosition = parentTransform.Position.FromTo(Position).Scale(parentTransform.Scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLocalScale()
|
||||||
|
{
|
||||||
|
if (parentTransform is null)
|
||||||
|
_localScale = Scale;
|
||||||
|
else
|
||||||
|
_localScale = Scale.Scale(new(1f / parentTransform.Scale.X, 1f / parentTransform.Scale.Y, 1f / parentTransform.Scale.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLocalRotation()
|
||||||
|
{
|
||||||
|
if (parentTransform is null)
|
||||||
|
_localRotation = Rotation;
|
||||||
|
else
|
||||||
|
_localRotation = Rotation * parentTransform.Rotation.Invert();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePosition()
|
||||||
|
{
|
||||||
|
if (parentTransform is null)
|
||||||
|
_position = LocalPosition;
|
||||||
|
else
|
||||||
|
_position = parentTransform.Position + Quaternion.RotateVector(LocalPosition.Scale(new(parentTransform.Scale.X, parentTransform.Scale.Y, parentTransform.Scale.Z)), parentTransform.Rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateScale()
|
||||||
|
{
|
||||||
|
if (parentTransform is null)
|
||||||
|
_scale = LocalScale;
|
||||||
|
else
|
||||||
|
_scale = (parentTransform?.Scale ?? Vector3D.One).Scale(_localScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRotation()
|
||||||
|
{
|
||||||
|
if (parentTransform is null)
|
||||||
|
_rotation = LocalRotation;
|
||||||
|
else
|
||||||
|
_rotation = parentTransform.Rotation * LocalRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InitializeInternal()
|
||||||
|
{
|
||||||
|
UpdateReferences(UniverseObject.Parent);
|
||||||
|
UniverseObject.OnParentChanged.AddListener(OnParentChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void FinalizeInternal()
|
||||||
|
{
|
||||||
|
UniverseObject.OnParentChanged.RemoveListener(OnParentChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateReferences(IUniverseObject? parent)
|
||||||
|
{
|
||||||
|
ITransform3D? previousParent = parentTransform;
|
||||||
|
if (previousParent is not null)
|
||||||
|
{
|
||||||
|
previousParent.OnPositionChanged.RemoveListener(RecalculatePosition);
|
||||||
|
previousParent.OnScaleChanged.RemoveListener(RecalculateScale);
|
||||||
|
previousParent.OnRotationChanged.RemoveListener(RecalculateRotation);
|
||||||
|
previousParent.BehaviourController.UniverseObject.OnParentChanged.RemoveListener(OnParentChanged);
|
||||||
|
previousParent.BehaviourController.OnBehaviourAdded.RemoveListener(LookForTransform3D);
|
||||||
|
}
|
||||||
|
|
||||||
|
parentTransform = parent?.BehaviourController.GetBehaviour<ITransform3D>();
|
||||||
|
|
||||||
|
if (parentTransform is not null)
|
||||||
|
{
|
||||||
|
parentTransform.OnPositionChanged.AddListener(RecalculatePosition);
|
||||||
|
parentTransform.OnScaleChanged.AddListener(RecalculateScale);
|
||||||
|
parentTransform.OnRotationChanged.AddListener(RecalculateRotation);
|
||||||
|
parentTransform.BehaviourController.UniverseObject.OnParentChanged.AddListener(OnParentChanged);
|
||||||
|
|
||||||
|
UpdatePosition();
|
||||||
|
UpdateScale();
|
||||||
|
UpdateRotation();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
UniverseObject.Parent?.BehaviourController.OnBehaviourAdded.AddListener(LookForTransform3D);
|
||||||
|
|
||||||
|
UpdateLocalPosition();
|
||||||
|
UpdateLocalScale();
|
||||||
|
UpdateLocalRotation();
|
||||||
|
|
||||||
|
OnPositionChanged.Invoke(this, new(Position));
|
||||||
|
OnScaleChanged.Invoke(this, new(Scale));
|
||||||
|
OnRotationChanged.Invoke(this, new(Rotation));
|
||||||
|
OnTransformUpdated.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LookForTransform3D(IBehaviourController sender, IBehaviourController.BehaviourAddedArguments args)
|
||||||
|
{
|
||||||
|
if (args.BehaviourAdded is not ITransform3D)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UpdateReferences(UniverseObject.Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnParentChanged(IUniverseObject sender, IUniverseObject.ParentChangedArguments args)
|
||||||
|
{
|
||||||
|
UpdateReferences(args.CurrentParent);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user