Skip to content

Commit

Permalink
Introduce IInteractorVisualizer and implement foundation for separati…
Browse files Browse the repository at this point in the history
…ng visualization from interactor logic (#132)

* Define IInteractorVisualizer

* Define BaseInteractorVisualizer

* Begin setup of line interactor visualizer

* Extract far interactor visualizer
  • Loading branch information
FejZa authored Jun 20, 2024
1 parent 524ef85 commit f9ba807
Show file tree
Hide file tree
Showing 17 changed files with 889 additions and 560 deletions.
515 changes: 148 additions & 367 deletions Assets~/StandardAssets/Interactors/FarInteractor.prefab

Large diffs are not rendered by default.

405 changes: 405 additions & 0 deletions Assets~/StandardAssets/Interactors/LineInteractorVisualizer.prefab

Large diffs are not rendered by default.

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

39 changes: 33 additions & 6 deletions Runtime/Input/Interactors/BaseControllerInteractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public abstract class BaseControllerInteractor : ControllerPoseSynchronizer, ICo
[SerializeField]
private bool setCursorVisibilityOnSourceDetected = false;

[SerializeField]
private BaseInteractorVisualizer visualizer = null;

private GameObject cursorInstance = null;
private Vector3 lastPointerPosition = Vector3.zero;
private readonly List<InputAction> inputDownActions = new List<InputAction>();
Expand All @@ -91,10 +94,8 @@ public abstract class BaseControllerInteractor : ControllerPoseSynchronizer, ICo
/// </summary>
protected bool IsSelectPressed { get; set; } = false;

/// <summary>
/// <c>true</c>, if any <see cref="InputAction"/> is down on this <see cref="IInteractor"/>.
/// </summary>
protected bool IsInputDown => inputDownActions.Count > 0;
/// <inheritdoc />
public bool IsInputDown => inputDownActions.Count > 0;

/// <summary>
/// True if select has been pressed once since this component was enabled
Expand Down Expand Up @@ -384,6 +385,11 @@ protected override void Start()
{
base.Start();
SetCursor();

if (visualizer.IsNotNull())
{
visualizer.Interactor = this;
}
}

/// <inheritdoc/>
Expand Down Expand Up @@ -419,12 +425,34 @@ protected override void OnDisable()
}
}

/// <inheritdoc/>
protected override void OnDestroy()
{
if (visualizer.IsNotNull())
{
visualizer.gameObject.Destroy();
}

base.OnDestroy();
}

/// <inheritdoc />
public virtual void OnPreRaycast() { }
public virtual void OnPreRaycast()
{
if (visualizer.IsNotNull())
{
visualizer.OnPreRaycast();
}
}

/// <inheritdoc />
public virtual void OnPostRaycast()
{
if (visualizer.IsNotNull())
{
visualizer.OnPostRaycast();
}

if (grabAction != InputAction.None)
{
if (IsGrabPressed)
Expand All @@ -437,7 +465,6 @@ public virtual void OnPostRaycast()
if (IsSelectPressed)
{
DragHandler(pointerAction);

}
}
}
Expand Down
19 changes: 19 additions & 0 deletions Runtime/Input/Interactors/BaseInteractorVisualizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Reality Collective. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using UnityEngine;

namespace RealityToolkit.Input.Interactors
{
public abstract class BaseInteractorVisualizer : MonoBehaviour, IInteractorVisualizer
{
/// <inheritdoc />
public virtual IInteractor Interactor { get; set; }

/// <inheritdoc />
public virtual void OnPreRaycast() { }

/// <inheritdoc />
public virtual void OnPostRaycast() { }
}
}
11 changes: 11 additions & 0 deletions Runtime/Input/Interactors/BaseInteractorVisualizer.cs.meta

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

