using Chernobyl.Mathematics.Vectors;
namespace Chernobyl.Mathematics.Movement
{
///
/// A transformation that removes the scale that was applied by previous
/// ancestors in a transformation hierarchy. For example, lets say you have
/// two sprites. You want the child sprite to be attached to the parent sprite
/// so that the child sprite will "follow" the parent sprite as it moves. You
/// also want to scale the parent sprite but don't want the child to be affected
/// by this scaling. To do this, you would place a RemoveScaleTransform between
/// the parent and the child like so:
///
/// Parent
/// |
/// |---- RemoveScaleTransform
/// |
/// |------ Child
///
/// In code:
///
/// RemoveScaleTransform rst = new RemoveScaleTransform();
/// parentSprite.AttachTo(rst);
/// rst.AttachTo(childSprite);
///
///
/// Note that, a scale can still be applied to the RemoveScaleTransform and
/// that scale WILL be applied to descendant transforms in the transform
/// hierarchy.
///
public class RemoveScaleTransform : MatrixTransform
{
///
/// Initializes a new instance of the
/// class that removes scale from all axis of a transform.
///
public RemoveScaleTransform() : this(Axis.All)
{ }
///
/// Initializes a new instance of the class.
///
/// The axis of the transform hierarchy whose
/// scale should be removed. For example, if you want to remove scale from
/// only the X axis then you would specify . If you
/// would like to remove scale from the Y and Z axis only, then you would
/// specify and like so:
///
/// RemoveScaleTransform rms = new RemoveScaleTransform(Axis.Y | Axis.Z);
///
///
public RemoveScaleTransform(Axis axisToRemoveScaleFrom)
: this(axisToRemoveScaleFrom, DefaultKeepScaleAffectedPosition)
{ }
///
/// Initializes a new instance of the class.
///
/// The axis of the transform hierarchy whose
/// scale should be removed. For example, if you want to remove scale from
/// only the X axis then you would specify . If you
/// would like to remove scale from the Y and Z axis only, then you would
/// specify and like so:
///
/// RemoveScaleTransform rms = new RemoveScaleTransform(Axis.Y | Axis.Z);
///
///
///
/// True if the position of this transform should remain unaffected by
/// the removal of the scale from it's ancestors, false if otherwise.
/// Setting this property to true can help in cases where you want the
/// scaled ancestors to not affect the scale of a child but want the
/// child's position to be made relative to the scale of the ancestors.
/// By default, the value of this property is set by the
/// static field .
///
/// Performance warning: if this value is true, an additional matrix
/// multiplication has to be done in order to find the position affected
/// by the scale. This may introduce a small (perhaps insignificant)
/// performance degradation.
public RemoveScaleTransform(Axis axisToRemoveScaleFrom, bool keepScaleAffectedPosition)
{
AxisToRemoveScaleFrom = axisToRemoveScaleFrom;
KeepScaleAffectedPosition = keepScaleAffectedPosition;
}
///
/// Orders this transform so that it's world matrix represents it's
/// local matrix relative to it's parent's world matrix.
///
protected override void Update()
{
if (IsUpdateNeeded == true)
{
// if we have a parent: recompute our world matrix using our
// local matrix and the world matrix of our parent
if (TransformParent != null)
{
if(AxisToRemoveScaleFrom != Axis.None)
{
// now we need to take the parent's transformation matrix and
// remove the scale from it. After we have done that, we can
// use the parent's scale-less matrix like we normally would.
Matrix4 parentsWorldMatrix = TransformParent.WorldMatrix;
// normalize each axis of the parent's world matrix to remove
// the scale...
// X axis
if ((AxisToRemoveScaleFrom & Axis.X) == Axis.X && parentsWorldMatrix.Row0 != Vector4.Zero)
Vector4.Normalize(ref parentsWorldMatrix.Row0, out parentsWorldMatrix.Row0);
// Y axis
if ((AxisToRemoveScaleFrom & Axis.Y) == Axis.Y && parentsWorldMatrix.Row1 != Vector4.Zero)
Vector4.Normalize(ref parentsWorldMatrix.Row1, out parentsWorldMatrix.Row1);
// Z axis
if ((AxisToRemoveScaleFrom & Axis.Z) == Axis.Z && parentsWorldMatrix.Row2 != Vector4.Zero)
Vector4.Normalize(ref parentsWorldMatrix.Row2, out parentsWorldMatrix.Row2);
// if necessary, find the "normal" position or the resulting
// position of this transform had the scale not been removed
// from the ancestor transforms
if (KeepScaleAffectedPosition == true)
{
Matrix4 normalWorldMatrix = LocalMatrix * TransformParent.WorldMatrix;
Matrix4 deScaledWorldMatrix = LocalMatrix * parentsWorldMatrix;
WorldMatrix = new Matrix4( deScaledWorldMatrix.Row0, deScaledWorldMatrix.Row1,
deScaledWorldMatrix.Row2, normalWorldMatrix.Row3);
}
else
WorldMatrix = LocalMatrix * parentsWorldMatrix;
}
else
WorldMatrix = LocalMatrix * TransformParent.WorldMatrix;
}
else // if we don't have a parent: our local is our world
WorldMatrix = LocalMatrix;
IsUpdateNeeded = false; // we are "clean" now..
}
}
///
/// The axis of the transform hierarchy whose
/// scale should be removed. For example, if you want to remove scale from
/// only the X axis then you would specify . If you
/// would like to remove scale from the Y and Z axis only, then you would
/// specify and like so:
///
/// RemoveScaleTransform rms = new RemoveScaleTransform(Axis.Y | Axis.Z);
///
///
/// By default, this value is .
public Axis AxisToRemoveScaleFrom { get; set; }
///
/// True if the position of this transform should remain unaffected by
/// the removal of the scale from it's ancestors, false if otherwise.
/// Setting this property to true can help in cases where you want the
/// scaled ancestors to not affect the scale of a child but want the
/// child's position to be made relative to the scale of the ancestors.
///
/// Performance warning: if this value is true, an additional matrix
/// multiplication has to be done in order to find the position affected
/// by the scale. This may introduce a small (perhaps insignificant)
/// performance degradation.
///
/// By default, the value of this property is set by the
/// static field .
public bool KeepScaleAffectedPosition { get; set; }
///
/// The default value of the
/// property.
///
public static bool DefaultKeepScaleAffectedPosition = false;
}
}