Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate LocateTargetHitPoint methods #11699

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.XR.Interaction.Toolkit;
using static Microsoft.MixedReality.Toolkit.Input.XRRayInteractorExtensions;

namespace Microsoft.MixedReality.Toolkit.Input.Experimental
{
Expand Down Expand Up @@ -53,9 +52,7 @@ protected virtual void OnDisable()
Application.onBeforeRender -= OnBeforeRenderCursor;
}

private Vector3 targetLocalHitPoint;
private Vector3 targetLocalHitNormal;
private Transform hitTargetTransform;
private TargetHitDetails selectedHitDetails = new TargetHitDetails();

// reusable lists of the points returned by the XRRayInteractor
Vector3[] rayPositions;
Expand All @@ -68,17 +65,6 @@ protected virtual void OnDisable()

private void LocateTargetHitPoint(SelectEnterEventArgs args)
{
// If no hit, abort.
if (!mouseInteractor.TryGetCurrentRaycast(
out RaycastHit? raycastHit,
out _,
out UnityEngine.EventSystems.RaycastResult? raycastResult,
out _,
out bool isUIHitClosest))
{
return;
}

// Sanity check.
if (rayPositions == null ||
rayPositions.Length == 0 ||
Expand All @@ -88,29 +74,7 @@ private void LocateTargetHitPoint(SelectEnterEventArgs args)
return;
}

// Record relevant data about the hit point.
if (raycastResult.HasValue && isUIHitClosest)
{
hitTargetTransform = raycastResult.Value.gameObject.transform;
targetLocalHitPoint = hitTargetTransform.InverseTransformPoint(raycastResult.Value.worldPosition);
targetLocalHitNormal = hitTargetTransform.InverseTransformDirection(raycastResult.Value.worldNormal);
}
else if (raycastHit.HasValue)
{
// In the case of affordances/handles, we can stick the ray right on to the handle.
if (args.interactableObject is ISnapInteractable snappable)
{
hitTargetTransform = snappable.HandleTransform;
targetLocalHitPoint = Vector3.zero;
targetLocalHitNormal = Vector3.up;
}
else
{
hitTargetTransform = raycastHit.Value.collider.transform;
targetLocalHitPoint = hitTargetTransform.InverseTransformPoint(raycastHit.Value.point);
targetLocalHitNormal = hitTargetTransform.InverseTransformPoint(raycastHit.Value.normal);
}
}
mouseInteractor.TryLocateTargetHitPoint(args.interactableObject, out selectedHitDetails);
}

private void OnBeforeRenderCursor()
Expand Down Expand Up @@ -142,7 +106,7 @@ private void OnBeforeRenderCursor()
// If the mouse is selecting an interactable, then position the cursor based on the target transform
if (mouseInteractor.interactablesSelected.Count > 0)
{
reticlePosition = hitTargetTransform.TransformPoint(targetLocalHitPoint);
reticlePosition = selectedHitDetails.HitTargetTransform.TransformPoint(selectedHitDetails.TargetLocalHitPoint);
}
// otherwise, try getting reticlePosition from the ray hit or set it a default distance from the user
else if (!mouseInteractor.TryGetHitInfo(out reticlePosition, out reticleNormal, out endPositionInLine, out bool isValidTarget))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.XR.Interaction.Toolkit;
using static Microsoft.MixedReality.Toolkit.Input.XRRayInteractorExtensions;

