233 lines
9 KiB
C#
233 lines
9 KiB
C#
|
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
|
|||
|
|
|||
|
using UnityEngine;
|
|||
|
using System.Collections;
|
|||
|
|
|||
|
namespace Valve.VR.InteractionSystem.Sample
|
|||
|
{
|
|||
|
public class FloppyHand : MonoBehaviour
|
|||
|
{
|
|||
|
protected float fingerFlexAngle = 140;
|
|||
|
|
|||
|
public SteamVR_Action_Single squeezyAction = SteamVR_Input.GetAction<SteamVR_Action_Single>("Squeeze");
|
|||
|
public SteamVR_Input_Sources inputSource;
|
|||
|
|
|||
|
[System.Serializable]
|
|||
|
public class Finger
|
|||
|
{
|
|||
|
public float mass;
|
|||
|
|
|||
|
[Range(0, 1)]
|
|||
|
public float pos;
|
|||
|
|
|||
|
public Vector3 forwardAxis;
|
|||
|
|
|||
|
public SkinnedMeshRenderer renderer;
|
|||
|
[HideInInspector]
|
|||
|
public SteamVR_Action_Single squeezyAction;
|
|||
|
public SteamVR_Input_Sources inputSource;
|
|||
|
|
|||
|
public Transform[] bones;
|
|||
|
public Transform referenceBone;
|
|||
|
public Vector2 referenceAngles;
|
|||
|
|
|||
|
public enum eulerAxis
|
|||
|
{
|
|||
|
X, Y, Z
|
|||
|
}
|
|||
|
public eulerAxis referenceAxis;
|
|||
|
|
|||
|
|
|||
|
[HideInInspector]
|
|||
|
public float flexAngle;
|
|||
|
|
|||
|
private Vector3[] rotation;
|
|||
|
private Vector3[] velocity;
|
|||
|
private Transform[] boneTips;
|
|||
|
private Vector3[] oldTipPosition;
|
|||
|
private Vector3[] oldTipDelta;
|
|||
|
private Vector3[,] inertiaSmoothing;
|
|||
|
|
|||
|
float squeezySmooth;
|
|||
|
|
|||
|
private int inertiaSteps = 10;
|
|||
|
private float k = 400;
|
|||
|
private float damping = 8;
|
|||
|
private Quaternion[] startRot;
|
|||
|
|
|||
|
public void ApplyForce(Vector3 worldForce)
|
|||
|
{
|
|||
|
for (int i = 0; i < startRot.Length; i++)
|
|||
|
{
|
|||
|
velocity[i] += worldForce / 50;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public void Init()
|
|||
|
{
|
|||
|
startRot = new Quaternion[bones.Length];
|
|||
|
rotation = new Vector3[bones.Length];
|
|||
|
velocity = new Vector3[bones.Length];
|
|||
|
oldTipPosition = new Vector3[bones.Length];
|
|||
|
oldTipDelta = new Vector3[bones.Length];
|
|||
|
boneTips = new Transform[bones.Length];
|
|||
|
inertiaSmoothing = new Vector3[bones.Length, inertiaSteps];
|
|||
|
for (int i = 0; i < bones.Length; i++)
|
|||
|
{
|
|||
|
startRot[i] = bones[i].localRotation;
|
|||
|
if (i < bones.Length - 1)
|
|||
|
{
|
|||
|
boneTips[i] = bones[i + 1];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void UpdateFinger(float deltaTime)
|
|||
|
{
|
|||
|
if (deltaTime == 0)
|
|||
|
return;
|
|||
|
|
|||
|
float squeezeValue = 0;
|
|||
|
if (squeezyAction != null && squeezyAction.GetActive(inputSource))
|
|||
|
squeezeValue = squeezyAction.GetAxis(inputSource);
|
|||
|
|
|||
|
squeezySmooth = Mathf.Lerp(squeezySmooth, Mathf.Sqrt(squeezeValue), deltaTime * 10);
|
|||
|
|
|||
|
if (renderer.sharedMesh.blendShapeCount > 0)
|
|||
|
{
|
|||
|
renderer.SetBlendShapeWeight(0, squeezySmooth * 100);
|
|||
|
}
|
|||
|
|
|||
|
float boneRot = 0;
|
|||
|
if (referenceAxis == eulerAxis.X)
|
|||
|
boneRot = referenceBone.localEulerAngles.x;
|
|||
|
if (referenceAxis == eulerAxis.Y)
|
|||
|
boneRot = referenceBone.localEulerAngles.y;
|
|||
|
if (referenceAxis == eulerAxis.Z)
|
|||
|
boneRot = referenceBone.localEulerAngles.z;
|
|||
|
boneRot = FixAngle(boneRot);
|
|||
|
|
|||
|
pos = Mathf.InverseLerp(referenceAngles.x, referenceAngles.y, boneRot);
|
|||
|
|
|||
|
if (mass > 0)
|
|||
|
{
|
|||
|
for (int boneIndex = 0; boneIndex < bones.Length; boneIndex++)
|
|||
|
{
|
|||
|
bool useOffset = boneTips[boneIndex] != null;
|
|||
|
if (useOffset) // inertia sim
|
|||
|
{
|
|||
|
Vector3 offset = (boneTips[boneIndex].localPosition - bones[boneIndex].InverseTransformPoint(oldTipPosition[boneIndex])) / deltaTime;
|
|||
|
Vector3 inertia = (offset - oldTipDelta[boneIndex]) / deltaTime;
|
|||
|
oldTipDelta[boneIndex] = offset;
|
|||
|
|
|||
|
Vector3 drag = offset * -2;
|
|||
|
inertia *= -2f;
|
|||
|
|
|||
|
for (int offsetIndex = inertiaSteps - 1; offsetIndex > 0; offsetIndex--) // offset inertia steps
|
|||
|
{
|
|||
|
inertiaSmoothing[boneIndex, offsetIndex] = inertiaSmoothing[boneIndex, offsetIndex - 1];
|
|||
|
}
|
|||
|
inertiaSmoothing[boneIndex, 0] = inertia;
|
|||
|
|
|||
|
Vector3 smoothedInertia = Vector3.zero;
|
|||
|
for (int offsetIndex = 0; offsetIndex < inertiaSteps; offsetIndex++) // offset inertia steps
|
|||
|
{
|
|||
|
smoothedInertia += inertiaSmoothing[boneIndex, offsetIndex];
|
|||
|
}
|
|||
|
|
|||
|
smoothedInertia = smoothedInertia / inertiaSteps;
|
|||
|
//if (boneIndex == 0 && Input.GetKey(KeyCode.Space))
|
|||
|
// Debug.Log(smoothedInertia);
|
|||
|
smoothedInertia = PowVector(smoothedInertia / 20, 3) * 20;
|
|||
|
|
|||
|
Vector3 forward = forwardAxis;
|
|||
|
Vector3 forwardDrag = forwardAxis + drag;
|
|||
|
Vector3 forwardInertia = forwardAxis + smoothedInertia;
|
|||
|
Quaternion dragQuaternion = Quaternion.FromToRotation(forward, forwardDrag);
|
|||
|
Quaternion inertiaQuaternion = Quaternion.FromToRotation(forward, forwardInertia);
|
|||
|
velocity[boneIndex] += FixVector(dragQuaternion.eulerAngles) * 2 * deltaTime;
|
|||
|
velocity[boneIndex] += FixVector(inertiaQuaternion.eulerAngles) * 50 * deltaTime;
|
|||
|
velocity[boneIndex] = Vector3.ClampMagnitude(velocity[boneIndex], 1000);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
Vector3 targetPos = pos * Vector3.right * (flexAngle / bones.Length);
|
|||
|
|
|||
|
Vector3 springForce = -k * (rotation[boneIndex] - targetPos);
|
|||
|
var dampingForce = damping * velocity[boneIndex];
|
|||
|
var force = springForce - dampingForce;
|
|||
|
var acceleration = force / mass;
|
|||
|
velocity[boneIndex] += acceleration * deltaTime;
|
|||
|
rotation[boneIndex] += velocity[boneIndex] * Time.deltaTime;
|
|||
|
rotation[boneIndex] = Vector3.ClampMagnitude(rotation[boneIndex], 180);
|
|||
|
if (useOffset)
|
|||
|
{
|
|||
|
oldTipPosition[boneIndex] = boneTips[boneIndex].position;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogError("<b>[SteamVR Interaction]</b> finger mass is zero");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void ApplyTransforms()
|
|||
|
{
|
|||
|
for (int i = 0; i < bones.Length; i++)
|
|||
|
{
|
|||
|
bones[i].localRotation = startRot[i];
|
|||
|
bones[i].Rotate(rotation[i], Space.Self);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private Vector3 FixVector(Vector3 ang)
|
|||
|
{
|
|||
|
return new Vector3(FixAngle(ang.x), FixAngle(ang.y), FixAngle(ang.z));
|
|||
|
}
|
|||
|
|
|||
|
private float FixAngle(float ang)
|
|||
|
{
|
|||
|
if (ang > 180)
|
|||
|
ang = -360 + ang;
|
|||
|
return ang;
|
|||
|
}
|
|||
|
|
|||
|
private Vector3 PowVector(Vector3 vector, float power)
|
|||
|
{
|
|||
|
Vector3 sign = new Vector3(Mathf.Sign(vector.x), Mathf.Sign(vector.y), Mathf.Sign(vector.z));
|
|||
|
vector.x = Mathf.Pow(Mathf.Abs(vector.x), power) * sign.x;
|
|||
|
vector.y = Mathf.Pow(Mathf.Abs(vector.y), power) * sign.y;
|
|||
|
vector.z = Mathf.Pow(Mathf.Abs(vector.z), power) * sign.z;
|
|||
|
return vector;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Finger[] fingers;
|
|||
|
|
|||
|
public Vector3 constforce;
|
|||
|
|
|||
|
private void Start()
|
|||
|
{
|
|||
|
for (int fingerIndex = 0; fingerIndex < fingers.Length; fingerIndex++)
|
|||
|
{
|
|||
|
fingers[fingerIndex].Init();
|
|||
|
fingers[fingerIndex].flexAngle = fingerFlexAngle;
|
|||
|
fingers[fingerIndex].squeezyAction = squeezyAction;
|
|||
|
fingers[fingerIndex].inputSource = inputSource;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void Update()
|
|||
|
{
|
|||
|
for (int fingerIndex = 0; fingerIndex < fingers.Length; fingerIndex++)
|
|||
|
{
|
|||
|
fingers[fingerIndex].ApplyForce(constforce);
|
|||
|
fingers[fingerIndex].UpdateFinger(Time.deltaTime);
|
|||
|
fingers[fingerIndex].ApplyTransforms();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|