/****************************************************************************
* 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;
}
}
}