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.

233 lines
9.0 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();
}
}
}
}