first commit
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
namespace UnityEngine.Animations.Rigging
|
||||
{
|
||||
/// <summary>
|
||||
/// Rigid transform with only translation and rotation components.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct AffineTransform
|
||||
{
|
||||
/// <summary>Translation component of the AffineTransform.</summary>
|
||||
public Vector3 translation;
|
||||
/// <summary>Rotation component of the AffineTransform.</summary>
|
||||
public Quaternion rotation;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="t">Translation component of the AffineTransform.</param>
|
||||
/// <param name="r">Rotation component of the AffineTransform.</param>
|
||||
public AffineTransform(Vector3 t, Quaternion r)
|
||||
{
|
||||
translation = t;
|
||||
rotation = r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the translation and rotation in the AffineTransform.
|
||||
/// </summary>
|
||||
/// <param name="t">Translation component of the AffineTransform.</param>
|
||||
/// <param name="r">Rotation component of the AffineTransform.</param>
|
||||
public void Set(Vector3 t, Quaternion r)
|
||||
{
|
||||
translation = t;
|
||||
rotation = r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Vector3 point by the AffineTransform.
|
||||
/// </summary>
|
||||
/// <param name="p">Vector3 point.</param>
|
||||
/// <returns>Transformed Vector3 point.</returns>
|
||||
public Vector3 Transform(Vector3 p) =>
|
||||
rotation * p + translation;
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Vector3 point by the inverse of the AffineTransform.
|
||||
/// </summary>
|
||||
/// <param name="p">Vector3 point.</param>
|
||||
/// <returns>Transformed Vector3 point.</returns>
|
||||
public Vector3 InverseTransform(Vector3 p) =>
|
||||
Quaternion.Inverse(rotation) * (p - translation);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the inverse of the AffineTransform.
|
||||
/// </summary>
|
||||
/// <returns>The inverse of the AffineTransform.</returns>
|
||||
public AffineTransform Inverse()
|
||||
{
|
||||
var invR = Quaternion.Inverse(rotation);
|
||||
return new AffineTransform(invR * -translation, invR);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply a transform by the inverse of the AffineTransform.
|
||||
/// </summary>
|
||||
/// <param name="transform">AffineTransform value.</param>
|
||||
/// <returns>Multiplied AffineTransform result.</returns>
|
||||
public AffineTransform InverseMul(AffineTransform transform)
|
||||
{
|
||||
var invR = Quaternion.Inverse(rotation);
|
||||
return new AffineTransform(invR * (transform.translation - translation), invR * transform.rotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Vector3 point by the AffineTransform.
|
||||
/// </summary>
|
||||
/// <param name="lhs">AffineTransform value.</param>
|
||||
/// <param name="rhs">Vector3 point.</param>
|
||||
/// <returns>Transformed Vector3 point.</returns>
|
||||
public static Vector3 operator *(AffineTransform lhs, Vector3 rhs) =>
|
||||
lhs.rotation * rhs + lhs.translation;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two AffineTransform.
|
||||
/// </summary>
|
||||
/// <param name="lhs">First AffineTransform value.</param>
|
||||
/// <param name="rhs">Second AffineTransform value.</param>
|
||||
/// <returns>Multiplied AffineTransform result.</returns>
|
||||
public static AffineTransform operator *(AffineTransform lhs, AffineTransform rhs) =>
|
||||
new AffineTransform(lhs.Transform(rhs.translation), lhs.rotation * rhs.rotation);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates an AffineTransform.
|
||||
/// </summary>
|
||||
/// <param name="lhs">Quaternion rotation.</param>
|
||||
/// <param name="rhs">AffineTransform value.</param>
|
||||
/// <returns>Rotated AffineTransform result.</returns>
|
||||
public static AffineTransform operator *(Quaternion lhs, AffineTransform rhs) =>
|
||||
new AffineTransform(lhs * rhs.translation, lhs * rhs.rotation);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Quaternion value by the AffineTransform.
|
||||
/// </summary>
|
||||
/// <param name="lhs">AffineTransform value.</param>
|
||||
/// <param name="rhs">Quaternion rotation.</param>
|
||||
/// <returns>Transformed AffineTransform result.</returns>
|
||||
public static AffineTransform operator *(AffineTransform lhs, Quaternion rhs) =>
|
||||
new AffineTransform(lhs.translation, lhs.rotation * rhs);
|
||||
|
||||
/// <summary>
|
||||
/// AffineTransform identity value.
|
||||
/// </summary>
|
||||
public static AffineTransform identity { get; } = new AffineTransform(Vector3.zero, Quaternion.identity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9baa6934219a6645a3f6830ee9960fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,397 @@
|
||||
using Unity.Collections;
|
||||
|
||||
namespace UnityEngine.Animations.Rigging
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility functions for runtime constraints.
|
||||
/// </summary>
|
||||
public static class AnimationRuntimeUtils
|
||||
{
|
||||
const float k_SqrEpsilon = 1e-8f;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the Two-Bone IK algorithm.
|
||||
/// </summary>
|
||||
/// <param name="stream">The animation stream to work on.</param>
|
||||
/// <param name="root">The transform handle for the root transform.</param>
|
||||
/// <param name="mid">The transform handle for the mid transform.</param>
|
||||
/// <param name="tip">The transform handle for the tip transform.</param>
|
||||
/// <param name="target">The transform handle for the target transform.</param>
|
||||
/// <param name="hint">The transform handle for the hint transform.</param>
|
||||
/// <param name="posWeight">The weight for which target position has an effect on IK calculations. This is a value in between 0 and 1.</param>
|
||||
/// <param name="rotWeight">The weight for which target rotation has an effect on IK calculations. This is a value in between 0 and 1.</param>
|
||||
/// <param name="hintWeight">The weight for which hint transform has an effect on IK calculations. This is a value in between 0 and 1.</param>
|
||||
/// <param name="targetOffset">The offset applied to the target transform.</param>
|
||||
public static void SolveTwoBoneIK(
|
||||
AnimationStream stream,
|
||||
ReadWriteTransformHandle root,
|
||||
ReadWriteTransformHandle mid,
|
||||
ReadWriteTransformHandle tip,
|
||||
ReadOnlyTransformHandle target,
|
||||
ReadOnlyTransformHandle hint,
|
||||
float posWeight,
|
||||
float rotWeight,
|
||||
float hintWeight,
|
||||
AffineTransform targetOffset
|
||||
)
|
||||
{
|
||||
Vector3 aPosition = root.GetPosition(stream);
|
||||
Vector3 bPosition = mid.GetPosition(stream);
|
||||
Vector3 cPosition = tip.GetPosition(stream);
|
||||
target.GetGlobalTR(stream, out Vector3 targetPos, out Quaternion targetRot);
|
||||
Vector3 tPosition = Vector3.Lerp(cPosition, targetPos + targetOffset.translation, posWeight);
|
||||
Quaternion tRotation = Quaternion.Lerp(tip.GetRotation(stream), targetRot * targetOffset.rotation, rotWeight);
|
||||
bool hasHint = hint.IsValid(stream) && hintWeight > 0f;
|
||||
|
||||
Vector3 ab = bPosition - aPosition;
|
||||
Vector3 bc = cPosition - bPosition;
|
||||
Vector3 ac = cPosition - aPosition;
|
||||
Vector3 at = tPosition - aPosition;
|
||||
|
||||
float abLen = ab.magnitude;
|
||||
float bcLen = bc.magnitude;
|
||||
float acLen = ac.magnitude;
|
||||
float atLen = at.magnitude;
|
||||
|
||||
float oldAbcAngle = TriangleAngle(acLen, abLen, bcLen);
|
||||
float newAbcAngle = TriangleAngle(atLen, abLen, bcLen);
|
||||
|
||||
// Bend normal strategy is to take whatever has been provided in the animation
|
||||
// stream to minimize configuration changes, however if this is collinear
|
||||
// try computing a bend normal given the desired target position.
|
||||
// If this also fails, try resolving axis using hint if provided.
|
||||
Vector3 axis = Vector3.Cross(ab, bc);
|
||||
if (axis.sqrMagnitude < k_SqrEpsilon)
|
||||
{
|
||||
axis = hasHint ? Vector3.Cross(hint.GetPosition(stream) - aPosition, bc) : Vector3.zero;
|
||||
|
||||
if (axis.sqrMagnitude < k_SqrEpsilon)
|
||||
axis = Vector3.Cross(at, bc);
|
||||
|
||||
if (axis.sqrMagnitude < k_SqrEpsilon)
|
||||
axis = Vector3.up;
|
||||
}
|
||||
axis = Vector3.Normalize(axis);
|
||||
|
||||
float a = 0.5f * (oldAbcAngle - newAbcAngle);
|
||||
float sin = Mathf.Sin(a);
|
||||
float cos = Mathf.Cos(a);
|
||||
Quaternion deltaR = new Quaternion(axis.x * sin, axis.y * sin, axis.z * sin, cos);
|
||||
mid.SetRotation(stream, deltaR * mid.GetRotation(stream));
|
||||
|
||||
cPosition = tip.GetPosition(stream);
|
||||
ac = cPosition - aPosition;
|
||||
root.SetRotation(stream, QuaternionExt.FromToRotation(ac, at) * root.GetRotation(stream));
|
||||
|
||||
if (hasHint)
|
||||
{
|
||||
float acSqrMag = ac.sqrMagnitude;
|
||||
if (acSqrMag > 0f)
|
||||
{
|
||||
bPosition = mid.GetPosition(stream);
|
||||
cPosition = tip.GetPosition(stream);
|
||||
ab = bPosition - aPosition;
|
||||
ac = cPosition - aPosition;
|
||||
|
||||
Vector3 acNorm = ac / Mathf.Sqrt(acSqrMag);
|
||||
Vector3 ah = hint.GetPosition(stream) - aPosition;
|
||||
Vector3 abProj = ab - acNorm * Vector3.Dot(ab, acNorm);
|
||||
Vector3 ahProj = ah - acNorm * Vector3.Dot(ah, acNorm);
|
||||
|
||||
float maxReach = abLen + bcLen;
|
||||
if (abProj.sqrMagnitude > (maxReach * maxReach * 0.001f) && ahProj.sqrMagnitude > 0f)
|
||||
{
|
||||
Quaternion hintR = QuaternionExt.FromToRotation(abProj, ahProj);
|
||||
hintR.x *= hintWeight;
|
||||
hintR.y *= hintWeight;
|
||||
hintR.z *= hintWeight;
|
||||
hintR = QuaternionExt.NormalizeSafe(hintR);
|
||||
root.SetRotation(stream, hintR * root.GetRotation(stream));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tip.SetRotation(stream, tRotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the position for a hint and target given bone positions.
|
||||
/// </summary>
|
||||
/// <param name="stream">The animation stream to work on.</param>
|
||||
/// <param name="root">The transform handle for the root transform.</param>
|
||||
/// <param name="mid">The transform handle for the mid transform.</param>
|
||||
/// <param name="tip">The transform handle for the tip transform.</param>
|
||||
/// <param name="target">The transform handle for the target transform.</param>
|
||||
/// <param name="hint">The transform handle for the hint transform.</param>
|
||||
/// <param name="posWeight">The weight for which target position has an effect on IK calculations. This is a value in between 0 and 1.</param>
|
||||
/// <param name="rotWeight">The weight for which target rotation has an effect on IK calculations. This is a value in between 0 and 1.</param>
|
||||
/// <param name="hintWeight">The weight for which hint transform has an effect on IK calculations. This is a value in between 0 and 1.</param>
|
||||
/// <param name="targetOffset">The offset applied to the target transform.</param>
|
||||
public static void InverseSolveTwoBoneIK(
|
||||
AnimationStream stream,
|
||||
ReadOnlyTransformHandle root,
|
||||
ReadOnlyTransformHandle mid,
|
||||
ReadOnlyTransformHandle tip,
|
||||
ReadWriteTransformHandle target,
|
||||
ReadWriteTransformHandle hint,
|
||||
float posWeight,
|
||||
float rotWeight,
|
||||
float hintWeight,
|
||||
AffineTransform targetOffset
|
||||
)
|
||||
{
|
||||
Vector3 rootPosition = root.GetPosition(stream);
|
||||
Vector3 midPosition = mid.GetPosition(stream);
|
||||
tip.GetGlobalTR(stream, out var tipPosition, out var tipRotation);
|
||||
target.GetGlobalTR(stream, out var targetPosition, out var targetRotation);
|
||||
bool isHintValid = hint.IsValid(stream);
|
||||
Vector3 hintPosition = Vector3.zero;
|
||||
if(isHintValid)
|
||||
hintPosition = hint.GetPosition(stream);
|
||||
|
||||
InverseSolveTwoBoneIK(rootPosition, midPosition, tipPosition, tipRotation, ref targetPosition,
|
||||
ref targetRotation, ref hintPosition, isHintValid, posWeight, rotWeight, hintWeight, targetOffset);
|
||||
|
||||
target.SetPosition(stream, targetPosition);
|
||||
target.SetRotation(stream, targetRotation);
|
||||
hint.SetPosition(stream, hintPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the position for a hint and target for given bone positions.
|
||||
/// </summary>
|
||||
/// <param name="rootPosition">The position of the root bone.</param>
|
||||
/// <param name="midPosition">The position of the mid bone.</param>
|
||||
/// <param name="tipPosition">The position of the tip bone.</param>
|
||||
/// <param name="tipRotation">The rotation of the tip bone.</param>
|
||||
/// <param name="targetPosition">The position of the target.</param>
|
||||
/// <param name="targetRotation">The rotation of the target.</param>
|
||||
/// <param name="hintPosition">The position of the hint.</param>
|
||||
/// <param name="isHintValid">Whether the hint position should be set.</param>
|
||||
/// <param name="posWeight">The weight for which target position has an effect on IK calculations. This is a value in between 0 and 1.</param>
|
||||
/// <param name="rotWeight">The weight for which target rotation has an effect on IK calculations. This is a value in between 0 and 1.</param>
|
||||
/// <param name="hintWeight">The weight for which hint transform has an effect on IK calculations. This is a value in between 0 and 1.</param>
|
||||
/// <param name="targetOffset">The offset applied to the target transform.</param>
|
||||
public static void InverseSolveTwoBoneIK(
|
||||
Vector3 rootPosition,
|
||||
Vector3 midPosition,
|
||||
Vector3 tipPosition, Quaternion tipRotation,
|
||||
ref Vector3 targetPosition, ref Quaternion targetRotation,
|
||||
ref Vector3 hintPosition, bool isHintValid,
|
||||
float posWeight,
|
||||
float rotWeight,
|
||||
float hintWeight,
|
||||
AffineTransform targetOffset
|
||||
)
|
||||
{
|
||||
targetPosition = (posWeight > 0f) ? tipPosition + targetOffset.translation : targetPosition;
|
||||
targetRotation = (rotWeight > 0f) ? tipRotation * targetOffset.rotation : targetRotation;
|
||||
|
||||
if (isHintValid)
|
||||
{
|
||||
var ac = tipPosition - rootPosition;
|
||||
var ab = midPosition - rootPosition;
|
||||
var bc = tipPosition - midPosition;
|
||||
|
||||
float abLen = ab.magnitude;
|
||||
float bcLen = bc.magnitude;
|
||||
|
||||
var acSqrMag = Vector3.Dot(ac, ac);
|
||||
var projectionPoint = rootPosition;
|
||||
if (acSqrMag > k_SqrEpsilon)
|
||||
projectionPoint += Vector3.Dot(ab / acSqrMag, ac) * ac;
|
||||
var poleVectorDirection = midPosition - projectionPoint;
|
||||
|
||||
var scale = abLen + bcLen;
|
||||
hintPosition = (hintWeight > 0f) ? projectionPoint + (poleVectorDirection.normalized * scale) : hintPosition;
|
||||
}
|
||||
}
|
||||
|
||||
static float TriangleAngle(float aLen, float aLen1, float aLen2)
|
||||
{
|
||||
float c = Mathf.Clamp((aLen1 * aLen1 + aLen2 * aLen2 - aLen * aLen) / (aLen1 * aLen2) / 2.0f, -1.0f, 1.0f);
|
||||
return Mathf.Acos(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the FABRIK ChainIK algorithm.
|
||||
/// </summary>
|
||||
/// <param name="linkPositions">Uninitialized buffer of positions. linkPositions and linkLengths must have the same size.</param>
|
||||
/// <param name="linkLengths">Array of distances in between positions. linkPositions and linkLenghts must have the same size.</param>
|
||||
/// <param name="target">Target position.</param>
|
||||
/// <param name="tolerance">The maximum distance the resulting position and initial target are allowed to have in between them.</param>
|
||||
/// <param name="maxReach">The maximum distance the Transform chain can reach.</param>
|
||||
/// <param name="maxIterations">The maximum number of iterations allowed for the ChainIK algorithm to converge to a solution.</param>
|
||||
/// <returns>Returns true if ChainIK calculations were successful. False otherwise.</returns>
|
||||
/// <remarks>
|
||||
/// Implementation of unconstrained FABRIK solver : Forward and Backward Reaching Inverse Kinematic
|
||||
/// Aristidou A, Lasenby J. FABRIK: a fast, iterative solver for the inverse kinematics problem. Graphical Models 2011; 73(5): 243–260.
|
||||
/// </remarks>
|
||||
public static bool SolveFABRIK(
|
||||
ref NativeArray<Vector3> linkPositions,
|
||||
ref NativeArray<float> linkLengths,
|
||||
Vector3 target,
|
||||
float tolerance,
|
||||
float maxReach,
|
||||
int maxIterations
|
||||
)
|
||||
{
|
||||
// If the target is unreachable
|
||||
var rootToTargetDir = target - linkPositions[0];
|
||||
if (rootToTargetDir.sqrMagnitude > Square(maxReach))
|
||||
{
|
||||
// Line up chain towards target
|
||||
var dir = rootToTargetDir.normalized;
|
||||
for (int i = 1; i < linkPositions.Length; ++i)
|
||||
linkPositions[i] = linkPositions[i - 1] + dir * linkLengths[i - 1];
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int tipIndex = linkPositions.Length - 1;
|
||||
float sqrTolerance = Square(tolerance);
|
||||
if (SqrDistance(linkPositions[tipIndex], target) > sqrTolerance)
|
||||
{
|
||||
var rootPos = linkPositions[0];
|
||||
int iteration = 0;
|
||||
|
||||
do
|
||||
{
|
||||
// Forward reaching phase
|
||||
// Set tip to target and propagate displacement to rest of chain
|
||||
linkPositions[tipIndex] = target;
|
||||
for (int i = tipIndex - 1; i > -1; --i)
|
||||
linkPositions[i] = linkPositions[i + 1] + ((linkPositions[i] - linkPositions[i + 1]).normalized * linkLengths[i]);
|
||||
|
||||
// Backward reaching phase
|
||||
// Set root back at it's original position and propagate displacement to rest of chain
|
||||
linkPositions[0] = rootPos;
|
||||
for (int i = 1; i < linkPositions.Length; ++i)
|
||||
linkPositions[i] = linkPositions[i - 1] + ((linkPositions[i] - linkPositions[i - 1]).normalized * linkLengths[i - 1]);
|
||||
}
|
||||
while ((SqrDistance(linkPositions[tipIndex], target) > sqrTolerance) && (++iteration < maxIterations));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the square length between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs">Vector3 value.</param>
|
||||
/// <param name="rhs">Vector3 value.</param>
|
||||
/// <returns>Square length between lhs and rhs.</returns>
|
||||
public static float SqrDistance(Vector3 lhs, Vector3 rhs)
|
||||
{
|
||||
return (rhs - lhs).sqrMagnitude;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the square value of a float.
|
||||
/// </summary>
|
||||
/// <param name="value">Float value.</param>
|
||||
/// <returns>Squared value.</returns>
|
||||
public static float Square(float value)
|
||||
{
|
||||
return value * value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Linearly interpolates between two vectors using a vector interpolant.
|
||||
/// </summary>
|
||||
/// <param name="a">Start Vector3 value.</param>
|
||||
/// <param name="b">End Vector3 value.</param>
|
||||
/// <param name="t">Interpolant Vector3 value.</param>
|
||||
/// <returns>Interpolated value.</returns>
|
||||
public static Vector3 Lerp(Vector3 a, Vector3 b, Vector3 t)
|
||||
{
|
||||
return Vector3.Scale(a, Vector3.one - t) + Vector3.Scale(b, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns b if c is greater than zero, a otherwise.
|
||||
/// </summary>
|
||||
/// <param name="a">First float value.</param>
|
||||
/// <param name="b">Second float value.</param>
|
||||
/// <param name="c">Comparator float value.</param>
|
||||
/// <returns>Selected float value.</returns>
|
||||
public static float Select(float a, float b, float c)
|
||||
{
|
||||
return (c > 0f) ? b : a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a componentwise selection between two vectors a and b based on a vector selection mask c.
|
||||
/// Per component, the component from b is selected when c is greater than zero, otherwise the component from a is selected.
|
||||
/// </summary>
|
||||
/// <param name="a">First Vector3 value.</param>
|
||||
/// <param name="b">Second Vector3 value.</param>
|
||||
/// <param name="c">Comparator Vector3 value.</param>
|
||||
/// <returns>Selected Vector3 value.</returns>
|
||||
public static Vector3 Select(Vector3 a, Vector3 b, Vector3 c)
|
||||
{
|
||||
return new Vector3(Select(a.x, b.x, c.x), Select(a.y, b.y, c.y), Select(a.z, b.z, c.z));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects a vector onto a plane defined by a normal orthogonal to the plane.
|
||||
/// </summary>
|
||||
/// <param name="vector">The location of the vector above the plane.</param>
|
||||
/// <param name="planeNormal">The direction from the vector towards the plane.</param>
|
||||
/// <returns>The location of the vector on the plane.</returns>
|
||||
public static Vector3 ProjectOnPlane(Vector3 vector, Vector3 planeNormal)
|
||||
{
|
||||
float sqrMag = Vector3.Dot(planeNormal, planeNormal);
|
||||
var dot = Vector3.Dot(vector, planeNormal);
|
||||
return new Vector3(vector.x - planeNormal.x * dot / sqrMag,
|
||||
vector.y - planeNormal.y * dot / sqrMag,
|
||||
vector.z - planeNormal.z * dot / sqrMag);
|
||||
}
|
||||
|
||||
internal static float Sum(AnimationJobCache cache, CacheIndex index, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
return 0f;
|
||||
|
||||
float sum = 0f;
|
||||
for (int i = 0; i < count; ++i)
|
||||
sum += cache.GetRaw(index, i);
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the sum of all float elements in the array.
|
||||
/// </summary>
|
||||
/// <param name="floatBuffer">An array of float elements.</param>
|
||||
/// <returns>Sum of all float elements.</returns>
|
||||
public static float Sum(NativeArray<float> floatBuffer)
|
||||
{
|
||||
if (floatBuffer.Length == 0)
|
||||
return 0f;
|
||||
|
||||
float sum = 0f;
|
||||
for (int i = 0; i< floatBuffer.Length; ++i)
|
||||
{
|
||||
sum += floatBuffer[i];
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies translation, rotation and scale values from specified Transform handle to stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The animation stream to work on.</param>
|
||||
/// <param name="handle">The transform handle to copy.</param>
|
||||
public static void PassThrough(AnimationStream stream, ReadWriteTransformHandle handle)
|
||||
{
|
||||
handle.GetLocalTRS(stream, out Vector3 position, out Quaternion rotation, out Vector3 scale);
|
||||
handle.SetLocalTRS(stream, position, rotation, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c57b76759571b24baf139b32a086d87
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,203 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Animations.Rigging
|
||||
{
|
||||
/// <summary>
|
||||
/// The BoneRenderer component is responsible for displaying pickable bones in the Scene View.
|
||||
/// This component does nothing during runtime.
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Animation Rigging/Setup/Bone Renderer")]
|
||||
[HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/RiggingWorkflow.html#bone-renderer-component")]
|
||||
public class BoneRenderer : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Shape used by individual bones.
|
||||
/// </summary>
|
||||
public enum BoneShape
|
||||
{
|
||||
/// <summary>Bones are rendered with single lines.</summary>
|
||||
Line,
|
||||
|
||||
/// <summary>Bones are rendered with pyramid shapes.</summary>
|
||||
Pyramid,
|
||||
|
||||
/// <summary>Bones are rendered with box shapes.</summary>
|
||||
Box
|
||||
};
|
||||
|
||||
/// <summary>Shape of the bones.</summary>
|
||||
public BoneShape boneShape = BoneShape.Pyramid;
|
||||
|
||||
/// <summary>Toggles whether to render bone shapes or not.</summary>
|
||||
public bool drawBones = true;
|
||||
|
||||
/// <summary>Toggles whether to draw tripods on bones or not.</summary>
|
||||
public bool drawTripods = false;
|
||||
|
||||
/// <summary>Size of the bones.</summary>
|
||||
[Range(0.01f, 5.0f)] public float boneSize = 1.0f;
|
||||
|
||||
/// <summary>Size of the tripod axis.</summary>
|
||||
[Range(0.01f, 5.0f)] public float tripodSize = 1.0f;
|
||||
|
||||
/// <summary>Color of the bones.</summary>
|
||||
public Color boneColor = new Color(0f, 0f, 1f, 0.5f);
|
||||
|
||||
[SerializeField] private Transform[] m_Transforms;
|
||||
|
||||
/// <summary>Transform references in the BoneRenderer hierarchy that are used to build bones.</summary>
|
||||
public Transform[] transforms
|
||||
{
|
||||
get { return m_Transforms; }
|
||||
#if UNITY_EDITOR
|
||||
set
|
||||
{
|
||||
m_Transforms = value;
|
||||
ExtractBones();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Bone described by two Transform references.
|
||||
/// </summary>
|
||||
public struct TransformPair
|
||||
{
|
||||
public Transform first;
|
||||
public Transform second;
|
||||
};
|
||||
|
||||
private TransformPair[] m_Bones;
|
||||
private Transform[] m_Tips;
|
||||
|
||||
/// <summary>Retrieves the bones isolated from the Transform references.</summary>
|
||||
/// <seealso cref="BoneRenderer.transforms"/>
|
||||
public TransformPair[] bones
|
||||
{
|
||||
get => m_Bones;
|
||||
}
|
||||
|
||||
/// <summary>Retrieves the tip bones isolated from the Transform references.</summary>
|
||||
/// <seealso cref="BoneRenderer.transforms"/>
|
||||
public Transform[] tips
|
||||
{
|
||||
get => m_Tips;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate function that covers a BoneRenderer calling OnEnable.
|
||||
/// </summary>
|
||||
/// <param name="boneRenderer">The BoneRenderer component</param>
|
||||
public delegate void OnAddBoneRendererCallback(BoneRenderer boneRenderer);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate function that covers a BoneRenderer calling OnDisable.
|
||||
/// </summary>
|
||||
/// <param name="boneRenderer">The BoneRenderer component</param>
|
||||
public delegate void OnRemoveBoneRendererCallback(BoneRenderer boneRenderer);
|
||||
|
||||
/// <summary>
|
||||
/// Notification callback that is sent whenever a BoneRenderer calls OnEnable.
|
||||
/// </summary>
|
||||
public static OnAddBoneRendererCallback onAddBoneRenderer;
|
||||
|
||||
/// <summary>
|
||||
/// Notification callback that is sent whenever a BoneRenderer calls OnDisable.
|
||||
/// </summary>
|
||||
public static OnRemoveBoneRendererCallback onRemoveBoneRenderer;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
ExtractBones();
|
||||
onAddBoneRenderer?.Invoke(this);
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
onRemoveBoneRenderer?.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidate and Rebuild bones and tip bones from Transform references.
|
||||
/// </summary>
|
||||
public void Invalidate()
|
||||
{
|
||||
ExtractBones();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the BoneRenderer to default values.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
ClearBones();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears bones and tip bones.
|
||||
/// </summary>
|
||||
public void ClearBones()
|
||||
{
|
||||
m_Bones = null;
|
||||
m_Tips = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds bones and tip bones from Transform references.
|
||||
/// </summary>
|
||||
public void ExtractBones()
|
||||
{
|
||||
if (m_Transforms == null || m_Transforms.Length == 0)
|
||||
{
|
||||
ClearBones();
|
||||
return;
|
||||
}
|
||||
|
||||
var transformsHashSet = new HashSet<Transform>(m_Transforms);
|
||||
|
||||
var bonesList = new List<TransformPair>(m_Transforms.Length);
|
||||
var tipsList = new List<Transform>(m_Transforms.Length);
|
||||
|
||||
for (int i = 0; i < m_Transforms.Length; ++i)
|
||||
{
|
||||
bool hasValidChildren = false;
|
||||
|
||||
var transform = m_Transforms[i];
|
||||
if (transform == null)
|
||||
continue;
|
||||
|
||||
if (UnityEditor.SceneVisibilityManager.instance.IsHidden(transform.gameObject, false))
|
||||
continue;
|
||||
|
||||
var mask = UnityEditor.Tools.visibleLayers;
|
||||
if ((mask & (1 << transform.gameObject.layer)) == 0)
|
||||
continue;
|
||||
|
||||
if (transform.childCount > 0)
|
||||
{
|
||||
for (var k = 0; k < transform.childCount; ++k)
|
||||
{
|
||||
var childTransform = transform.GetChild(k);
|
||||
|
||||
if (transformsHashSet.Contains(childTransform))
|
||||
{
|
||||
bonesList.Add(new TransformPair() {first = transform, second = childTransform});
|
||||
hasValidChildren = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasValidChildren)
|
||||
{
|
||||
tipsList.Add(transform);
|
||||
}
|
||||
}
|
||||
|
||||
m_Bones = bonesList.ToArray();
|
||||
m_Tips = tipsList.ToArray();
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2d8418b0b9634b1892b0268dd9c2743
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Animations.Rigging
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility functions for constraints.
|
||||
/// </summary>
|
||||
public static class ConstraintsUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a list of Transforms all parented to one another in between two GameObjects.
|
||||
/// </summary>
|
||||
/// <param name="root">The root Transform.</param>
|
||||
/// <param name="tip">The tip Transform.</param>
|
||||
/// <returns></returns>
|
||||
public static Transform[] ExtractChain(Transform root, Transform tip)
|
||||
{
|
||||
if (!tip.IsChildOf(root))
|
||||
return new Transform[0]{};
|
||||
|
||||
var chain = new List<Transform>();
|
||||
|
||||
Transform tmp = tip;
|
||||
while (tmp != root)
|
||||
{
|
||||
chain.Add(tmp);
|
||||
tmp = tmp.parent;
|
||||
}
|
||||
chain.Add(root);
|
||||
chain.Reverse();
|
||||
|
||||
return chain.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distances in between every Transforms in the specified Transform chain.
|
||||
/// </summary>
|
||||
/// <param name="chain">The Transform chain.</param>
|
||||
/// <returns>An array of distances.</returns>
|
||||
public static float[] ExtractLengths(Transform[] chain)
|
||||
{
|
||||
float[] lengths = new float[chain.Length];
|
||||
lengths[0] = 0f;
|
||||
|
||||
// Evaluate lengths as distance between each transform in the chain.
|
||||
for (int i = 1; i < chain.Length; ++i)
|
||||
{
|
||||
lengths[i] = chain[i].localPosition.magnitude;
|
||||
}
|
||||
|
||||
return lengths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the interpolant values for each Transform using distance as a measure
|
||||
/// such that first Transform is at 0 and last Transform is at 1.
|
||||
/// </summary>
|
||||
/// <param name="chain">The Transform chain.</param>
|
||||
/// <returns>An array of interpolants.</returns>
|
||||
public static float[] ExtractSteps(Transform[] chain)
|
||||
{
|
||||
float[] lengths = ExtractLengths(chain);
|
||||
|
||||
float totalLength = 0f;
|
||||
Array.ForEach(lengths, (length) => totalLength += length);
|
||||
|
||||
float[] steps = new float[lengths.Length];
|
||||
|
||||
// Evaluate weights and steps based on curve.
|
||||
float cumulativeLength = 0.0f;
|
||||
for (int i = 0; i < lengths.Length; ++i)
|
||||
{
|
||||
cumulativeLength += lengths[i];
|
||||
|
||||
float t = cumulativeLength / totalLength;
|
||||
|
||||
steps[i] = t;
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepends RigConstraint data property to specified property name.
|
||||
/// </summary>
|
||||
/// <param name="property">Property name.</param>
|
||||
/// <returns>Return a complete property name.</returns>
|
||||
public static string ConstructConstraintDataPropertyName(string property)
|
||||
{
|
||||
return "m_Data." + property;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a unique property name for a custom property.
|
||||
/// </summary>
|
||||
/// <param name="component">Associated component.</param>
|
||||
/// <param name="property">Property name.</param>
|
||||
/// <returns>Returns a custom property name.</returns>
|
||||
public static string ConstructCustomPropertyName(Component component, string property)
|
||||
{
|
||||
return component.transform.GetInstanceID() + "/" + component.GetType() + "/" + property;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff1d1a6b9dacf476fb3597334c297c7b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
namespace UnityEngine.Animations.Rigging
|
||||
{
|
||||
/// <summary>
|
||||
/// Supplementary functions for Quaternion.
|
||||
/// </summary>
|
||||
public static class QuaternionExt
|
||||
{
|
||||
const float k_FloatMin = 1e-10f;
|
||||
|
||||
/// <summary>Zero quaternion. All quaternion channels are set to zero.</summary>
|
||||
public static readonly Quaternion zero = new Quaternion(0f, 0f, 0f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a Quaternion rotation of one vector to another.
|
||||
/// </summary>
|
||||
/// <param name="from">Starting vector.</param>
|
||||
/// <param name="to">Destination vector.</param>
|
||||
/// <returns>Quaternion rotation.</returns>
|
||||
public static Quaternion FromToRotation(Vector3 from, Vector3 to)
|
||||
{
|
||||
float theta = Vector3.Dot(from.normalized, to.normalized);
|
||||
if (theta >= 1f)
|
||||
return Quaternion.identity;
|
||||
|
||||
if (theta <= -1f)
|
||||
{
|
||||
Vector3 axis = Vector3.Cross(from, Vector3.right);
|
||||
if (axis.sqrMagnitude == 0f)
|
||||
axis = Vector3.Cross(from, Vector3.up);
|
||||
|
||||
return Quaternion.AngleAxis(180f, axis);
|
||||
}
|
||||
|
||||
return Quaternion.AngleAxis(Mathf.Acos(theta) * Mathf.Rad2Deg, Vector3.Cross(from, to).normalized);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two quaternion.
|
||||
/// </summary>
|
||||
/// <param name="rhs">Quaternion value.</param>
|
||||
/// <param name="lhs">Quaternion value.</param>
|
||||
/// <returns>Added Quaternion.</returns>
|
||||
public static Quaternion Add(Quaternion rhs, Quaternion lhs)
|
||||
{
|
||||
float sign = Mathf.Sign(Quaternion.Dot(rhs, lhs));
|
||||
return new Quaternion(rhs.x + sign * lhs.x, rhs.y + sign * lhs.y, rhs.z + sign * lhs.z, rhs.w + sign * lhs.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies all Quaternion channels by a scale value.
|
||||
/// </summary>
|
||||
/// <param name="q">Quaternion value.</param>
|
||||
/// <param name="scale">Scale value.</param>
|
||||
/// <returns>Scaled Quaternion.</returns>
|
||||
public static Quaternion Scale(Quaternion q, float scale)
|
||||
{
|
||||
return new Quaternion(q.x * scale, q.y * scale, q.z * scale, q.w * scale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a Quaternion. Returns identity if normalized Quaternion is not finite.
|
||||
/// </summary>
|
||||
/// <param name="q">Quaternion value.</param>
|
||||
/// <returns>Normalized Quaternion.</returns>
|
||||
public static Quaternion NormalizeSafe(Quaternion q)
|
||||
{
|
||||
float dot = Quaternion.Dot(q, q);
|
||||
if (dot > k_FloatMin)
|
||||
{
|
||||
float rsqrt = 1.0f / Mathf.Sqrt(dot);
|
||||
return new Quaternion(q.x * rsqrt, q.y * rsqrt, q.z * rsqrt, q.w * rsqrt);
|
||||
}
|
||||
|
||||
return Quaternion.identity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b62601624da18e54fb7fbc031f4ac8ae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace UnityEngine.Animations.Rigging
|
||||
{
|
||||
/// <summary>
|
||||
/// Three-dimensional boolean vector.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct Vector3Bool
|
||||
{
|
||||
/// <summary>X component of the vector.</summary>
|
||||
public bool x;
|
||||
/// <summary>Y component of the vector.</summary>
|
||||
public bool y;
|
||||
/// <summary>Z component of the vector.</summary>
|
||||
public bool z;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="val">Boolean value for x, y and z.</param>
|
||||
public Vector3Bool(bool val)
|
||||
{
|
||||
x = y = z = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="x">Boolean value for x.</param>
|
||||
/// <param name="y">Boolean value for y.</param>
|
||||
/// <param name="z">Boolean value for z.</param>
|
||||
public Vector3Bool(bool x, bool y, bool z)
|
||||
{
|
||||
this.x = x; this.y = y; this.z = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 545e69336e8e62e47817399d2c47b673
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user