/**************************************************************************** * Copyright 2019 Nreal Techonology Limited. All rights reserved. * * This file is part of NRSDK. * * https://www.nreal.ai/ * *****************************************************************************/ namespace NRKernal { using System; using System.Collections.Generic; using UnityEngine; /// A nr grabber. [RequireComponent(typeof(Rigidbody))] public class NRGrabber : MonoBehaviour { /// The grab button. public ControllerButton grabButton = ControllerButton.GRIP; /// The hand enum. public ControllerHandEnum handEnum; /// True to enable, false to disable the grab multi. public bool grabMultiEnabled = false; /// True to update pose by rigidbody. public bool updatePoseByRigidbody = true; /// True to previous grab press. private bool m_PreviousGrabPress; /// Dictionary of grab readies. private Dictionary m_GrabReadyDict = new Dictionary(); /// Dictionary of grab start offset. private Dictionary m_GrabStartOffsetDict = new Dictionary(); /// List of grabbings. private List m_GrabbingList = new List(); /// The children colliders. private Collider[] m_ChildrenColliders; /// Func to judge if is grabbing. private Func m_FunToJudgeIsGrabbing; public bool IsGrabbingObjects{ get { return m_GrabbingList.Count > 0; } } /// Awakes this object. private void Awake() { Rigidbody rigid = GetComponent(); rigid.useGravity = false; rigid.isKinematic = true; m_ChildrenColliders = GetComponentsInChildren(); } /// Executes the 'enable' action. private void OnEnable() { NRInput.OnControllerStatesUpdated += OnControllerPoseUpdated; } /// Executes the 'disable' action. private void OnDisable() { NRInput.OnControllerStatesUpdated -= OnControllerPoseUpdated; ReleaseAllGrabbedObject(); } /// Fixed update. private void FixedUpdate() { if (!updatePoseByRigidbody) return; UpdateGrabbles(); } /// Executes the 'trigger enter' action. /// The other. private void OnTriggerEnter(Collider other) { NRGrabbableObject grabble = other.GetComponent() ?? other.GetComponentInParent(); if (grabble == null) return; if (m_GrabReadyDict.ContainsKey(grabble)) { m_GrabReadyDict[grabble] += 1; } else { m_GrabReadyDict.Add(grabble, 1); } } /// Executes the 'trigger exit' action. /// The other. private void OnTriggerExit(Collider other) { NRGrabbableObject grabble = other.GetComponent() ?? other.GetComponentInParent(); if (grabble == null) return; int count = 0; if (m_GrabReadyDict.TryGetValue(grabble, out count)) { if (count > 1) { m_GrabReadyDict[grabble] = count - 1; } else { m_GrabReadyDict.Remove(grabble); } } } /// Executes the 'controller pose updated' action. private void OnControllerPoseUpdated() { if (updatePoseByRigidbody) return; UpdateGrabbles(); } /// Updates the grabbles. private void UpdateGrabbles() { bool pressGrab = false; if (m_FunToJudgeIsGrabbing != null) { pressGrab = m_FunToJudgeIsGrabbing.Invoke(); } else { pressGrab = NRInput.GetButton(handEnum, grabButton); } bool triggeredGrab = !m_PreviousGrabPress && pressGrab; bool releaseAction = m_PreviousGrabPress && !pressGrab; m_PreviousGrabPress = pressGrab; if (triggeredGrab && m_GrabbingList.Count == 0 && m_GrabReadyDict.Keys.Count != 0) { if (!grabMultiEnabled) { NRGrabbableObject nearestGrabble = GetNearestGrabbleObject(); if (nearestGrabble) { GrabTarget(nearestGrabble); } } else { var grabbleQueue = new Queue(m_GrabReadyDict.Keys); while(grabbleQueue.Count > 0) { GrabTarget(grabbleQueue.Dequeue()); } m_GrabReadyDict.Clear(); } SetChildrenCollidersEnabled(false); } if (releaseAction) { ReleaseAllGrabbedObject(); return; } if (m_GrabbingList.Count > 0 && !triggeredGrab) { MoveGrabbingObjects(); } } /// Gets nearest grabble object. /// The nearest grabble object. private NRGrabbableObject GetNearestGrabbleObject() { NRGrabbableObject nearestGrabble = null; float nearestSqrMagnitude = float.MaxValue; foreach (NRGrabbableObject grabbleObj in m_GrabReadyDict.Keys) { if (grabbleObj.AttachedColliders == null) continue; for (int i = 0; i < grabbleObj.AttachedColliders.Length; i++) { Vector3 closestPoint = grabbleObj.AttachedColliders[i].ClosestPointOnBounds(transform.position); float grabbableSqrMagnitude = (transform.position - closestPoint).sqrMagnitude; if (grabbableSqrMagnitude < nearestSqrMagnitude) { nearestSqrMagnitude = grabbableSqrMagnitude; nearestGrabble = grabbleObj; } } } return nearestGrabble; } /// Grab target. /// Target for the. private void GrabTarget(NRGrabbableObject target) { if (target == null || !target.CanGrab) return; if (!m_GrabbingList.Contains(target)) { m_GrabbingList.Add(target); } if (!m_GrabStartOffsetDict.ContainsKey(target)) { var offsetPosition = transform.InverseTransformPoint(target.transform.position); var offsetRotation = Quaternion.Inverse(transform.rotation) * target.transform.rotation; m_GrabStartOffsetDict.Add(target, new Pose(offsetPosition, offsetRotation)); } m_GrabReadyDict.Remove(target); target.GrabBegin(this); } /// Move grabbing objects. private void MoveGrabbingObjects() { for (int i = 0; i < m_GrabbingList.Count; i++) { Vector3 targetPos; Quaternion targetRot; Pose offsetPose; if(m_GrabStartOffsetDict.TryGetValue(m_GrabbingList[i], out offsetPose)) { targetPos = transform.TransformPoint(offsetPose.position); targetRot = transform.rotation * offsetPose.rotation; } else { targetPos = transform.position; targetRot = transform.rotation; } if (updatePoseByRigidbody) { m_GrabbingList[i].MoveRigidbody(targetPos, targetRot); } else { m_GrabbingList[i].MoveTransform(targetPos, targetRot); } } } /// Sets children colliders enabled. /// True if is enabled, false if not. private void SetChildrenCollidersEnabled(bool isEnabled) { if (m_ChildrenColliders == null) return; for (int i = 0; i < m_ChildrenColliders.Length; i++) { m_ChildrenColliders[i].enabled = isEnabled; } } /// Release all the grabbed objects. public void ReleaseAllGrabbedObject() { for (int i = 0; i < m_GrabbingList.Count; i++) { m_GrabbingList[i].GrabEnd(); } m_GrabbingList.Clear(); m_GrabStartOffsetDict.Clear(); SetChildrenCollidersEnabled(true); } /// Set the condition to judge if is grabbing. /// public void SetGrabJudgeCondition(Func judgeGrabbingFunc) { m_FunToJudgeIsGrabbing = judgeGrabbingFunc; } } }