123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- //======= Copyright (c) Valve Corporation, All rights reserved. ===============
- //
- // Purpose: handles the physics of hands colliding with the world
- //
- //=============================================================================
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- namespace Valve.VR.InteractionSystem
- {
- public class HandPhysics : MonoBehaviour
- {
- [Tooltip("Hand collider prefab to instantiate")]
- public HandCollider handColliderPrefab;
- [HideInInspector]
- public HandCollider handCollider;
- [Tooltip("Layers to consider when checking if an area is clear")]
- public LayerMask clearanceCheckMask;
- [HideInInspector]
- public Hand hand;
- // distance at which hand will teleport back to controller
- const float handResetDistance = 0.6f;
- const float collisionReenableClearanceRadius = 0.1f;
- private bool initialized = false;
- private bool collisionsEnabled = true;
- private void Start()
- {
- hand = GetComponent<Hand>();
- //spawn hand collider and link it to us
-
- handCollider = ((GameObject)Instantiate(handColliderPrefab.gameObject)).GetComponent<HandCollider>();
- Vector3 localPosition = handCollider.transform.localPosition;
- Quaternion localRotation = handCollider.transform.localRotation;
- handCollider.transform.parent = Player.instance.transform;
- handCollider.transform.localPosition = localPosition;
- handCollider.transform.localRotation = localRotation;
- handCollider.hand = this;
- GetComponent<SteamVR_Behaviour_Pose>().onTransformUpdated.AddListener(UpdateHand);
- }
- // cached transformations
- Matrix4x4 wristToRoot;
- Matrix4x4 rootToArmature;
- Matrix4x4 wristToArmature;
- Vector3 targetPosition = Vector3.zero;
- Quaternion targetRotation = Quaternion.identity;
- //bones
- const int wristBone = SteamVR_Skeleton_JointIndexes.wrist;
- const int rootBone = SteamVR_Skeleton_JointIndexes.root;
- private void FixedUpdate()
- {
- if (hand.skeleton == null) return;
- initialized = true;
- UpdateCenterPoint();
- handCollider.MoveTo(targetPosition, targetRotation);
- if ((handCollider.transform.position - targetPosition).sqrMagnitude > handResetDistance * handResetDistance)
- handCollider.TeleportTo(targetPosition, targetRotation);
- UpdateFingertips();
- }
- private void UpdateCenterPoint()
- {
- Vector3 offset = hand.skeleton.GetBonePosition(SteamVR_Skeleton_JointIndexes.middleProximal) - hand.skeleton.GetBonePosition(SteamVR_Skeleton_JointIndexes.root);
- if (hand.HasSkeleton())
- {
- handCollider.SetCenterPoint(hand.skeleton.transform.position + offset);
- }
- }
- Collider[] clearanceBuffer = new Collider[1];
- private void UpdatePositions()
- {
- // disable collisions when holding something
- if (hand.currentAttachedObject != null)
- {
- collisionsEnabled = false;
- }
- else
- {
- // wait for area to become clear before reenabling collisions
- if (!collisionsEnabled)
- {
- clearanceBuffer[0] = null;
- Physics.OverlapSphereNonAlloc(hand.objectAttachmentPoint.position, collisionReenableClearanceRadius, clearanceBuffer);
- // if we don't find anything in the vicinity, reenable collisions!
- if (clearanceBuffer[0] == null)
- {
- collisionsEnabled = true;
- }
- }
- }
- handCollider.SetCollisionDetectionEnabled(collisionsEnabled);
- if (hand.skeleton == null) return;
- initialized = true;
- // get the desired pose of the wrist in world space. Can't get the wrist bone transform, as this is affected by the resulting physics.
- wristToRoot = Matrix4x4.TRS(ProcessPos(wristBone, hand.skeleton.GetBone(wristBone).localPosition),
- ProcessRot(wristBone, hand.skeleton.GetBone(wristBone).localRotation),
- Vector3.one).inverse;
- rootToArmature = Matrix4x4.TRS(ProcessPos(rootBone, hand.skeleton.GetBone(rootBone).localPosition),
- ProcessRot(rootBone, hand.skeleton.GetBone(rootBone).localRotation),
- Vector3.one).inverse;
- wristToArmature = (wristToRoot * rootToArmature).inverse;
- // step up through virtual transform hierarchy and into world space
- targetPosition = transform.TransformPoint(wristToArmature.MultiplyPoint3x4(Vector3.zero));
- targetRotation = transform.rotation * wristToArmature.GetRotation();
- //bypass physics when game paused
- if (Time.timeScale == 0)
- {
- handCollider.TeleportTo(targetPosition, targetRotation);
- }
- }
- Transform wrist;
- const int thumbBone = SteamVR_Skeleton_JointIndexes.thumbDistal;
- const int indexBone = SteamVR_Skeleton_JointIndexes.indexDistal;
- const int middleBone = SteamVR_Skeleton_JointIndexes.middleDistal;
- const int ringBone = SteamVR_Skeleton_JointIndexes.ringDistal;
- const int pinkyBone = SteamVR_Skeleton_JointIndexes.pinkyDistal;
- void UpdateFingertips()
- {
- wrist = hand.skeleton.GetBone(SteamVR_Skeleton_JointIndexes.wrist);
- // set finger tip positions in wrist space
- for(int finger = 0; finger < 5; finger++)
- {
- int tip = SteamVR_Skeleton_JointIndexes.GetBoneForFingerTip(finger);
- int bone = tip;
- for(int i = 0; i < handCollider.fingerColliders[finger].Length; i++)
- {
- bone = tip - 1 - i; // start at distal and go down
- if (handCollider.fingerColliders[finger][i] != null)
- handCollider.fingerColliders[finger][i].localPosition = wrist.InverseTransformPoint(hand.skeleton.GetBone(bone).position);
- }
- }
- /*
- if(handCollider.tip_thumb != null)
- handCollider.tip_thumb.localPosition = wrist.InverseTransformPoint(hand.skeleton.GetBone(thumbBone).position);
- if(handCollider.tip_index != null)
- handCollider.tip_index.localPosition = wrist.InverseTransformPoint(hand.skeleton.GetBone(indexBone).position);
- if(handCollider.tip_middle != null)
- handCollider.tip_middle.localPosition = wrist.InverseTransformPoint(hand.skeleton.GetBone(middleBone).position);
- if(handCollider.tip_ring != null)
- handCollider.tip_ring.localPosition = wrist.InverseTransformPoint(hand.skeleton.GetBone(ringBone).position);
- if (handCollider.tip_pinky != null)
- handCollider.tip_pinky.localPosition = wrist.InverseTransformPoint(hand.skeleton.GetBone(pinkyBone).position);
- */
- }
- void UpdateHand(SteamVR_Behaviour_Pose pose, SteamVR_Input_Sources inputSource)
- {
- if (!initialized) return;
- UpdateCenterPoint();
- UpdatePositions();
- Quaternion offsetRotation = handCollider.transform.rotation * wristToArmature.inverse.GetRotation();
- hand.mainRenderModel.transform.rotation = offsetRotation;
- Vector3 offsetPosition = handCollider.transform.TransformPoint(wristToArmature.inverse.MultiplyPoint3x4(Vector3.zero));
- hand.mainRenderModel.transform.position = offsetPosition;
- /*
- Vector3 wristPointInArmatureSpace = transform.InverseTransformPoint(handCollider.transform.position);
- Vector3 handTargetPosition =
- hand.mainRenderModel.transform.position = handTargetPosition;
- //Quaternion handTargetRotation = transform.rotation * (wristToArmature.inverse.rotation * (Quaternion.Inverse(transform.rotation) * handCollider.transform.rotation));
- //hand.mainRenderModel.transform.rotation = handTargetRotation;
- */
- }
- Vector3 ProcessPos(int boneIndex, Vector3 pos)
- {
- if(hand.skeleton.mirroring != SteamVR_Behaviour_Skeleton.MirrorType.None)
- {
- return SteamVR_Behaviour_Skeleton.MirrorPosition(boneIndex, pos);
- }
- return pos;
- }
- Quaternion ProcessRot(int boneIndex, Quaternion rot)
- {
- if (hand.skeleton.mirroring != SteamVR_Behaviour_Skeleton.MirrorType.None)
- {
- return SteamVR_Behaviour_Skeleton.MirrorRotation(boneIndex, rot);
- }
- return rot;
- }
- }
- }
|