您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
160 行
6.8 KiB
160 行
6.8 KiB
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.Experimental.Animations;
|
|
using UnityEngine.Serialization;
|
|
|
|
public struct FootIkJob : IAnimationJob
|
|
{
|
|
[Serializable]
|
|
public struct JobSettings
|
|
{
|
|
[Tooltip("Place over toe bone when in it's stand idle pose. " +
|
|
"Note that the Foot IK should not be enabled while adjusting!")]
|
|
[FormerlySerializedAs("leftToeIdlePos")]
|
|
public Vector3 leftToeStandPos;
|
|
|
|
[Tooltip("Place over toe bone when in it's stand idle pose. " +
|
|
"Note that the Foot IK should not be enabled while adjusting!")]
|
|
[FormerlySerializedAs("rightToeIdlePos")]
|
|
public Vector3 rightToeStandPos;
|
|
public bool debugIdlePos;
|
|
|
|
[Space(10)]
|
|
public bool enabled;
|
|
|
|
[Space(10)]
|
|
[Range(0f, 1f)]
|
|
public float emitRayOffset;
|
|
[Range(0f, 20f)]
|
|
public float maxRayDistance;
|
|
public bool debugRayCast;
|
|
|
|
[Space(10)]
|
|
[Range(0f, 1f)]
|
|
public float maxStepSize;
|
|
[Range(-90f, 90f)]
|
|
public float weightShiftAngle;
|
|
[Range(-1f, 1f)]
|
|
public float weightShiftHorizontal;
|
|
[Range(-1f, 1f)]
|
|
public float weightShiftVertical;
|
|
[Range(5f, 50f)]
|
|
public float maxFootRotationOffset;
|
|
|
|
[Space(10)]
|
|
[Range(0f, 1f)]
|
|
public float enterStateEaseIn;
|
|
}
|
|
|
|
public JobSettings settings;
|
|
|
|
public Vector2 ikOffset;
|
|
public Vector3 normalLeftFoot;
|
|
public Vector3 normalRightFoot;
|
|
|
|
public TransformStreamHandle leftToe;
|
|
public TransformStreamHandle rightToe;
|
|
|
|
public float ikWeight;
|
|
|
|
public bool Setup(Animator animator)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public void Dispose() { }
|
|
|
|
public void ProcessRootMotion(AnimationStream stream) { }
|
|
|
|
public void ProcessAnimation(AnimationStream stream)
|
|
{
|
|
if (!settings.enabled || AnimGraph_Stand.useFootIk.IntValue < 1)
|
|
return;
|
|
|
|
ikWeight = Mathf.Clamp01(ikWeight + (1 - settings.enterStateEaseIn));
|
|
|
|
if (stream.isHumanStream)
|
|
{
|
|
var human = stream.AsHuman();
|
|
|
|
var leftToePos = leftToe.GetPosition(stream);
|
|
var rightToePos = rightToe.GetPosition(stream);
|
|
|
|
var leftIkOffset = ikOffset.x * ikWeight;
|
|
var rightIkOffset = ikOffset.y * ikWeight;
|
|
|
|
leftToePos += new Vector3(0f, leftIkOffset, 0f);
|
|
rightToePos += new Vector3(0f, rightIkOffset, 0f);
|
|
|
|
|
|
var leftAnklePos = human.GetGoalPosition(AvatarIKGoal.LeftFoot);
|
|
var rightAnklePos = human.GetGoalPosition(AvatarIKGoal.RightFoot);
|
|
var leftAnkleRot = human.GetGoalRotation(AvatarIKGoal.LeftFoot);
|
|
var rightAnkleRot = human.GetGoalRotation(AvatarIKGoal.RightFoot);
|
|
var leftAnkleIkPos = new Vector3(leftAnklePos.x, leftAnklePos.y + leftIkOffset, leftAnklePos.z);
|
|
var rightAnkleIkPos = new Vector3(rightAnklePos.x, rightAnklePos.y + rightIkOffset, rightAnklePos.z);
|
|
|
|
var hipHeightOffset = (leftIkOffset + rightIkOffset) * 0.5f;
|
|
var forwardBackBias = (leftIkOffset - rightIkOffset) * settings.weightShiftHorizontal;
|
|
|
|
// TODO: (sunek) Rework weight shift to move towards actual lower foot?
|
|
hipHeightOffset += Mathf.Abs(leftIkOffset - rightIkOffset) * settings.weightShiftVertical;
|
|
var standAngle = Quaternion.AngleAxis(settings.weightShiftAngle, Vector3.up) * Vector3.forward;
|
|
human.bodyLocalPosition += new Vector3(standAngle.x * forwardBackBias, hipHeightOffset, standAngle.z * forwardBackBias);
|
|
|
|
// Figure out the normal rotation
|
|
var leftNormalRot = Quaternion.LookRotation(Vector3.forward, normalLeftFoot);
|
|
var rightNormalRot = Quaternion.LookRotation(Vector3.forward, normalRightFoot);
|
|
|
|
// Clamp normal rotation
|
|
var leftAngle = Quaternion.Angle(Quaternion.identity, leftNormalRot);
|
|
var rightAngle = Quaternion.Angle(Quaternion.identity, rightNormalRot);
|
|
|
|
if (leftAngle > settings.maxFootRotationOffset && settings.maxFootRotationOffset > 0f)
|
|
{
|
|
var fraction = settings.maxFootRotationOffset / leftAngle;
|
|
leftNormalRot = Quaternion.Lerp(Quaternion.identity, leftNormalRot, fraction);
|
|
}
|
|
|
|
if (rightAngle > settings.maxFootRotationOffset && settings.maxFootRotationOffset > 0f)
|
|
{
|
|
var fraction = settings.maxFootRotationOffset / rightAngle;
|
|
rightNormalRot = Quaternion.Lerp(Quaternion.identity, rightNormalRot, fraction);
|
|
}
|
|
|
|
// Apply rotation to ankle
|
|
var leftToesMatrix = Matrix4x4.TRS(leftToePos, Quaternion.identity, Vector3.one);
|
|
var rightToesMatrix = Matrix4x4.TRS(rightToePos, Quaternion.identity, Vector3.one);
|
|
|
|
var leftToesNormalDeltaMatrix = Matrix4x4.TRS(leftToePos, leftNormalRot, Vector3.one) * leftToesMatrix.inverse;
|
|
var rightToesNormalDeltaMatrix = Matrix4x4.TRS(rightToePos, rightNormalRot, Vector3.one) * rightToesMatrix.inverse;
|
|
|
|
var leftAnkleMatrix = Matrix4x4.TRS(leftAnkleIkPos, leftAnkleRot, Vector3.one) * leftToesMatrix.inverse;
|
|
var rightAnkleMatrix = Matrix4x4.TRS(rightAnkleIkPos, rightAnkleRot, Vector3.one) * rightToesMatrix.inverse;
|
|
|
|
leftAnkleMatrix = leftToesNormalDeltaMatrix * leftAnkleMatrix * leftToesMatrix;
|
|
rightAnkleMatrix = rightToesNormalDeltaMatrix * rightAnkleMatrix * rightToesMatrix;
|
|
|
|
leftAnkleIkPos = leftAnkleMatrix.GetColumn(3);
|
|
rightAnkleIkPos = rightAnkleMatrix.GetColumn(3);
|
|
|
|
leftAnkleRot = Quaternion.Lerp(leftAnkleRot, leftAnkleMatrix.rotation, ikWeight);
|
|
rightAnkleRot = Quaternion.Lerp(rightAnkleRot, rightAnkleMatrix.rotation, ikWeight);
|
|
|
|
// Update ik position
|
|
// TODO: (sunek) Consider combating leg overstretch
|
|
var leftPosition = Vector3.Lerp(leftAnklePos, leftAnkleIkPos, ikWeight);
|
|
var rightPosition = Vector3.Lerp(rightAnklePos, rightAnkleIkPos, ikWeight);
|
|
|
|
human.SetGoalPosition(AvatarIKGoal.LeftFoot, leftPosition);
|
|
human.SetGoalPosition(AvatarIKGoal.RightFoot, rightPosition);
|
|
human.SetGoalRotation(AvatarIKGoal.LeftFoot, leftAnkleRot);
|
|
human.SetGoalRotation(AvatarIKGoal.RightFoot, rightAnkleRot);
|
|
|
|
human.SetGoalWeightPosition(AvatarIKGoal.LeftFoot, 1f);
|
|
human.SetGoalWeightPosition(AvatarIKGoal.RightFoot, 1f);
|
|
human.SetGoalWeightRotation(AvatarIKGoal.LeftFoot, 1f);
|
|
human.SetGoalWeightRotation(AvatarIKGoal.RightFoot, 1f);
|
|
}
|
|
}
|
|
}
|