You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			172 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			172 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C#
		
	
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
 | 
						|
//
 | 
						|
// Purpose: Simple two bone ik solver.
 | 
						|
//
 | 
						|
//=============================================================================
 | 
						|
 | 
						|
using UnityEngine;
 | 
						|
 | 
						|
namespace Valve.VR
 | 
						|
{
 | 
						|
    public class SteamVR_IK : MonoBehaviour
 | 
						|
    {
 | 
						|
        public Transform target;
 | 
						|
        public Transform start, joint, end;
 | 
						|
        public Transform poleVector, upVector;
 | 
						|
 | 
						|
        public float blendPct = 1.0f;
 | 
						|
 | 
						|
        [HideInInspector]
 | 
						|
        public Transform startXform, jointXform, endXform;
 | 
						|
 | 
						|
        void LateUpdate()
 | 
						|
        {
 | 
						|
            const float epsilon = 0.001f;
 | 
						|
            if (blendPct < epsilon)
 | 
						|
                return;
 | 
						|
 | 
						|
            var preUp = upVector ? upVector.up : Vector3.Cross(end.position - start.position, joint.position - start.position).normalized;
 | 
						|
 | 
						|
            var targetPosition = target.position;
 | 
						|
            var targetRotation = target.rotation;
 | 
						|
 | 
						|
            Vector3 forward, up, result = joint.position;
 | 
						|
            Solve(start.position, targetPosition, poleVector.position,
 | 
						|
                (joint.position - start.position).magnitude,
 | 
						|
                (end.position - joint.position).magnitude,
 | 
						|
                ref result, out forward, out up);
 | 
						|
 | 
						|
            if (up == Vector3.zero)
 | 
						|
                return;
 | 
						|
 | 
						|
            var startPosition = start.position;
 | 
						|
            var jointPosition = joint.position;
 | 
						|
            var endPosition = end.position;
 | 
						|
 | 
						|
            var startRotationLocal = start.localRotation;
 | 
						|
            var jointRotationLocal = joint.localRotation;
 | 
						|
            var endRotationLocal = end.localRotation;
 | 
						|
 | 
						|
            var startParent = start.parent;
 | 
						|
            var jointParent = joint.parent;
 | 
						|
            var endParent = end.parent;
 | 
						|
 | 
						|
            var startScale = start.localScale;
 | 
						|
            var jointScale = joint.localScale;
 | 
						|
            var endScale = end.localScale;
 | 
						|
 | 
						|
            if (startXform == null)
 | 
						|
            {
 | 
						|
                startXform = new GameObject("startXform").transform;
 | 
						|
                startXform.parent = transform;
 | 
						|
            }
 | 
						|
 | 
						|
            startXform.position = startPosition;
 | 
						|
            startXform.LookAt(joint, preUp);
 | 
						|
            start.parent = startXform;
 | 
						|
 | 
						|
            if (jointXform == null)
 | 
						|
            {
 | 
						|
                jointXform = new GameObject("jointXform").transform;
 | 
						|
                jointXform.parent = startXform;
 | 
						|
            }
 | 
						|
 | 
						|
            jointXform.position = jointPosition;
 | 
						|
            jointXform.LookAt(end, preUp);
 | 
						|
            joint.parent = jointXform;
 | 
						|
 | 
						|
            if (endXform == null)
 | 
						|
            {
 | 
						|
                endXform = new GameObject("endXform").transform;
 | 
						|
                endXform.parent = jointXform;
 | 
						|
            }
 | 
						|
 | 
						|
            endXform.position = endPosition;
 | 
						|
            end.parent = endXform;
 | 
						|
 | 
						|
            startXform.LookAt(result, up);
 | 
						|
            jointXform.LookAt(targetPosition, up);
 | 
						|
            endXform.rotation = targetRotation;
 | 
						|
 | 
						|
            start.parent = startParent;
 | 
						|
            joint.parent = jointParent;
 | 
						|
            end.parent = endParent;
 | 
						|
 | 
						|
            end.rotation = targetRotation; // optionally blend?
 | 
						|
 | 
						|
            // handle blending in/out
 | 
						|
            if (blendPct < 1.0f)
 | 
						|
            {
 | 
						|
                start.localRotation = Quaternion.Slerp(startRotationLocal, start.localRotation, blendPct);
 | 
						|
                joint.localRotation = Quaternion.Slerp(jointRotationLocal, joint.localRotation, blendPct);
 | 
						|
                end.localRotation = Quaternion.Slerp(endRotationLocal, end.localRotation, blendPct);
 | 
						|
            }
 | 
						|
 | 
						|
            // restore scale so it doesn't blow out
 | 
						|
            start.localScale = startScale;
 | 
						|
            joint.localScale = jointScale;
 | 
						|
            end.localScale = endScale;
 | 
						|
        }
 | 
						|
 | 
						|
        public static bool Solve(
 | 
						|
            Vector3 start, // shoulder / hip
 | 
						|
            Vector3 end, // desired hand / foot position
 | 
						|
            Vector3 poleVector, // point to aim elbow / knee toward
 | 
						|
            float jointDist, // distance from start to elbow / knee
 | 
						|
            float targetDist, // distance from joint to hand / ankle
 | 
						|
            ref Vector3 result, // original and output elbow / knee position
 | 
						|
            out Vector3 forward, out Vector3 up) // plane formed by root, joint and target
 | 
						|
        {
 | 
						|
            var totalDist = jointDist + targetDist;
 | 
						|
            var start2end = end - start;
 | 
						|
            var poleVectorDir = (poleVector - start).normalized;
 | 
						|
            var baseDist = start2end.magnitude;
 | 
						|
 | 
						|
            result = start;
 | 
						|
 | 
						|
            const float epsilon = 0.001f;
 | 
						|
            if (baseDist < epsilon)
 | 
						|
            {
 | 
						|
                // move jointDist toward jointTarget
 | 
						|
                result += poleVectorDir * jointDist;
 | 
						|
 | 
						|
                forward = Vector3.Cross(poleVectorDir, Vector3.up);
 | 
						|
                up = Vector3.Cross(forward, poleVectorDir).normalized;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                forward = start2end * (1.0f / baseDist);
 | 
						|
                up = Vector3.Cross(forward, poleVectorDir).normalized;
 | 
						|
 | 
						|
                if (baseDist + epsilon < totalDist)
 | 
						|
                {
 | 
						|
                    // calculate the area of the triangle to determine its height
 | 
						|
                    var p = (totalDist + baseDist) * 0.5f; // half perimeter
 | 
						|
                    if (p > jointDist + epsilon && p > targetDist + epsilon)
 | 
						|
                    {
 | 
						|
                        var A = Mathf.Sqrt(p * (p - jointDist) * (p - targetDist) * (p - baseDist));
 | 
						|
                        var height = 2.0f * A / baseDist; // distance of joint from line between root and target
 | 
						|
 | 
						|
                        var dist = Mathf.Sqrt((jointDist * jointDist) - (height * height));
 | 
						|
                        var right = Vector3.Cross(up, forward); // no need to normalized - already orthonormal
 | 
						|
 | 
						|
                        result += (forward * dist) + (right * height);
 | 
						|
                        return true; // in range
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        // move jointDist toward jointTarget
 | 
						|
                        result += poleVectorDir * jointDist;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    // move elboDist toward target
 | 
						|
                    result += forward * jointDist;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return false; // edge cases
 | 
						|
        }
 | 
						|
    }
 | 
						|
} |