123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- /************************************************************************************
- Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
- Licensed under the Oculus Utilities SDK License Version 1.31 (the "License"); you may not use
- the Utilities SDK except in compliance with the License, which is provided at the time of installation
- or download, or which otherwise accompanies this software in either electronic or hard copy form.
- You may obtain a copy of the License at
- https://developer.oculus.com/licenses/utilities-1.31
- Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
- under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
- ANY KIND, either express or implied. See the License for the specific language governing
- permissions and limitations under the License.
- ************************************************************************************/
- using System.Collections.Generic;
- using UnityEngine;
- /// <summary>
- /// Allows grabbing and throwing of objects with the OVRGrabbable component on them.
- /// </summary>
- [RequireComponent(typeof(Rigidbody))]
- public class OVRGrabber : MonoBehaviour
- {
- // Grip trigger thresholds for picking up objects, with some hysteresis.
- public float grabBegin = 0.55f;
- public float grabEnd = 0.35f;
- bool alreadyUpdated = false;
- // Demonstrates parenting the held object to the hand's transform when grabbed.
- // When false, the grabbed object is moved every FixedUpdate using MovePosition.
- // Note that MovePosition is required for proper physics simulation. If you set this to true, you can
- // easily observe broken physics simulation by, for example, moving the bottom cube of a stacked
- // tower and noting a complete loss of friction.
- [SerializeField]
- protected bool m_parentHeldObject = false;
- // If true, will move the hand to the transform specified by m_parentTransform, using MovePosition in
- // FixedUpdate. This allows correct physics behavior, at the cost of some latency.
- // (If false, the hand can simply be attached to the hand anchor, which updates position in LateUpdate,
- // gaining us a few ms of reduced latency.)
- [SerializeField]
- protected bool m_moveHandPosition = false;
- // Child/attached transforms of the grabber, indicating where to snap held objects to (if you snap them).
- // Also used for ranking grab targets in case of multiple candidates.
- [SerializeField]
- protected Transform m_gripTransform = null;
- // Child/attached Colliders to detect candidate grabbable objects.
- [SerializeField]
- protected Collider[] m_grabVolumes = null;
- // Should be OVRInput.Controller.LTouch or OVRInput.Controller.RTouch.
- [SerializeField]
- protected OVRInput.Controller m_controller;
- [SerializeField]
- protected Transform m_parentTransform;
- [SerializeField]
- protected GameObject m_player;
- protected bool m_grabVolumeEnabled = true;
- protected Vector3 m_lastPos;
- protected Quaternion m_lastRot;
- protected Quaternion m_anchorOffsetRotation;
- protected Vector3 m_anchorOffsetPosition;
- protected float m_prevFlex;
- protected OVRGrabbable m_grabbedObj = null;
- protected Vector3 m_grabbedObjectPosOff;
- protected Quaternion m_grabbedObjectRotOff;
- protected Dictionary<OVRGrabbable, int> m_grabCandidates = new Dictionary<OVRGrabbable, int>();
- protected bool m_operatingWithoutOVRCameraRig = true;
- /// <summary>
- /// The currently grabbed object.
- /// </summary>
- public OVRGrabbable grabbedObject
- {
- get { return m_grabbedObj; }
- }
- public void ForceRelease(OVRGrabbable grabbable)
- {
- bool canRelease = (
- (m_grabbedObj != null) &&
- (m_grabbedObj == grabbable)
- );
- if (canRelease)
- {
- GrabEnd();
- }
- }
- protected virtual void Awake()
- {
- m_anchorOffsetPosition = transform.localPosition;
- m_anchorOffsetRotation = transform.localRotation;
- if(!m_moveHandPosition)
- {
- // If we are being used with an OVRCameraRig, let it drive input updates, which may come from Update or FixedUpdate.
- OVRCameraRig rig = transform.GetComponentInParent<OVRCameraRig>();
- if (rig != null)
- {
- rig.UpdatedAnchors += (r) => {OnUpdatedAnchors();};
- m_operatingWithoutOVRCameraRig = false;
- }
- }
- }
- protected virtual void Start()
- {
- m_lastPos = transform.position;
- m_lastRot = transform.rotation;
- if(m_parentTransform == null)
- {
- if(gameObject.transform.parent != null)
- {
- m_parentTransform = gameObject.transform.parent.transform;
- }
- else
- {
- m_parentTransform = new GameObject().transform;
- m_parentTransform.position = Vector3.zero;
- m_parentTransform.rotation = Quaternion.identity;
- }
- }
- }
- virtual public void Update()
- {
- alreadyUpdated = false;
- }
- virtual public void FixedUpdate()
- {
- if (m_operatingWithoutOVRCameraRig)
- {
- OnUpdatedAnchors();
- }
- }
- // Hands follow the touch anchors by calling MovePosition each frame to reach the anchor.
- // This is done instead of parenting to achieve workable physics. If you don't require physics on
- // your hands or held objects, you may wish to switch to parenting.
- void OnUpdatedAnchors()
- {
- // Don't want to MovePosition multiple times in a frame, as it causes high judder in conjunction
- // with the hand position prediction in the runtime.
- if (alreadyUpdated) return;
- alreadyUpdated = true;
- Vector3 destPos = m_parentTransform.TransformPoint(m_anchorOffsetPosition);
- Quaternion destRot = m_parentTransform.rotation * m_anchorOffsetRotation;
- if (m_moveHandPosition)
- {
- GetComponent<Rigidbody>().MovePosition(destPos);
- GetComponent<Rigidbody>().MoveRotation(destRot);
- }
- if (!m_parentHeldObject)
- {
- MoveGrabbedObject(destPos, destRot);
- }
- m_lastPos = transform.position;
- m_lastRot = transform.rotation;
- float prevFlex = m_prevFlex;
- // Update values from inputs
- m_prevFlex = OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, m_controller);
- CheckForGrabOrRelease(prevFlex);
- }
- void OnDestroy()
- {
- if (m_grabbedObj != null)
- {
- GrabEnd();
- }
- }
- void OnTriggerEnter(Collider otherCollider)
- {
- // Get the grab trigger
- OVRGrabbable grabbable = otherCollider.GetComponent<OVRGrabbable>() ?? otherCollider.GetComponentInParent<OVRGrabbable>();
- if (grabbable == null) return;
- // Add the grabbable
- int refCount = 0;
- m_grabCandidates.TryGetValue(grabbable, out refCount);
- m_grabCandidates[grabbable] = refCount + 1;
- }
- void OnTriggerExit(Collider otherCollider)
- {
- OVRGrabbable grabbable = otherCollider.GetComponent<OVRGrabbable>() ?? otherCollider.GetComponentInParent<OVRGrabbable>();
- if (grabbable == null) return;
- // Remove the grabbable
- int refCount = 0;
- bool found = m_grabCandidates.TryGetValue(grabbable, out refCount);
- if (!found)
- {
- return;
- }
- if (refCount > 1)
- {
- m_grabCandidates[grabbable] = refCount - 1;
- }
- else
- {
- m_grabCandidates.Remove(grabbable);
- }
- }
- protected void CheckForGrabOrRelease(float prevFlex)
- {
- if ((m_prevFlex >= grabBegin) && (prevFlex < grabBegin))
- {
- GrabBegin();
- }
- else if ((m_prevFlex <= grabEnd) && (prevFlex > grabEnd))
- {
- GrabEnd();
- }
- }
- protected virtual void GrabBegin()
- {
- float closestMagSq = float.MaxValue;
- OVRGrabbable closestGrabbable = null;
- Collider closestGrabbableCollider = null;
- // Iterate grab candidates and find the closest grabbable candidate
- foreach (OVRGrabbable grabbable in m_grabCandidates.Keys)
- {
- bool canGrab = !(grabbable.isGrabbed && !grabbable.allowOffhandGrab);
- if (!canGrab)
- {
- continue;
- }
- for (int j = 0; j < grabbable.grabPoints.Length; ++j)
- {
- Collider grabbableCollider = grabbable.grabPoints[j];
- // Store the closest grabbable
- Vector3 closestPointOnBounds = grabbableCollider.ClosestPointOnBounds(m_gripTransform.position);
- float grabbableMagSq = (m_gripTransform.position - closestPointOnBounds).sqrMagnitude;
- if (grabbableMagSq < closestMagSq)
- {
- closestMagSq = grabbableMagSq;
- closestGrabbable = grabbable;
- closestGrabbableCollider = grabbableCollider;
- }
- }
- }
- // Disable grab volumes to prevent overlaps
- GrabVolumeEnable(false);
- if (closestGrabbable != null)
- {
- if (closestGrabbable.isGrabbed)
- {
- closestGrabbable.grabbedBy.OffhandGrabbed(closestGrabbable);
- }
- m_grabbedObj = closestGrabbable;
- m_grabbedObj.GrabBegin(this, closestGrabbableCollider);
- m_lastPos = transform.position;
- m_lastRot = transform.rotation;
- // Set up offsets for grabbed object desired position relative to hand.
- if(m_grabbedObj.snapPosition)
- {
- m_grabbedObjectPosOff = m_gripTransform.localPosition;
- if(m_grabbedObj.snapOffset)
- {
- Vector3 snapOffset = m_grabbedObj.snapOffset.position;
- if (m_controller == OVRInput.Controller.LTouch) snapOffset.x = -snapOffset.x;
- m_grabbedObjectPosOff += snapOffset;
- }
- }
- else
- {
- Vector3 relPos = m_grabbedObj.transform.position - transform.position;
- relPos = Quaternion.Inverse(transform.rotation) * relPos;
- m_grabbedObjectPosOff = relPos;
- }
- if (m_grabbedObj.snapOrientation)
- {
- m_grabbedObjectRotOff = m_gripTransform.localRotation;
- if(m_grabbedObj.snapOffset)
- {
- m_grabbedObjectRotOff = m_grabbedObj.snapOffset.rotation * m_grabbedObjectRotOff;
- }
- }
- else
- {
- Quaternion relOri = Quaternion.Inverse(transform.rotation) * m_grabbedObj.transform.rotation;
- m_grabbedObjectRotOff = relOri;
- }
- // Note: force teleport on grab, to avoid high-speed travel to dest which hits a lot of other objects at high
- // speed and sends them flying. The grabbed object may still teleport inside of other objects, but fixing that
- // is beyond the scope of this demo.
- MoveGrabbedObject(m_lastPos, m_lastRot, true);
- SetPlayerIgnoreCollision(m_grabbedObj.gameObject, true);
- if (m_parentHeldObject)
- {
- m_grabbedObj.transform.parent = transform;
- }
- }
- }
- protected virtual void MoveGrabbedObject(Vector3 pos, Quaternion rot, bool forceTeleport = false)
- {
- if (m_grabbedObj == null)
- {
- return;
- }
- Rigidbody grabbedRigidbody = m_grabbedObj.grabbedRigidbody;
- Vector3 grabbablePosition = pos + rot * m_grabbedObjectPosOff;
- Quaternion grabbableRotation = rot * m_grabbedObjectRotOff;
- if (forceTeleport)
- {
- grabbedRigidbody.transform.position = grabbablePosition;
- grabbedRigidbody.transform.rotation = grabbableRotation;
- }
- else
- {
- grabbedRigidbody.MovePosition(grabbablePosition);
- grabbedRigidbody.MoveRotation(grabbableRotation);
- }
- }
- protected void GrabEnd()
- {
- if (m_grabbedObj != null)
- {
- OVRPose localPose = new OVRPose { position = OVRInput.GetLocalControllerPosition(m_controller), orientation = OVRInput.GetLocalControllerRotation(m_controller) };
- OVRPose offsetPose = new OVRPose { position = m_anchorOffsetPosition, orientation = m_anchorOffsetRotation };
- localPose = localPose * offsetPose;
- OVRPose trackingSpace = transform.ToOVRPose() * localPose.Inverse();
- Vector3 linearVelocity = trackingSpace.orientation * OVRInput.GetLocalControllerVelocity(m_controller);
- Vector3 angularVelocity = trackingSpace.orientation * OVRInput.GetLocalControllerAngularVelocity(m_controller);
- GrabbableRelease(linearVelocity, angularVelocity);
- }
- // Re-enable grab volumes to allow overlap events
- GrabVolumeEnable(true);
- }
- protected void GrabbableRelease(Vector3 linearVelocity, Vector3 angularVelocity)
- {
- m_grabbedObj.GrabEnd(linearVelocity, angularVelocity);
- if(m_parentHeldObject) m_grabbedObj.transform.parent = null;
- SetPlayerIgnoreCollision(m_grabbedObj.gameObject, false);
- m_grabbedObj = null;
- }
- protected virtual void GrabVolumeEnable(bool enabled)
- {
- if (m_grabVolumeEnabled == enabled)
- {
- return;
- }
- m_grabVolumeEnabled = enabled;
- for (int i = 0; i < m_grabVolumes.Length; ++i)
- {
- Collider grabVolume = m_grabVolumes[i];
- grabVolume.enabled = m_grabVolumeEnabled;
- }
- if (!m_grabVolumeEnabled)
- {
- m_grabCandidates.Clear();
- }
- }
- protected virtual void OffhandGrabbed(OVRGrabbable grabbable)
- {
- if (m_grabbedObj == grabbable)
- {
- GrabbableRelease(Vector3.zero, Vector3.zero);
- }
- }
- protected void SetPlayerIgnoreCollision(GameObject grabbable, bool ignore)
- {
- if (m_player != null)
- {
- Collider playerCollider = m_player.GetComponent<Collider>();
- if (playerCollider != null)
- {
- Collider[] colliders = grabbable.GetComponents<Collider>();
- foreach (Collider c in colliders)
- {
- Physics.IgnoreCollision(c, playerCollider, ignore);
- }
- }
- }
- }
- }
|