185 lines
7.4 KiB
C#
185 lines
7.4 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using Microsoft.MixedReality.Toolkit.Utilities;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace Microsoft.MixedReality.Toolkit.Input
|
|
{
|
|
/// <summary>
|
|
/// Utility behavior to access the axis aligned bounds of IMixedRealityHands (or the proxy visualizer of IMixedRealityControllers).
|
|
/// </summary>
|
|
[AddComponentMenu("Scripts/MRTK/Core/HandBounds")]
|
|
public class HandBounds : MonoBehaviour, IMixedRealitySourceStateHandler, IMixedRealityHandJointHandler
|
|
{
|
|
/// <summary>
|
|
/// Accessor for the bounds associated with a handedness, calculated in global-axis-aligned space.
|
|
/// </summary>
|
|
public Dictionary<Handedness, Bounds> Bounds { get; private set; } = new Dictionary<Handedness, Bounds>();
|
|
|
|
/// <summary>
|
|
/// Accessor for the bounds associated with a handedness, calculated in local hand-space, locally axis aligned.
|
|
/// </summary>
|
|
public Dictionary<Handedness, Bounds> LocalBounds { get; private set; } = new Dictionary<Handedness, Bounds>();
|
|
|
|
[SerializeField]
|
|
[Tooltip("Should a gizmo be drawn to represent the hand bounds.")]
|
|
private bool drawBoundsGizmo = false;
|
|
|
|
/// <summary>
|
|
/// Should a gizmo be drawn to represent the hand bounds.
|
|
/// </summary>
|
|
public bool DrawBoundsGizmo
|
|
{
|
|
get => drawBoundsGizmo;
|
|
set => drawBoundsGizmo = value;
|
|
}
|
|
|
|
[SerializeField]
|
|
[Tooltip("Should a gizmo be drawn to represent the locally-calculated hand bounds.")]
|
|
private bool drawLocalBoundsGizmo = false;
|
|
|
|
/// <summary>
|
|
/// Should a gizmo be drawn to represent the locally-calculated hand bounds.
|
|
/// </summary>
|
|
public bool DrawLocalBoundsGizmo
|
|
{
|
|
get => drawLocalBoundsGizmo;
|
|
set => drawLocalBoundsGizmo = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mapping between controller handedness and associated hand transforms.
|
|
/// Used to transform the debug gizmos when rendering the hand AABBs.
|
|
/// </summary>
|
|
private Dictionary<Handedness, Matrix4x4> BoundsTransforms = new Dictionary<Handedness, Matrix4x4>();
|
|
|
|
#region MonoBehaviour Implementation
|
|
|
|
private void OnEnable()
|
|
{
|
|
CoreServices.InputSystem?.RegisterHandler<IMixedRealitySourceStateHandler>(this);
|
|
CoreServices.InputSystem?.RegisterHandler<IMixedRealityHandJointHandler>(this);
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
CoreServices.InputSystem?.UnregisterHandler<IMixedRealitySourceStateHandler>(this);
|
|
CoreServices.InputSystem?.UnregisterHandler<IMixedRealityHandJointHandler>(this);
|
|
}
|
|
|
|
private void OnDrawGizmos()
|
|
{
|
|
if (drawBoundsGizmo)
|
|
{
|
|
Gizmos.color = Color.yellow;
|
|
foreach (var kvp in Bounds)
|
|
{
|
|
Gizmos.DrawWireCube(kvp.Value.center, kvp.Value.size);
|
|
}
|
|
}
|
|
if (drawLocalBoundsGizmo)
|
|
{
|
|
Gizmos.color = Color.cyan;
|
|
foreach (var kvp in LocalBounds)
|
|
{
|
|
Gizmos.matrix = BoundsTransforms[kvp.Key];
|
|
Gizmos.DrawWireCube(kvp.Value.center, kvp.Value.size);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion MonoBehaviour Implementation
|
|
|
|
#region IMixedRealitySourceStateHandler Implementation
|
|
|
|
/// <inheritdoc />
|
|
public void OnSourceDetected(SourceStateEventData eventData)
|
|
{
|
|
IMixedRealityController hand = eventData.Controller;
|
|
|
|
// If a hand does not contain joints, OnHandJointsUpdated will not be called the bounds should
|
|
// be calculated based on the proxy visuals.
|
|
if (hand != null && !(hand is IMixedRealityHand))
|
|
{
|
|
var proxy = hand.Visualizer?.GameObjectProxy;
|
|
|
|
if (proxy != null)
|
|
{
|
|
// Bounds calculated in proxy-space will have an origin of zero, but bounds
|
|
// calculated in global space will have an origin centered on the proxy transform.
|
|
var newGlobalBounds = new Bounds(proxy.transform.position, Vector3.zero);
|
|
var newLocalBounds = new Bounds(Vector3.zero, Vector3.zero);
|
|
var boundsPoints = new List<Vector3>();
|
|
BoundsExtensions.GetRenderBoundsPoints(proxy, boundsPoints, 0);
|
|
|
|
foreach (var point in boundsPoints)
|
|
{
|
|
newGlobalBounds.Encapsulate(point);
|
|
// Local hand-space bounds are encapsulated using proxy-space point coordinates
|
|
newLocalBounds.Encapsulate(proxy.transform.InverseTransformPoint(point));
|
|
}
|
|
|
|
Bounds[hand.ControllerHandedness] = newGlobalBounds;
|
|
LocalBounds[hand.ControllerHandedness] = newLocalBounds;
|
|
BoundsTransforms[hand.ControllerHandedness] = proxy.transform.localToWorldMatrix;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void OnSourceLost(SourceStateEventData eventData)
|
|
{
|
|
var hand = eventData.Controller;
|
|
|
|
if (hand != null)
|
|
{
|
|
Bounds.Remove(hand.ControllerHandedness);
|
|
LocalBounds.Remove(hand.ControllerHandedness);
|
|
BoundsTransforms.Remove(hand.ControllerHandedness);
|
|
}
|
|
}
|
|
|
|
#endregion IMixedRealitySourceStateHandler Implementation
|
|
|
|
#region IMixedRealityHandJointHandler Implementation
|
|
|
|
/// <inheritdoc />
|
|
public void OnHandJointsUpdated(InputEventData<IDictionary<TrackedHandJoint, MixedRealityPose>> eventData)
|
|
{
|
|
if (eventData.InputData.TryGetValue(TrackedHandJoint.Palm, out MixedRealityPose palmPose))
|
|
{
|
|
var newGlobalBounds = new Bounds(palmPose.Position, Vector3.zero);
|
|
var newLocalBounds = new Bounds(Vector3.zero, Vector3.zero);
|
|
|
|
// This starts at 1 to skip over TrackedHandJoint.None.
|
|
for (int i = 1; i < ArticulatedHandPose.JointCount; i++)
|
|
{
|
|
TrackedHandJoint handJoint = (TrackedHandJoint)i;
|
|
|
|
if (handJoint == TrackedHandJoint.Palm)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (eventData.InputData.TryGetValue(handJoint, out MixedRealityPose pose))
|
|
{
|
|
newGlobalBounds.Encapsulate(pose.Position);
|
|
newLocalBounds.Encapsulate(Quaternion.Inverse(palmPose.Rotation) * (pose.Position - palmPose.Position));
|
|
}
|
|
}
|
|
|
|
Bounds[eventData.Handedness] = newGlobalBounds;
|
|
LocalBounds[eventData.Handedness] = newLocalBounds;
|
|
|
|
// We must normalize the quaternion before constructing the TRS matrix; non-unit-length quaternions
|
|
// may be emitted from the palm-pose and they must be renormalized.
|
|
BoundsTransforms[eventData.Handedness] = Matrix4x4.TRS(palmPose.Position, palmPose.Rotation.normalized, Vector3.one);
|
|
}
|
|
}
|
|
|
|
#endregion IMixedRealityHandJointHandler Implementation
|
|
}
|
|
}
|