diff options
Diffstat (limited to 'JamHelper/Assets/JamUtils/FastIK/Scripts')
14 files changed, 547 insertions, 0 deletions
diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK.meta b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK.meta new file mode 100644 index 0000000..a9848af --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 69d93e855b2f3534f90112f45a5e2180 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKCCD.cs b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKCCD.cs new file mode 100644 index 0000000..9fb4883 --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKCCD.cs @@ -0,0 +1,95 @@ +using UnityEngine; +/* +public class IKCCD : MonoBehaviour +{ + public int ChainLength = 2; + public Transform Target; + protected Quaternion TargetInitialRotation; + protected Quaternion EndInitialRotation; + public Transform Pole; + protected float CompleteLength; + + public int Iterations = 10; + public float Delta = 0.001f; + + protected Transform[] Bones; + //protected Quaternion[] InitialRotation; + + + // Start is called before the first frame update + void Awake() + { + //initial length + Bones = new Transform[ChainLength + 1]; + //InitialRotation = new Quaternion[ChainLength + 1]; + TargetInitialRotation = Target.rotation; + EndInitialRotation = transform.rotation; + + var current = transform; + CompleteLength = 0; + for (int i = ChainLength - 1; i >= 0; i--) + { + CompleteLength += (current.position - current.parent.position).magnitude; + Bones[i + 1] = current; + Bones[i] = current.parent; + //InitialRotation[i + 1] = current.rotation; + //InitialRotation[i] = current.parent.rotation; + current = current.parent; + } + if (Bones[0] == null) + throw new UnityException("The chain value is longer than the ancestor chain!"); + } + + // Update is called once per frame + void LateUpdate() + { + //CCD + var lastBone = Bones[Bones.Length - 1]; + + //for (var i = 0; i < Bones.Length; i++) + // Bones[i].rotation = InitialRotation[i]; + + for (int iteration = 0; iteration < Iterations; iteration++) + { + for (var i = Bones.Length - 1; i >= 0; i--) + { + //https://www.youtube.com/watch?v=MA1nT9RAF3k + + if (i == Bones.Length - 1) + { + Bones[i].rotation = Target.rotation * Quaternion.Inverse(TargetInitialRotation) * EndInitialRotation; + } + else + { + Bones[i].rotation = Quaternion.FromToRotation(lastBone.position - Bones[i].position, Target.position - Bones[i].position) * Bones[i].rotation; + + //jitter to solve strait line + //if (iteration == 5 && i == 0 && (Target.position - lastBone.position).sqrMagnitude > 0.01f && (Target.position - Bones[i].position).sqrMagnitude < CompleteLength * CompleteLength) + // Bones[i].rotation = Quaternion.AngleAxis(10, Vector3.up) * Bones[i].rotation; + + //move towards pole + if (Pole != null && i + 2 <= Bones.Length - 1) + { + var plane = new Plane(Bones[i + 2].position - Bones[i].position, Bones[i].position); + var projectedPole = plane.ClosestPointOnPlane(Pole.position); + var projectedBone = plane.ClosestPointOnPlane(Bones[i + 1].position); + if ((projectedBone - Bones[i].position).sqrMagnitude > 0.01f) + { + var angle = Vector3.SignedAngle(projectedBone - Bones[i].position, projectedPole - Bones[i].position, plane.normal); + Bones[i].rotation = Quaternion.AngleAxis(angle, plane.normal) * Bones[i].rotation; + } + } + } + + + //close enough? + if ((lastBone.position - Target.position).sqrMagnitude < Delta * Delta) + break; + } + } + + } + + +} +*/
\ No newline at end of file diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKCCD.cs.meta b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKCCD.cs.meta new file mode 100644 index 0000000..c7c447c --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKCCD.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12ebedeac55c4bb4aa003bfe2e00556e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKFabric.cs b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKFabric.cs new file mode 100644 index 0000000..a9f6ee1 --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKFabric.cs @@ -0,0 +1,250 @@ +#if UNITY_EDITOR +using UnityEditor; +#endif +using UnityEngine; + +namespace DitzelGames.FastIK +{ + /// <summary> + /// Fabrik IK Solver + /// </summary> + public class FastIKFabric : MonoBehaviour + { + /// <summary> + /// Chain length of bones + /// </summary> + public int ChainLength = 2; + + /// <summary> + /// Target the chain should bent to + /// </summary> + public Transform Target; + public Transform Pole; + + /// <summary> + /// Solver iterations per update + /// </summary> + [Header("Solver Parameters")] + public int Iterations = 10; + + /// <summary> + /// Distance when the solver stops + /// </summary> + public float Delta = 0.001f; + + /// <summary> + /// Strength of going back to the start position. + /// </summary> + [Range(0, 1)] + public float SnapBackStrength = 1f; + + + protected float[] BonesLength; //Target to Origin + protected float CompleteLength; + protected Transform[] Bones; + protected Vector3[] Positions; + protected Vector3[] StartDirectionSucc; + protected Quaternion[] StartRotationBone; + protected Quaternion StartRotationTarget; + protected Transform Root; + + + // Start is called before the first frame update + void Awake() + { + Init(); + } + + void Init() + { + //initial array + Bones = new Transform[ChainLength + 1]; + Positions = new Vector3[ChainLength + 1]; + BonesLength = new float[ChainLength]; + StartDirectionSucc = new Vector3[ChainLength + 1]; + StartRotationBone = new Quaternion[ChainLength + 1]; + + //find root + Root = transform; + for (var i = 0; i <= ChainLength; i++) + { + if (Root == null) + throw new UnityException("The chain value is longer than the ancestor chain!"); + Root = Root.parent; + } + + //init target + if (Target == null) + { + Target = new GameObject(gameObject.name + " Target").transform; + SetPositionRootSpace(Target, GetPositionRootSpace(transform)); + } + StartRotationTarget = GetRotationRootSpace(Target); + + + //init data + var current = transform; + CompleteLength = 0; + for (var i = Bones.Length - 1; i >= 0; i--) + { + Bones[i] = current; + StartRotationBone[i] = GetRotationRootSpace(current); + + if (i == Bones.Length - 1) + { + //leaf + StartDirectionSucc[i] = GetPositionRootSpace(Target) - GetPositionRootSpace(current); + } + else + { + //mid bone + StartDirectionSucc[i] = GetPositionRootSpace(Bones[i + 1]) - GetPositionRootSpace(current); + BonesLength[i] = StartDirectionSucc[i].magnitude; + CompleteLength += BonesLength[i]; + } + + current = current.parent; + } + + + + } + + // Update is called once per frame + void LateUpdate() + { + ResolveIK(); + } + + private void ResolveIK() + { + if (Target == null) + return; + + if (BonesLength.Length != ChainLength) + Init(); + + //Fabric + + // root + // (bone0) (bonelen 0) (bone1) (bonelen 1) (bone2)... + // x--------------------x--------------------x---... + + //get position + for (int i = 0; i < Bones.Length; i++) + Positions[i] = GetPositionRootSpace(Bones[i]); + + var targetPosition = GetPositionRootSpace(Target); + var targetRotation = GetRotationRootSpace(Target); + + //1st is possible to reach? + if ((targetPosition - GetPositionRootSpace(Bones[0])).sqrMagnitude >= CompleteLength * CompleteLength) + { + //just strech it + var direction = (targetPosition - Positions[0]).normalized; + //set everything after root + for (int i = 1; i < Positions.Length; i++) + Positions[i] = Positions[i - 1] + direction * BonesLength[i - 1]; + } + else + { + for (int i = 0; i < Positions.Length - 1; i++) + Positions[i + 1] = Vector3.Lerp(Positions[i + 1], Positions[i] + StartDirectionSucc[i], SnapBackStrength); + + for (int iteration = 0; iteration < Iterations; iteration++) + { + //https://www.youtube.com/watch?v=UNoX65PRehA + //back + for (int i = Positions.Length - 1; i > 0; i--) + { + if (i == Positions.Length - 1) + Positions[i] = targetPosition; //set it to target + else + Positions[i] = Positions[i + 1] + (Positions[i] - Positions[i + 1]).normalized * BonesLength[i]; //set in line on distance + } + + //forward + for (int i = 1; i < Positions.Length; i++) + Positions[i] = Positions[i - 1] + (Positions[i] - Positions[i - 1]).normalized * BonesLength[i - 1]; + + //close enough? + if ((Positions[Positions.Length - 1] - targetPosition).sqrMagnitude < Delta * Delta) + break; + } + } + + //move towards pole + if (Pole != null) + { + var polePosition = GetPositionRootSpace(Pole); + for (int i = 1; i < Positions.Length - 1; i++) + { + var plane = new Plane(Positions[i + 1] - Positions[i - 1], Positions[i - 1]); + var projectedPole = plane.ClosestPointOnPlane(polePosition); + var projectedBone = plane.ClosestPointOnPlane(Positions[i]); + var angle = Vector3.SignedAngle(projectedBone - Positions[i - 1], projectedPole - Positions[i - 1], plane.normal); + Positions[i] = Quaternion.AngleAxis(angle, plane.normal) * (Positions[i] - Positions[i - 1]) + Positions[i - 1]; + } + } + + //set position & rotation + for (int i = 0; i < Positions.Length; i++) + { + if (i == Positions.Length - 1) + SetRotationRootSpace(Bones[i], Quaternion.Inverse(targetRotation) * StartRotationTarget * Quaternion.Inverse(StartRotationBone[i])); + else + SetRotationRootSpace(Bones[i], Quaternion.FromToRotation(StartDirectionSucc[i], Positions[i + 1] - Positions[i]) * Quaternion.Inverse(StartRotationBone[i])); + SetPositionRootSpace(Bones[i], Positions[i]); + } + } + + private Vector3 GetPositionRootSpace(Transform current) + { + if (Root == null) + return current.position; + else + return Quaternion.Inverse(Root.rotation) * (current.position - Root.position); + } + + private void SetPositionRootSpace(Transform current, Vector3 position) + { + if (Root == null) + current.position = position; + else + current.position = Root.rotation * position + Root.position; + } + + private Quaternion GetRotationRootSpace(Transform current) + { + //inverse(after) * before => rot: before -> after + if (Root == null) + return current.rotation; + else + return Quaternion.Inverse(current.rotation) * Root.rotation; + } + + private void SetRotationRootSpace(Transform current, Quaternion rotation) + { + if (Root == null) + current.rotation = rotation; + else + current.rotation = Root.rotation * rotation; + } + + void OnDrawGizmos() + { +#if UNITY_EDITOR + var current = this.transform; + for (int i = 0; i < ChainLength && current != null && current.parent != null; i++) + { + var scale = Vector3.Distance(current.position, current.parent.position) * 0.1f; + Handles.matrix = Matrix4x4.TRS(current.position, Quaternion.FromToRotation(Vector3.up, current.parent.position - current.position), new Vector3(scale, Vector3.Distance(current.parent.position, current.position), scale)); + Handles.color = Color.green; + Handles.DrawWireCube(Vector3.up * 0.5f, Vector3.one); + current = current.parent; + } + } +#endif + + } +}
\ No newline at end of file diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKFabric.cs.meta b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKFabric.cs.meta new file mode 100644 index 0000000..05db1b4 --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKFabric.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03ca45bb53dc94f478358a1b6475653d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 200 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKLook.cs b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKLook.cs new file mode 100644 index 0000000..54ed1ed --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKLook.cs @@ -0,0 +1,40 @@ +using UnityEngine; + +namespace DitzelGames.FastIK +{ + public class FastIKLook : MonoBehaviour + { + /// <summary> + /// Look at target + /// </summary> + public Transform Target; + + /// <summary> + /// Initial direction + /// </summary> + protected Vector3 StartDirection; + + /// <summary> + /// Initial Rotation + /// </summary> + protected Quaternion StartRotation; + + void Awake() + { + if (Target == null) + return; + + StartDirection = Target.position - transform.position; + StartRotation = transform.rotation; + } + + void Update() + { + if (Target == null) + return; + + + transform.rotation = Quaternion.FromToRotation(StartDirection, Target.position - transform.position) * StartRotation; + } + } +} diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKLook.cs.meta b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKLook.cs.meta new file mode 100644 index 0000000..fa801cd --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/FastIK/FastIKLook.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5db2cc9d15d8e1c4e81fbc747c163d99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample.meta b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample.meta new file mode 100644 index 0000000..2fef29e --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f9ac29da80c8cfe4084775ebe4166f24 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleProcedualAnimation.cs b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleProcedualAnimation.cs new file mode 100644 index 0000000..01e38ff --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleProcedualAnimation.cs @@ -0,0 +1,45 @@ +using UnityEngine; + +namespace DitzelGames.FastIK +{ + class SampleProcedualAnimation : MonoBehaviour + { + public Transform[] FootTarget; + public Transform LookTarget; + public Transform HandTarget; + public Transform HandPole; + public Transform Step; + public Transform Attraction; + + public void LateUpdate() + { + //move step & attraction + Step.Translate(Vector3.forward * Time.deltaTime * 0.7f); + if (Step.position.z > 1f) + Step.position = Step.position + Vector3.forward * -2f; + Attraction.Translate(Vector3.forward * Time.deltaTime * 0.5f); + if (Attraction.position.z > 1f) + Attraction.position = Attraction.position + Vector3.forward * -2f; + + //footsteps + for(int i = 0; i < FootTarget.Length; i++) + { + var foot = FootTarget[i]; + var ray = new Ray(foot.transform.position + Vector3.up * 0.5f, Vector3.down); + var hitInfo = new RaycastHit(); + if(Physics.SphereCast(ray, 0.05f, out hitInfo, 0.50f)) + foot.position = hitInfo.point + Vector3.up * 0.05f; + } + + //hand and look + var normDist = Mathf.Clamp((Vector3.Distance(LookTarget.position, Attraction.position) - 0.3f) / 1f, 0, 1); + HandTarget.rotation = Quaternion.Lerp(Quaternion.Euler(90, 0, 0), HandTarget.rotation, normDist); + HandTarget.position = Vector3.Lerp(Attraction.position, HandTarget.position, normDist); + HandPole.position = Vector3.Lerp(HandTarget.position + Vector3.down * 2, HandTarget.position + Vector3.forward * 2f, normDist); + LookTarget.position = Vector3.Lerp(Attraction.position, LookTarget.position, normDist); + + + } + + } +} diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleProcedualAnimation.cs.meta b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleProcedualAnimation.cs.meta new file mode 100644 index 0000000..7f3ffb6 --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleProcedualAnimation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c9cd2c593f693345ad26b97d5a0d43f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 100 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleRotator.cs b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleRotator.cs new file mode 100644 index 0000000..62f201d --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleRotator.cs @@ -0,0 +1,14 @@ +using UnityEngine; + +namespace DitzelGames.FastIK +{ + public class SampleRotator : MonoBehaviour + { + + void Update() + { + //just rotate the object + transform.Rotate(0, Time.deltaTime * 90, 0); + } + } +} diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleRotator.cs.meta b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleRotator.cs.meta new file mode 100644 index 0000000..5fd4b94 --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleRotator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e9a7a99d0dda4cf438c3fd3db6627c08 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleSinMover.cs b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleSinMover.cs new file mode 100644 index 0000000..0607082 --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleSinMover.cs @@ -0,0 +1,21 @@ +using UnityEngine; + +namespace DitzelGames.FastIK +{ + public class SampleSinMover : MonoBehaviour + { + public Vector3 Dir; + public Vector3 Start; + + private void Awake() + { + Start = transform.position; + } + + void Update() + { + //just move the object from a to b and back + transform.position = Start + Dir * Mathf.Sin(Time.timeSinceLevelLoad); + } + } +} diff --git a/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleSinMover.cs.meta b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleSinMover.cs.meta new file mode 100644 index 0000000..9095f54 --- /dev/null +++ b/JamHelper/Assets/JamUtils/FastIK/Scripts/Sample/SampleSinMover.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf9a667200f2ae8459b397bc03e59350 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: |