Add ultimate xr
This commit is contained in:
243
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrAxis.cs
Normal file
243
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrAxis.cs
Normal file
@@ -0,0 +1,243 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAxis.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that allows to have formatted axes (A combo box with X, Y, Z strings) instead of numerical fields.
|
||||
/// It also allows conversion from and to integers and <see cref="Vector3" /> types.
|
||||
/// See the UxrAxisPropertyDrawer editor class for the integration with Unity Editor.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrAxis : IEquatable<UxrAxis>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private int _axis;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
public const int X = 0;
|
||||
public const int Y = 1;
|
||||
public const int Z = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a perpendicular axis.
|
||||
/// </summary>
|
||||
public UxrAxis Perpendicular => (_axis + 1) % 3;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the other perpendicular axis.
|
||||
/// </summary>
|
||||
public UxrAxis OtherPerpendicular => (_axis + 2) % 3;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="axis">Axis as an integer value</param>
|
||||
public UxrAxis(int axis)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
if (axis < 0 || axis > 3)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} Assigning invalid value to axis: {axis}");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
_axis = Mathf.Clamp(axis, 0, 2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IEquatable<UxrAxis>
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(UxrAxis other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _axis == other._axis;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return _axis switch
|
||||
{
|
||||
0 => "Right",
|
||||
1 => "Up",
|
||||
_ => "Forward"
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as UxrAxis);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _axis.GetHashCode();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns the remaining axis which is not axis1 nor axis2.
|
||||
/// </summary>
|
||||
/// <param name="axis1">Axis 1</param>
|
||||
/// <param name="axis2">Axis 2</param>
|
||||
/// <returns>The remaining axis which is not axis1 nor axis2</returns>
|
||||
public static UxrAxis OtherThan(UxrAxis axis1, UxrAxis axis2)
|
||||
{
|
||||
if (axis1 == axis2)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} {nameof(UxrAxis)}: Got same axis for {nameof(OtherThan)} (axis1)");
|
||||
}
|
||||
|
||||
return axis1.Perpendicular;
|
||||
}
|
||||
|
||||
int smaller = axis1._axis < axis2._axis ? axis1._axis : axis2._axis;
|
||||
int bigger = axis1._axis > axis2._axis ? axis1._axis : axis2._axis;
|
||||
|
||||
if (smaller == 0 && bigger == 1)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
if (smaller == 0 && bigger == 2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color representing
|
||||
/// </summary>
|
||||
/// <param name="alpha"></param>
|
||||
/// <returns></returns>
|
||||
public Color GetColor(float alpha)
|
||||
{
|
||||
Vector3 axis = this;
|
||||
return new Color(Mathf.Abs(axis.x), Mathf.Abs(axis.y), Mathf.Abs(axis.z), alpha);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Unary minus operator. Negates the axis value.
|
||||
/// </summary>
|
||||
/// <param name="axis">Axis to negate</param>
|
||||
/// <returns>Negated axis</returns>
|
||||
public static Vector3 operator -(UxrAxis axis)
|
||||
{
|
||||
return -(Vector3)axis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion from an <see cref="UxrAxis" /> to a <see cref="Vector3" />.
|
||||
/// </summary>
|
||||
/// <param name="axis">Axis to convert</param>
|
||||
/// <returns>Converted <see cref="Vector3" /> value</returns>
|
||||
public static implicit operator Vector3(UxrAxis axis)
|
||||
{
|
||||
return axis._axis switch
|
||||
{
|
||||
0 => Vector3.right,
|
||||
1 => Vector3.up,
|
||||
_ => Vector3.forward
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion from an integer to an <see cref="UxrAxis" />.
|
||||
/// </summary>
|
||||
/// <param name="axis">Integer to convert</param>
|
||||
/// <returns>Converted <see cref="UxrAxis" /> value</returns>
|
||||
public static implicit operator UxrAxis(int axis)
|
||||
{
|
||||
return new UxrAxis(axis);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equality operator.
|
||||
/// </summary>
|
||||
/// <param name="a">Operand A</param>
|
||||
/// <param name="b">Operand B</param>
|
||||
/// <returns>Whether the two operands are equal</returns>
|
||||
public static bool operator ==(UxrAxis a, UxrAxis b)
|
||||
{
|
||||
if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inequality operator.
|
||||
/// </summary>
|
||||
/// <param name="a">Operand A</param>
|
||||
/// <param name="b">Operand B</param>
|
||||
/// <returns>Whether the two operands are different</returns>
|
||||
public static bool operator !=(UxrAxis a, UxrAxis b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion from an <see cref="UxrAxis" /> to an integer.
|
||||
/// </summary>
|
||||
/// <param name="axis">Axis to convert</param>
|
||||
/// <returns>Int value (0 = right, 1 = up, 2 = forward)</returns>
|
||||
public static implicit operator int(UxrAxis axis)
|
||||
{
|
||||
return axis._axis;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrAxis.cs.meta
Normal file
13
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrAxis.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79ee905395498e5428d29fba7ef65416
|
||||
timeCreated: 1538564096
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
193
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrMathUtils.cs
Normal file
193
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrMathUtils.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrMathUtils.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains math computations involving elements that do not belong to a specific class.
|
||||
/// Most math is available through extensions classes in namespaces such as
|
||||
/// <see cref="UltimateXR.Extensions.System.Math">UltimateXR.Extensions.System.Math</see> or
|
||||
/// <see cref="UltimateXR.Extensions.Unity.Math">UltimateXR.Extensions.Unity.Math</see>.
|
||||
/// Math related to animation is also available through classes in namespaces such as
|
||||
/// <see cref="UltimateXR.Animation.IK">UltimateXR.Animation.IK</see>,
|
||||
/// <see cref="UltimateXR.Animation.Interpolation">UltimateXRAnimation.Interpolation</see> or
|
||||
/// <see cref="UltimateXR.Animation.Splines">UltimateXR.Animation.Splines</see>.
|
||||
/// This class will contain math functionality that cannot be assigned to any extensions class.
|
||||
/// </summary>
|
||||
public static class UxrMathUtils
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the intersection(s) between a 2D line and a 2D circle
|
||||
/// Code from: http://csharphelper.com/blog/2014/09/determine-where-a-line-intersects-a-circle-in-c/
|
||||
/// </summary>
|
||||
/// <param name="linePoint1">Point A in the line</param>
|
||||
/// <param name="linePoint2">Point B in the line</param>
|
||||
/// <param name="circlePos">Circle position</param>
|
||||
/// <param name="radius">Circle radius</param>
|
||||
/// <param name="intersection1">Intersection 1 result, if it exists</param>
|
||||
/// <param name="intersection2">Intersection 2 result, if it exists</param>
|
||||
/// <returns>Number of intersections found (0, 1 or 2)</returns>
|
||||
public static int FindLineCircleIntersections2D(Vector2 linePoint1, Vector2 linePoint2, Vector2 circlePos, float radius, out Vector2 intersection1, out Vector2 intersection2)
|
||||
{
|
||||
float t;
|
||||
float dx = linePoint2.x - linePoint1.x;
|
||||
float dy = linePoint2.y - linePoint1.y;
|
||||
float a = dx * dx + dy * dy;
|
||||
float b = 2 * (dx * (linePoint1.x - circlePos.x) + dy * (linePoint1.y - circlePos.y));
|
||||
float c = (linePoint1.x - circlePos.x) * (linePoint1.x - circlePos.x) + (linePoint1.y - circlePos.y) * (linePoint1.y - circlePos.y) - radius * radius;
|
||||
float det = b * b - 4 * a * c;
|
||||
|
||||
if (a <= 0.0000001 || det < 0)
|
||||
{
|
||||
// No real solutions.
|
||||
intersection1 = new Vector3(float.NaN, float.NaN);
|
||||
intersection2 = new Vector3(float.NaN, float.NaN);
|
||||
return 0;
|
||||
}
|
||||
if (det == 0)
|
||||
{
|
||||
// One solution.
|
||||
t = -b / (2 * a);
|
||||
intersection1 = new Vector3(linePoint1.x + t * dx, linePoint1.y + t * dy);
|
||||
intersection2 = new Vector3(float.NaN, float.NaN);
|
||||
return 1;
|
||||
}
|
||||
// Two solutions.
|
||||
t = (-b + Mathf.Sqrt(det)) / (2 * a);
|
||||
intersection1 = new Vector3(linePoint1.x + t * dx, linePoint1.y + t * dy);
|
||||
t = (-b - Mathf.Sqrt(det)) / (2 * a);
|
||||
intersection2 = new Vector3(linePoint1.x + t * dx, linePoint1.y + t * dy);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies to <paramref name="position" /> and <paramref name="rotation" /> the transformation to make a transform
|
||||
/// defined by <paramref name="sourcePosition" /> and <paramref name="sourceRotation" /> move and rotate to
|
||||
/// <paramref name="targetPosition" /> and <paramref name="targetRotation" />.
|
||||
/// </summary>
|
||||
/// <param name="position">Position to apply the transformation to</param>
|
||||
/// <param name="rotation">Rotation to apply the transformation to</param>
|
||||
/// <param name="sourcePosition">Source position that will try to match <paramref name="targetPosition" /></param>
|
||||
/// <param name="sourceRotation">Source rotation that will try to match <paramref name="targetRotation" /></param>
|
||||
/// <param name="targetPosition">Target position</param>
|
||||
/// <param name="targetRotation">Target rotation</param>
|
||||
/// <param name="rotate">Allows to control whether to rotate or not</param>
|
||||
/// <param name="translate">Allows to control whether to translate or not</param>
|
||||
/// <param name="t">Optional interpolation value [0.0, 1.0]</param>
|
||||
public static void ApplyAlignment(ref Vector3 position,
|
||||
ref Quaternion rotation,
|
||||
Vector3 sourcePosition,
|
||||
Quaternion sourceRotation,
|
||||
Vector3 targetPosition,
|
||||
Quaternion targetRotation,
|
||||
bool rotate,
|
||||
bool translate,
|
||||
float t = 1.0f)
|
||||
{
|
||||
if (rotate)
|
||||
{
|
||||
rotation.ApplyAlignment(sourceRotation, targetRotation, t);
|
||||
}
|
||||
|
||||
if (translate)
|
||||
{
|
||||
position += (targetPosition - sourcePosition) * t;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a box is completely (all corners) inside a BoxCollider
|
||||
/// </summary>
|
||||
/// <param name="boxPosition">Position of the box to test if it is inside the BoxCollider</param>
|
||||
/// <param name="boxRotation">Rotation of the box to test if it is inside the BoxCollider</param>
|
||||
/// <param name="boxScale">Scale of the box to test if it is inside the BoxCollider</param>
|
||||
/// <param name="boxCenter">Center of the box (in local coordinates) to test if it is inside the BoxCollider</param>
|
||||
/// <param name="boxSize">Size of the box (in local coordinates) to test if it is inside the BoxCollider</param>
|
||||
/// <param name="boxVolume">BoxCollider to test against</param>
|
||||
/// <param name="margin">Allowed margin for each x, y, z component</param>
|
||||
/// <returns>True if all corners are inside the BoxCollider plus margin</returns>
|
||||
public static bool IsBoxInsideBox(Vector3 boxPosition, Quaternion boxRotation, Vector3 boxScale, Vector3 boxCenter, Vector3 boxSize, BoxCollider boxVolume, Vector3 margin = default)
|
||||
{
|
||||
Matrix4x4 boxMatrix = Matrix4x4.TRS(boxPosition, boxRotation, boxScale);
|
||||
Vector3[] corners = new Vector3[8];
|
||||
|
||||
corners[0] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(+boxSize.x, +boxSize.y, +boxSize.z) * 0.5f);
|
||||
corners[1] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(+boxSize.x, +boxSize.y, -boxSize.z) * 0.5f);
|
||||
corners[2] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(+boxSize.x, -boxSize.y, +boxSize.z) * 0.5f);
|
||||
corners[3] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(+boxSize.x, -boxSize.y, -boxSize.z) * 0.5f);
|
||||
corners[4] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(-boxSize.x, +boxSize.y, +boxSize.z) * 0.5f);
|
||||
corners[5] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(-boxSize.x, +boxSize.y, -boxSize.z) * 0.5f);
|
||||
corners[6] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(-boxSize.x, -boxSize.y, +boxSize.z) * 0.5f);
|
||||
corners[7] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(-boxSize.x, -boxSize.y, -boxSize.z) * 0.5f);
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
if (!corners[i].IsInsideBox(boxVolume, margin))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if box1 is completely (all corners) inside box2
|
||||
/// </summary>
|
||||
/// <param name="box1Position">Position of box1</param>
|
||||
/// <param name="box1Rotation">Rotation of box1</param>
|
||||
/// <param name="box1Scale">Scale of box1</param>
|
||||
/// <param name="box1Center">Center of box1 in its own local coordinates</param>
|
||||
/// <param name="box1Size">Size of box1 in its own local coordinates</param>
|
||||
/// <param name="box2Position">Position of box2</param>
|
||||
/// <param name="box2Rotation">Rotation of box2</param>
|
||||
/// <param name="box2Scale">Scale of box2</param>
|
||||
/// <param name="box2Center">Center of box2 in its own local coordinates</param>
|
||||
/// <param name="box2Size">Size of box2 in its own local coordinates</param>
|
||||
/// <param name="margin">Allowed margin for each x, y, z component</param>
|
||||
/// <returns>True if all corners of box1 are inside box2 plus margin</returns>
|
||||
public static bool IsBoxInsideBox(Vector3 box1Position,
|
||||
Quaternion box1Rotation,
|
||||
Vector3 box1Scale,
|
||||
Vector3 box1Center,
|
||||
Vector3 box1Size,
|
||||
Vector3 box2Position,
|
||||
Quaternion box2Rotation,
|
||||
Vector3 box2Scale,
|
||||
Vector3 box2Center,
|
||||
Vector3 box2Size,
|
||||
Vector3 margin = default)
|
||||
{
|
||||
Matrix4x4 boxMatrix = Matrix4x4.TRS(box1Position, box1Rotation, box1Scale);
|
||||
Vector3[] corners = new Vector3[8];
|
||||
|
||||
corners[0] = boxMatrix.MultiplyPoint(box1Center + new Vector3(+box1Size.x, +box1Size.y, +box1Size.z) * 0.5f);
|
||||
corners[1] = boxMatrix.MultiplyPoint(box1Center + new Vector3(+box1Size.x, +box1Size.y, -box1Size.z) * 0.5f);
|
||||
corners[2] = boxMatrix.MultiplyPoint(box1Center + new Vector3(+box1Size.x, -box1Size.y, +box1Size.z) * 0.5f);
|
||||
corners[3] = boxMatrix.MultiplyPoint(box1Center + new Vector3(+box1Size.x, -box1Size.y, -box1Size.z) * 0.5f);
|
||||
corners[4] = boxMatrix.MultiplyPoint(box1Center + new Vector3(-box1Size.x, +box1Size.y, +box1Size.z) * 0.5f);
|
||||
corners[5] = boxMatrix.MultiplyPoint(box1Center + new Vector3(-box1Size.x, +box1Size.y, -box1Size.z) * 0.5f);
|
||||
corners[6] = boxMatrix.MultiplyPoint(box1Center + new Vector3(-box1Size.x, -box1Size.y, +box1Size.z) * 0.5f);
|
||||
corners[7] = boxMatrix.MultiplyPoint(box1Center + new Vector3(-box1Size.x, -box1Size.y, -box1Size.z) * 0.5f);
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
if (!corners[i].IsInsideBox(box2Position, box2Rotation, box2Scale, box2Center, box2Size, margin))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94fda00f50a40c245b7a2825b2448da9
|
||||
timeCreated: 1501489697
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,317 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrUniversalLocalAxes.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Different parts of the framework need to deal with axes. These algorithms like IK solvers or avatar
|
||||
/// components need to know exactly where 'forward' is or which axis points to the right in avatar-space.
|
||||
/// Since modelling packages and artists may rig objects using arbitrary coordinate systems we need a way to
|
||||
/// perform operations in a way that takes this into account. The code also needs to remain readable since many
|
||||
/// math operations may increase complexity. Readability is favoured here over performance.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This class allows to transform from arbitrary coordinate systems to a universal one where different rotations
|
||||
/// can then be performed and vice versa.
|
||||
/// One example would be a finger bone curl. We create the convention that forward is the axis from one bone to
|
||||
/// the next, up points upwards and right would be the axis around which the bone should rotate to curl. This is OK
|
||||
/// but now we face the problem that different modelling packages or artists rig fingers in completely different
|
||||
/// ways using all varieties of axis systems. The purpose of this class is to help creating a system where
|
||||
/// operations can be performed in this universal system to follow our conventions and then rotated "back" to any
|
||||
/// kind of coordinate system afterwards.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// tl;dr A class that helps us operate with rotations and angles of an object no matter which convention the
|
||||
/// 3D assets use. We call 'Universal' the coordinate system we use as convention for our computations, we then
|
||||
/// can use <see cref="UniversalToActualAxesRotation" /> to transform the object back to its actual axes.
|
||||
/// This way our computations do not care which coordinate system the assets use, and is essential to simplify
|
||||
/// operations like inverse kinematics or angle computations.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrUniversalLocalAxes
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Transform _transform;
|
||||
[SerializeField] private Vector3 _localRight = Vector3.right;
|
||||
[SerializeField] private Vector3 _localUp = Vector3.up;
|
||||
[SerializeField] private Vector3 _localForward = Vector3.forward;
|
||||
[SerializeField] private Quaternion _universalToActualAxesRotation = Quaternion.identity;
|
||||
[SerializeField] private Quaternion _initialRotation = Quaternion.identity;
|
||||
[SerializeField] private Quaternion _initialLocalRotation = Quaternion.identity;
|
||||
[SerializeField] private Quaternion _initialLocalReferenceRotation = Quaternion.identity;
|
||||
[SerializeField] private Quaternion _initialUniversalLocalReferenceRotation = Quaternion.identity;
|
||||
[SerializeField] private Vector3 _initialPosition = Vector3.zero;
|
||||
[SerializeField] private Vector3 _initialLocalPosition = Vector3.zero;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'right' direction in world space.
|
||||
/// </summary>
|
||||
public Vector3 WorldRight => _transform.TransformDirection(LocalRight);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'up' direction in world space.
|
||||
/// </summary>
|
||||
public Vector3 WorldUp => _transform.TransformDirection(LocalUp);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'forward' direction in world space.
|
||||
/// </summary>
|
||||
public Vector3 WorldForward => _transform.TransformDirection(LocalForward);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local object rotation in universal convention
|
||||
/// </summary>
|
||||
public Quaternion UniversalLocalRotation => _transform.localRotation * Quaternion.Inverse(UniversalToActualAxesRotation);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object rotation in universal convention
|
||||
/// </summary>
|
||||
public Quaternion UniversalRotation => _transform.rotation * Quaternion.Inverse(UniversalToActualAxesRotation);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'right' direction in transform's local space.
|
||||
/// </summary>
|
||||
public Vector3 LocalRight
|
||||
{
|
||||
get => _localRight;
|
||||
private set => _localRight = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'up' direction in transform's local space.
|
||||
/// </summary>
|
||||
public Vector3 LocalUp
|
||||
{
|
||||
get => _localUp;
|
||||
private set => _localUp = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'forward' direction in transform's local space.
|
||||
/// </summary>
|
||||
public Vector3 LocalForward
|
||||
{
|
||||
get => _localForward;
|
||||
private set => _localForward = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation that transforms from the universal axes to the convention that the transform follows.
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // universalRotation may be a rotation around the y axis, where we know
|
||||
/// // exactly that y points upwards in that space.
|
||||
/// // This rotation will rotate an object around the "universal" y axis no
|
||||
/// // matter where his actual axes point to.
|
||||
/// transform.rotation = universalRotation * UniversalToActualAxesRotation;
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
public Quaternion UniversalToActualAxesRotation
|
||||
{
|
||||
get => _universalToActualAxesRotation;
|
||||
private set => _universalToActualAxesRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's rotation at the time of setting the object up.
|
||||
/// </summary>
|
||||
public Quaternion InitialRotation
|
||||
{
|
||||
get => _initialRotation;
|
||||
private set => _initialRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's local rotation at the time of setting the object up.
|
||||
/// </summary>
|
||||
public Quaternion InitialLocalRotation
|
||||
{
|
||||
get => _initialLocalRotation;
|
||||
private set => _initialLocalRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's rotation with respect to the reference transform at the time of setting the object up.
|
||||
/// This will only contain a rotation when the constructor using a reference transform was used.
|
||||
/// </summary>
|
||||
public Quaternion InitialLocalReferenceRotation
|
||||
{
|
||||
get => _initialLocalReferenceRotation;
|
||||
private set => _initialLocalReferenceRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's rotation (in universal coordinates) with respect to the reference transform at the time of
|
||||
/// setting the object up.
|
||||
/// This will only contain a rotation when the constructor using a reference transform was used.
|
||||
/// </summary>
|
||||
public Quaternion InitialUniversalLocalReferenceRotation
|
||||
{
|
||||
get => _initialUniversalLocalReferenceRotation;
|
||||
private set => _initialUniversalLocalReferenceRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's position at the time of setting the object up.
|
||||
/// </summary>
|
||||
public Vector3 InitialPosition
|
||||
{
|
||||
get => _initialPosition;
|
||||
private set => _initialPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's local position at the time of setting the object up.
|
||||
/// </summary>
|
||||
public Vector3 InitialLocalPosition
|
||||
{
|
||||
get => _initialLocalPosition;
|
||||
private set => _initialLocalPosition = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// Uses universalReference to check which axes of a transform are actually the ones
|
||||
/// that are right, up and forward. For example, universalReference may be the avatar
|
||||
/// root where we know that right, up and forward point to these actual directions and
|
||||
/// we want to know which axes of an upper body part point to these directions too.
|
||||
/// These may be completely different depending on the modelling package or artist.
|
||||
/// Using this class we can easily check which one points upwards and create a small
|
||||
/// chest torsion by rotating around this axis.
|
||||
/// </summary>
|
||||
/// <param name="transform">Transform to create the universal axes for</param>
|
||||
/// <param name="universalReference">
|
||||
/// The transform to use as a reference for the universal right, up and forward directions.
|
||||
/// </param>
|
||||
public UxrUniversalLocalAxes(Transform transform, Transform universalReference)
|
||||
{
|
||||
_transform = transform;
|
||||
|
||||
LocalRight = transform.InverseTransformDirection(universalReference != null ? universalReference.right : Vector3.right).GetClosestAxis();
|
||||
LocalUp = transform.InverseTransformDirection(universalReference != null ? universalReference.up : Vector3.up).GetClosestAxis();
|
||||
LocalForward = transform.InverseTransformDirection(universalReference != null ? universalReference.forward : Vector3.forward).GetClosestAxis();
|
||||
|
||||
UniversalToActualAxesRotation = GetUniversalToActualAxesRotation();
|
||||
|
||||
InitialRotation = transform.rotation;
|
||||
InitialLocalRotation = transform.localRotation;
|
||||
InitialLocalReferenceRotation = Quaternion.Inverse(universalReference.rotation) * transform.rotation;
|
||||
InitialUniversalLocalReferenceRotation = InitialLocalReferenceRotation * Quaternion.Inverse(UniversalToActualAxesRotation);
|
||||
InitialPosition = transform.position;
|
||||
InitialLocalPosition = transform.localPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor is private to make it inaccessible.
|
||||
/// </summary>
|
||||
private UxrUniversalLocalAxes()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates a UniversalLocalAxes object describing the universal local axes for the given transform.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transform the UniversalLocalAxes object is for</param>
|
||||
/// <param name="universalLocalRight">
|
||||
/// Which vector in the transform local coordinates points to the 'right' direction in the universal convention
|
||||
/// </param>
|
||||
/// <param name="universalLocalUp">
|
||||
/// Which vector in the transform local coordinates points to the 'up' direction in the universal convention
|
||||
/// </param>
|
||||
/// <param name="universalLocalForward">
|
||||
/// Which vector in the transform local coordinates points to the 'forward' direction in the universal convention
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// UniversalLocalAxes object that allows us to compute object rotations in a universal
|
||||
/// space and then apply it to a transform that can have any kind of axis convention
|
||||
/// (x may point up, z down...)
|
||||
/// </returns>
|
||||
public static UxrUniversalLocalAxes FromAxes(Transform transform, Vector3 universalLocalRight, Vector3 universalLocalUp, Vector3 universalLocalForward)
|
||||
{
|
||||
UxrUniversalLocalAxes localAxes = new UxrUniversalLocalAxes();
|
||||
|
||||
localAxes._transform = transform;
|
||||
localAxes.LocalRight = universalLocalRight;
|
||||
localAxes.LocalUp = universalLocalUp;
|
||||
localAxes.LocalForward = universalLocalForward;
|
||||
|
||||
localAxes.InitialRotation = transform.rotation;
|
||||
localAxes.InitialLocalRotation = transform.localRotation;
|
||||
localAxes.InitialLocalReferenceRotation = Quaternion.identity;
|
||||
localAxes.InitialUniversalLocalReferenceRotation = Quaternion.identity;
|
||||
localAxes.InitialPosition = transform.position;
|
||||
localAxes.InitialLocalPosition = transform.localPosition;
|
||||
|
||||
localAxes.UniversalToActualAxesRotation = localAxes.GetUniversalToActualAxesRotation();
|
||||
|
||||
return localAxes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="FromAxes" />.
|
||||
/// </summary>
|
||||
public static UxrUniversalLocalAxes FromRightUp(Transform transform, Vector3 universalLocalRight, Vector3 universalLocalUp)
|
||||
{
|
||||
return FromAxes(transform, universalLocalRight, universalLocalUp, Vector3.Cross(universalLocalRight, universalLocalUp));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="FromAxes" />.
|
||||
/// </summary>
|
||||
public static UxrUniversalLocalAxes FromRightForward(Transform transform, Vector3 universalLocalRight, Vector3 universalLocalForward)
|
||||
{
|
||||
return FromAxes(transform, universalLocalRight, Vector3.Cross(universalLocalForward, universalLocalRight), universalLocalForward);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="FromAxes" />.
|
||||
/// </summary>
|
||||
public static UxrUniversalLocalAxes FromUpForward(Transform transform, Vector3 universalLocalUp, Vector3 universalLocalForward)
|
||||
{
|
||||
return FromAxes(transform, Vector3.Cross(universalLocalUp, universalLocalForward), universalLocalUp, universalLocalForward);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Computes the rotation that transforms from the universal coordinate system to the convention that the transform
|
||||
/// follows.
|
||||
/// </summary>
|
||||
private Quaternion GetUniversalToActualAxesRotation()
|
||||
{
|
||||
Matrix4x4 matrix = new Matrix4x4();
|
||||
matrix.SetColumn(0, LocalRight);
|
||||
matrix.SetColumn(1, LocalUp);
|
||||
matrix.SetColumn(2, LocalForward);
|
||||
matrix.SetColumn(3, new Vector4(0, 0, 0, 1));
|
||||
|
||||
return Quaternion.Inverse(matrix.rotation);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92dff33db2971124397cffe499bf092a
|
||||
timeCreated: 1549525217
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user