194 changes: 9 additions & 185 deletions Runtime/Input/Interactors/FarInteractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,176 +2,52 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using RealityToolkit.Definitions.Physics;
using RealityToolkit.Utilities.Lines.DataProviders;
using RealityToolkit.Utilities.Lines.Renderers;
using UnityEngine;
using UnityEngine.Serialization;

namespace RealityToolkit.Input.Interactors
{
/// <summary>
/// A simple line pointer for drawing lines from the input source origin to the current pointer position.
/// </summary>
[AddComponentMenu("")]
public class FarInteractor : BaseControllerInteractor
{
[SerializeField]
private Gradient defaultLineColor = new Gradient();

protected Gradient DefaultLineColor
{
get => defaultLineColor;
set => defaultLineColor = value;
}

[SerializeField]
private Gradient lineColorInputDown = new Gradient();

protected Gradient LineColorInputDown
{
get => lineColorInputDown;
set => lineColorInputDown = value;
}

[Range(2, 50)]
[SerializeField]
[FormerlySerializedAs("LineCastResolution")]
[Tooltip("This setting has a high performance cost. Values above 20 are not recommended.")]
private int lineCastResolution = 10;

protected int LineCastResolution
{
get => lineCastResolution;
set => lineCastResolution = value;
}

[SerializeField]
private BaseLineDataProvider lineBase;

/// <summary>
/// The Line Data Provider driving this pointer.
/// </summary>
public BaseLineDataProvider LineBase => lineBase;

[SerializeField]
[Tooltip("If no line renderers are specified, this array will be auto-populated on startup.")]
private BaseLineRenderer[] lineRenderers;
private int lineCastResolution = 2;

/// <summary>
/// The current line renderers that this pointer is utilizing.
/// The amount of rays to cast.
/// </summary>
/// <remarks>
/// If no line renderers are specified, this array will be auto-populated on startup.
/// This setting has a high performance cost. Values above 20 are not recommended.
/// </remarks>
public BaseLineRenderer[] LineRenderers
public int LineCastResolution
{
get => lineRenderers;
set => lineRenderers = value;
get => lineCastResolution;
set => lineCastResolution = value;
}

/// <inheritdoc />
public override bool IsFarInteractor => true;

private void CheckInitialization()
{
if (lineBase == null)
{
lineBase = GetComponent<BaseLineDataProvider>();
}

if (lineBase == null)
{
Debug.LogError($"No Mixed Reality Line Data Provider found on {gameObject.name}. Did you forget to add a Line Data provider?");
}

if (lineBase != null && (lineRenderers == null || lineRenderers.Length == 0))
{
lineRenderers = lineBase.GetComponentsInChildren<BaseLineRenderer>();
}

if (lineRenderers == null || lineRenderers.Length == 0)
{
Debug.LogError($"No Mixed Reality Line Renderers found on {gameObject.name}. Did you forget to add a Mixed Reality Line Renderer?");
}
}

#region MonoBehaviour Implementation

protected virtual void OnValidate()
{
CheckInitialization();
}

protected override void OnEnable()
{
base.OnEnable();
CheckInitialization();
}

#endregion MonoBehaviour Implementation

#region IPointer Implementation

/// <inheritdoc />
public override void OnPreRaycast()
{
Debug.Assert(lineBase != null);

lineBase.UpdateMatrix();

if (RayStabilizer != null)
{
RayStabilizer.UpdateStability(Rays[0].Origin, Rays[0].Direction);
Rays[0].CopyRay(RayStabilizer.StableRay, Extent);
}

TryGetPointerPosition(out var pointerPosition);
TryGetPointerRotation(out var pointerRotation);

// Set our first and last points
lineBase.FirstPoint = pointerPosition;

if (IsFocusLocked && Result.CurrentTarget != null)
{
if (SyncedTarget != null)
{
// Now raycast out like nothing happened so we can get an updated pointer position.
lineBase.LastPoint = pointerPosition + pointerRotation * (Vector3.forward * Extent);
}
else
{
// Set the line to the locked position.
lineBase.LastPoint = Result.EndPoint;
}
}
else
{
lineBase.LastPoint = pointerPosition + pointerRotation * (Vector3.forward * Extent);
}

// Make sure our array will hold
if (Rays == null || Rays.Length != lineCastResolution)
{
Rays = new RayStep[lineCastResolution];
}

var stepSize = 1f / Rays.Length;
var lastPoint = lineBase.GetUnClampedPoint(0f);

for (int i = 0; i < Rays.Length; i++)
{
var currentPoint = lineBase.GetUnClampedPoint(stepSize * (i + 1));
Rays[i].UpdateRayStep(ref lastPoint, ref currentPoint);
lastPoint = currentPoint;
}
base.OnPreRaycast();
}

/// <inheritdoc />
public override void OnPostRaycast()
{
if (!IsInteractionEnabled)
{
lineBase.enabled = false;

if (BaseCursor != null)
{
BaseCursor.IsVisible = false;
Expand All @@ -180,64 +56,12 @@ public override void OnPostRaycast()
return;
}

base.OnPostRaycast();

Gradient lineColor;

lineBase.enabled = true;

if (BaseCursor != null)
{
BaseCursor.IsVisible = true;
}

// Used to ensure the line doesn't extend beyond the cursor
float cursorOffsetWorldLength = BaseCursor?.SurfaceCursorDistance ?? 0f;

// The distance the ray travels through the world before it hits something.
// Measured in world-units (as opposed to normalized distance).
var clearWorldLength = (Result?.CurrentTarget != null) ? Result.RayDistance : Extent;

lineColor = IsInputDown ? LineColorInputDown : DefaultLineColor;

int maxClampLineSteps = lineCastResolution;

for (var i = 0; i < lineRenderers.Length; i++)
{
var lineRenderer = lineRenderers[i];
// Renderers are enabled by default if line is enabled
lineRenderer.enabled = true;
maxClampLineSteps = Mathf.Max(maxClampLineSteps, lineRenderer.LineStepCount);
lineRenderer.LineColor = lineColor;
}

// If focus is locked, we're sticking to the target
// So don't clamp the world length
if (IsFocusLocked && Result.CurrentTarget != null)
{
if (SyncedTarget != null)
{
if (Result.GrabPoint == Vector3.zero)
{
LineBase.LastPoint = Result.EndPoint;
}
else
{
LineBase.LastPoint = Result.GrabPoint;
}
}

float cursorOffsetLocalLength = LineBase.GetNormalizedLengthFromWorldLength(cursorOffsetWorldLength);
LineBase.LineEndClamp = 1 - cursorOffsetLocalLength;
}
else
{
// Otherwise clamp the line end by the clear distance
float clearLocalLength = lineBase.GetNormalizedLengthFromWorldLength(clearWorldLength - cursorOffsetWorldLength, maxClampLineSteps);
lineBase.LineEndClamp = clearLocalLength;
}
base.OnPostRaycast();
}

#endregion IPointer Implementation
}
}
3 changes: 3 additions & 0 deletions Runtime/Input/Interactors/GenericPointer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public virtual IController Controller
/// <inheritdoc/>
public bool IsOverUI { get; } = false;

/// <inheritdoc/>
public bool IsInputDown { get; private set; }

/// <inheritdoc />
public virtual IInputSource InputSource
{
Expand Down
5 changes: 5 additions & 0 deletions Runtime/Input/Interactors/IInteractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ public interface IInteractor : IEqualityComparer
/// </summary>
bool IsFarInteractor { get; }

/// <summary>
/// <c>true</c>, if any <see cref="InputAction"/> is down on this <see cref="IInteractor"/>.
/// </summary>
bool IsInputDown { get; }

/// <summary>
/// This pointer's input source parent.
/// </summary>
Expand Down
Loading

0 comments on commit f9ba807

Please sign in to comment.