namespace Microsoft.MixedReality.Toolkit.Input
{
Expand Down Expand Up @@ -181,8 +182,7 @@ public bool StopLineAtFirstRaycastHit

// reusable values derived from raycast hit data
private Vector3 reticlePosition;
private Transform hitTargetTransform;
private Vector3 targetLocalHitPoint;
private TargetHitDetails selectedHitDetails = new TargetHitDetails();
private float hitDistance;

/// <summary>
Expand Down Expand Up @@ -324,7 +324,7 @@ private void UpdateLineVisual()
if (rayInteractor.hasSelection)
{
// Assign the last point to the one saved by the callback
lineDataProvider.LastPoint = hitTargetTransform.TransformPoint(targetLocalHitPoint);
lineDataProvider.LastPoint = selectedHitDetails.HitTargetTransform.TransformPoint(selectedHitDetails.TargetLocalHitPoint);
rayHasHit = true;
}
// Otherwise draw out the line exactly as the Ray Interactor prescribes
Expand Down Expand Up @@ -447,67 +447,14 @@ private void InitializeLineRendererProperties()
/// </summary>
private void LocateTargetHitPoint(SelectEnterEventArgs args)
{
// If no hit interactable, abort
if (args == null)
// If no hit interactable or we haven't even gotten any ray positions yet, abort
if (args == null || rayPositions == null || rayPositionsCount <= 0)
{
return;
}

bool hitPointAndTransformUpdated = false;

// In the case of affordances/handles, we can stick the ray right on to the handle.
if (args.interactableObject is ISnapInteractable snappable)
{
hitTargetTransform = snappable.HandleTransform;
targetLocalHitPoint = Vector3.zero;
hitPointAndTransformUpdated = true;
}

// In the case of an IScrollable being selected, ensure that the line visual locks to
// the scroller and not to the a list item within the scroller, such as a button.
if (args.interactableObject is IScrollable scrollable &&
scrollable.IsScrolling &&
scrollable.ScrollingInteractor == (IXRInteractor)rayInteractor)
{
hitTargetTransform = scrollable.ScrollableTransform;
targetLocalHitPoint = scrollable.ScrollingLocalAnchorPosition;
hitPointAndTransformUpdated = true;
}

// If no hit, abort.
if (!rayInteractor.TryGetCurrentRaycast(
out RaycastHit? raycastHit,
out _,
out UnityEngine.EventSystems.RaycastResult? raycastResult,
out _,
out bool isUIHitClosest))
{
return;
}

// If we haven't even gotten any ray positions yet, abort.
if (rayPositions == null || rayPositionsCount <= 0)
{
return;
}

// Record relevant data about the hit point.
if (raycastResult.HasValue && isUIHitClosest)
{
hitTargetTransform = raycastResult.Value.gameObject.transform;
targetLocalHitPoint = hitTargetTransform.InverseTransformPoint(raycastResult.Value.worldPosition);
hitDistance = (raycastResult.Value.worldPosition - rayPositions[0]).magnitude;
}
else if (raycastHit.HasValue)
{
if (!hitPointAndTransformUpdated)
{
hitTargetTransform = raycastHit.Value.collider.transform;
targetLocalHitPoint = hitTargetTransform.InverseTransformPoint(raycastHit.Value.point);
}

hitDistance = (hitTargetTransform.TransformPoint(targetLocalHitPoint) - rayPositions[0]).magnitude;
}
rayInteractor.TryLocateTargetHitPoint(args.interactableObject, out selectedHitDetails);
hitDistance = (selectedHitDetails.HitDistanceReferencePoint - rayPositions[0]).magnitude;
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Unity.Profiling;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using static Microsoft.MixedReality.Toolkit.Input.XRRayInteractorExtensions;

namespace Microsoft.MixedReality.Toolkit.Input
{
Expand Down Expand Up @@ -83,8 +84,8 @@ private void UpdateReticle()
{
if (rayInteractor.interactablesSelected.Count > 0)
{
reticlePosition = hitTargetTransform.TransformPoint(targetLocalHitPoint);
reticleNormal = hitTargetTransform.TransformDirection(targetLocalHitNormal);
reticlePosition = selectedHitDetails.HitTargetTransform.TransformPoint(selectedHitDetails.TargetLocalHitPoint);
reticleNormal = selectedHitDetails.HitTargetTransform.TransformDirection(selectedHitDetails.TargetLocalHitNormal);
ReticleSetActive(true);
}
else
Expand Down Expand Up @@ -131,71 +132,14 @@ private void ReticleSetActive(bool value)
}
}

private Vector3 targetLocalHitPoint;
private Vector3 targetLocalHitNormal;
private Transform hitTargetTransform;
private TargetHitDetails selectedHitDetails = new TargetHitDetails();

/// <summary>
/// Used to locate and lock the raycast hit data on a select
/// </summary>
private void LocateTargetHitPoint(SelectEnterEventArgs args)
{
bool hitPointAndTransformUpdated = false;
bool hitNormalUpdated = false;

// In the case of affordances/handles, we can stick the ray right on to the handle.
if (args.interactableObject is ISnapInteractable snappable)
{
hitTargetTransform = snappable.HandleTransform;
targetLocalHitPoint = Vector3.zero;
targetLocalHitNormal = Vector3.up;
hitPointAndTransformUpdated = true;
hitNormalUpdated = true;
}

// In the case of an IScrollable being selected, ensure that the reticle locks to the
// scroller and not to the a list item within the scroller, such as a button.
if (args.interactableObject is IScrollable scrollable &&
scrollable.IsScrolling &&
scrollable.ScrollingInteractor == (IXRInteractor)rayInteractor)
{
hitTargetTransform = scrollable.ScrollableTransform;
targetLocalHitPoint = scrollable.ScrollingLocalAnchorPosition;
hitPointAndTransformUpdated = true;
}

// If no hit, abort.
if (!rayInteractor.TryGetCurrentRaycast(
out RaycastHit? raycastHit,
out _,
out UnityEngine.EventSystems.RaycastResult? raycastResult,
out _,
out bool isUIHitClosest))
{
return;
}

// Align the reticle with a UI hit if applicable
if (raycastResult.HasValue && isUIHitClosest)
{
hitTargetTransform = raycastResult.Value.gameObject.transform;
targetLocalHitPoint = hitTargetTransform.InverseTransformPoint(raycastResult.Value.worldPosition);
targetLocalHitNormal = hitTargetTransform.InverseTransformDirection(raycastResult.Value.worldNormal);
}
// Otherwise, calculate the reticle pose based on the raycast hit.
else if (raycastHit.HasValue)
{
if (!hitPointAndTransformUpdated)
{
hitTargetTransform = raycastHit.Value.collider.transform;
targetLocalHitPoint = hitTargetTransform.InverseTransformPoint(raycastHit.Value.point);
}

if (!hitNormalUpdated)
{
targetLocalHitNormal = hitTargetTransform.InverseTransformDirection(raycastHit.Value.normal);
}
}
rayInteractor.TryLocateTargetHitPoint(args.interactableObject, out selectedHitDetails);
}

/// <summary>
Expand Down
112 changes: 112 additions & 0 deletions com.microsoft.mrtk.input/Interactors/XRRayInteractorExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Extensions to the XRRayInteractor and associated structs.
/// </summary>
public static class XRRayInteractorExtensions
{
/// <summary>
/// Used to locate and lock the raycast hit data on a select.
/// </summary>
/// <param name="rayInteractor">The XRRayInteractor responsible for the raycast hit.</param>
/// <param name="interactableObject"> The IXRSelectInteractable which has been selected. </param>
/// <param name="hitDetails"> The local position and normal of the hit target, the hit target transform, and a reference point to calculate hit distance, contained in a TargetHitDetails struct. </param>
/// <returns> Returns true if there was a raycast hit and false otherwise. </returns>
public static bool TryLocateTargetHitPoint(this XRRayInteractor rayInteractor, IXRSelectInteractable interactableObject, out TargetHitDetails hitDetails)
{
hitDetails = new TargetHitDetails();
bool hitPointAndTransformUpdated = false;
bool hitNormalUpdated = false;

// In the case of affordances/handles, we can stick the ray right on to the handle.
if (interactableObject is ISnapInteractable snappable)
{
hitDetails.HitTargetTransform = snappable.HandleTransform;
hitDetails.TargetLocalHitPoint = Vector3.zero;
hitDetails.TargetLocalHitNormal = Vector3.up;
hitPointAndTransformUpdated = true;
hitNormalUpdated = true;
}

// In the case of an IScrollable being selected, ensure that the reticle locks to the
// scroller and not to the a list item within the scroller, such as a button.
if (interactableObject is IScrollable scrollable &&
scrollable.IsScrolling &&
scrollable.ScrollingInteractor == (IXRInteractor)rayInteractor)
{
hitDetails.HitTargetTransform = scrollable.ScrollableTransform;
hitDetails.TargetLocalHitPoint = scrollable.ScrollingLocalAnchorPosition;
hitPointAndTransformUpdated = true;
}

// If no hit, abort.
if (!rayInteractor.TryGetCurrentRaycast(
out RaycastHit? raycastHit,
out _,
out UnityEngine.EventSystems.RaycastResult? raycastResult,
out _,
out bool isUIHitClosest))
{
return false;
}

// Align the reticle with a UI hit if applicable
if (raycastResult.HasValue && isUIHitClosest)
{
hitDetails.HitTargetTransform = raycastResult.Value.gameObject.transform;
hitDetails.TargetLocalHitPoint = hitDetails.HitTargetTransform.InverseTransformPoint(raycastResult.Value.worldPosition);
hitDetails.TargetLocalHitNormal = hitDetails.HitTargetTransform.InverseTransformDirection(raycastResult.Value.worldNormal);
hitDetails.HitDistanceReferencePoint = raycastResult.Value.worldPosition;
}
// Otherwise, calculate the reticle pose based on the raycast hit.
else if (raycastHit.HasValue)
{
if (!hitPointAndTransformUpdated)
{
hitDetails.HitTargetTransform = raycastHit.Value.collider.transform;
hitDetails.TargetLocalHitPoint = hitDetails.HitTargetTransform.InverseTransformPoint(raycastHit.Value.point);
}

if (!hitNormalUpdated)
{
hitDetails.TargetLocalHitNormal = hitDetails.HitTargetTransform.InverseTransformDirection(raycastHit.Value.normal);
}

hitDetails.HitDistanceReferencePoint = hitDetails.HitTargetTransform.TransformPoint(hitDetails.TargetLocalHitPoint);
}
return true;
}

/// <summary>
/// A data container for managing the position, normal, and transform of a target hit point.
/// </summary>
public struct TargetHitDetails
{
/// <summary>
/// The position of the target hit.
/// </summary>
public Vector3 TargetLocalHitPoint;

/// <summary>
/// The normal of the target hit.
/// </summary>
public Vector3 TargetLocalHitNormal;

/// <summary>
/// The Transform of the selected target.
/// </summary>
public Transform HitTargetTransform;

/// <summary>
/// The position used to calculate hit distance from a given ray position.
/// </summary>
public Vector3 HitDistanceReferencePoint;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading