Skip to content

Commit

Permalink
HPUIInteractor refactors to not inherit from poke
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmed-shariff committed Mar 5, 2024
1 parent 393427f commit d6ac0bd
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 24 deletions.
3 changes: 3 additions & 0 deletions Runtime/Interaction/HPUIBaseInteractable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ protected override void OnEnable()
}
#endregion


#region surface point calculations
/// <summary>
/// Compute and store the surface bounds to compute point on surface
/// </summary>
Expand Down Expand Up @@ -113,6 +115,7 @@ protected Vector2 ComputeTargetPointOnInteractablePlane(Vector3 targetPoint, Tra
pointOnXZPlane = worldToLocalMatrix.MultiplyPoint3x4(pointOnXZPlane);
return new Vector2(pointOnXZPlane.x, pointOnXZPlane.z);
}
#endregion

#region IHPUIInteractable interface
/// <inheritdoc />
Expand Down
114 changes: 90 additions & 24 deletions Runtime/Interaction/HPUIInteractor.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.XR.Hands;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Utilities;

namespace ubco.ovilab.HPUI.Interaction
{
Expand All @@ -13,10 +11,8 @@ namespace ubco.ovilab.HPUI.Interaction
/// </summary>
[SelectionBase]
[DisallowMultipleComponent]
public class HPUIInteractor: XRPokeInteractor, IHPUIInteractor
public class HPUIInteractor: XRBaseInteractor, IHPUIInteractor
{
public new Handedness handedness;

// TODO move these to an asset?
[SerializeField]
private float tapTimeThreshold;
Expand All @@ -27,29 +23,42 @@ public class HPUIInteractor: XRPokeInteractor, IHPUIInteractor
public float TapDistanceThreshold { get => tapDistanceThreshold; set => tapDistanceThreshold = value; }

[SerializeField]
[Tooltip("Event triggered on tap")]
private HPUITapEvent tapEvent = new HPUITapEvent();

/// <summary>
/// Event triggered on tap
/// </summary>
/// <inheritdoc />
public HPUITapEvent TapEvent { get => tapEvent; set => tapEvent = value; }

[SerializeField]
[Tooltip("Event triggered on gesture")]
private HPUIGestureEvent gestureEvent = new HPUIGestureEvent();

/// <inheritdoc />
public HPUIGestureEvent GestureEvent { get => gestureEvent; set => gestureEvent = value; }

[SerializeField]
[Tooltip("Interation hover radius.")]
private float interactionHoverRadius = 0.015f;

/// <summary>
/// Event triggered on gesture
/// Interation hover radius.
/// </summary>
public HPUIGestureEvent GestureEvent { get => gestureEvent; set => gestureEvent = value; }
public float InteractionHoverRadius { get => interactionHoverRadius; set => interactionHoverRadius = value; }

protected IHPUIGestureLogic gestureLogic;
private List<IXRInteractable> validTargets = new List<IXRInteractable>();
private bool justStarted = false;
private Vector3 lastInteractionPoint;
private PhysicsScene physicsScene;
private RaycastHit[] sphereCastHits = new RaycastHit[25];
private Collider[] overlapSphereHits = new Collider[25];

/// <inheritdoc />
protected override void Awake()
{
base.Awake();
keepSelectedTargetValid = true;
physicsScene = gameObject.scene.GetPhysicsScene();
gestureLogic = new HPUIGestureLogicUnified(this, TapTimeThreshold, TapDistanceThreshold);
}

Expand All @@ -66,6 +75,13 @@ protected void OnValidate()
}
#endif

/// <inheritdoc />
protected override void OnEnable()
{
base.OnEnable();
justStarted = true;
}

/// <inheritdoc />
protected override void OnSelectEntering(SelectEnterEventArgs args)
{
Expand All @@ -80,6 +96,68 @@ protected override void OnSelectExiting(SelectExitEventArgs args)
gestureLogic.OnSelectExiting(args.interactableObject as IHPUIInteractable);
}

/// <inheritdoc />
public override void PreprocessInteractor(XRInteractionUpdateOrder.UpdatePhase updatePhase)
{
base.PreprocessInteractor(updatePhase);

// Following the logic in XRPokeInteractor
if (updatePhase == XRInteractionUpdateOrder.UpdatePhase.Dynamic)
{
validTargets.Clear();

// Hover Check
Vector3 pokeInteractionPoint = GetAttachTransform(null).position;
Vector3 overlapStart = lastInteractionPoint;
Vector3 interFrameEnd = pokeInteractionPoint; // FIXME: Think of getting this of collision points?

BurstPhysicsUtils.GetSphereOverlapParameters(overlapStart, interFrameEnd, out Vector3 normalizedOverlapVector, out float overlapSqrMagnitude, out float overlapDistance);

// If no movement is recorded.
// Check if spherecast size is sufficient for proper cast, or if first frame since last frame poke position will be invalid.
int numberOfOverlaps;

if (justStarted || overlapSqrMagnitude < 0.001f)
{
numberOfOverlaps = physicsScene.OverlapSphere(
interFrameEnd,
InteractionHoverRadius,
overlapSphereHits,
// FIXME: physics layers should be allowed to be set in inpsector
Physics.AllLayers,
// FIXME: QueryTriggerInteraction should be allowed to be set in inpsector
QueryTriggerInteraction.Ignore);
}
else
{
numberOfOverlaps = physicsScene.SphereCast(
overlapStart,
InteractionHoverRadius,
normalizedOverlapVector,
sphereCastHits,
overlapDistance,
// FIXME: physics layers should be allowed to be set in inpsector
Physics.AllLayers,
// FIXME: QueryTriggerInteraction should be allowed to be set in inpsector
QueryTriggerInteraction.Ignore);

}

lastInteractionPoint = pokeInteractionPoint;
justStarted = false;

for (var i = 0; i < numberOfOverlaps; ++i)
{
if (interactionManager.TryGetInteractableForCollider(sphereCastHits[i].collider, out var interactable) &&
interactable is IXRSelectInteractable selectable &&
interactable is IXRHoverInteractable hoverable && hoverable.IsHoverableBy(this))
{
validTargets.Add(interactable);
}
}
}
}

/// <inheritdoc />
public override void ProcessInteractor(XRInteractionUpdateOrder.UpdatePhase updatePhase)
{
Expand All @@ -93,25 +171,13 @@ public override void ProcessInteractor(XRInteractionUpdateOrder.UpdatePhase upda
public override void GetValidTargets(List<IXRInteractable> targets)
{
base.GetValidTargets(targets);
if (handedness == Handedness.Invalid)
{
throw new InvalidOperationException("handedness not correcly set.");
}

List<IXRInteractable> recievedTargets = ListPool<IXRInteractable>.Get();
recievedTargets.AddRange(targets.Distinct());

targets.Clear();
validTargets.Clear();
foreach(IXRInteractable target in recievedTargets.Select(t => t as IHPUIInteractable).Where(ht => ht != null).OrderBy(ht => ht.zOrder))
foreach(IXRInteractable target in validTargets.Select(t => t as IHPUIInteractable).Where(ht => ht != null).OrderBy(ht => ht.zOrder))
{
targets.Add(target);
validTargets.Add(target);
}

// TODO check if an interactable with lower z order is selected. If so cancel it.

ListPool<IXRInteractable>.Release(recievedTargets);
}

// NOTE: PokeInteractor has a bug where it doesn't account for the re-prioritization.
Expand Down

0 comments on commit d6ac0bd

Please sign in to comment.