137 lines
6.3 KiB
C#
137 lines
6.3 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using UnityEngine;
|
|
|
|
namespace Microsoft.MixedReality.Toolkit.Utilities.Solvers
|
|
{
|
|
/// <summary>
|
|
/// This solver determines the position and orientation of an object as a directional indicator.
|
|
/// From the point of reference of the SolverHandler Tracked Target, this indicator will orient towards the DirectionalTarget supplied.
|
|
/// If the Directional Target is deemed within view of our frame of reference, then all renderers under this Solver will be disabled. They will be enabled otherwise
|
|
/// </summary>
|
|
[AddComponentMenu("Scripts/MRTK/SDK/DirectionalIndicator")]
|
|
public class DirectionalIndicator : Solver
|
|
{
|
|
/// <summary>
|
|
/// The GameObject transform to point the indicator towards when this object is not in view.
|
|
/// The frame of reference for viewing is defined by the Solver Handler Tracked Target Type
|
|
/// </summary>
|
|
[Tooltip("The GameObject transform to point the indicator towards when this object is not in view.\nThe frame of reference for viewing is defined by the Solver Handler Tracked Target Type")]
|
|
public Transform DirectionalTarget;
|
|
|
|
/// <summary>
|
|
/// The minimum scale for the indicator object
|
|
/// </summary>
|
|
[Tooltip("The offset from center to place the indicator. If frame of reference is the camera, then the object will be this distance from center of screen")]
|
|
[Min(0.0f)]
|
|
public float MinIndicatorScale = 0.05f;
|
|
|
|
/// <summary>
|
|
/// The maximum scale for the indicator object
|
|
/// </summary>
|
|
[Tooltip("The offset from center to place the indicator. If frame of reference is the camera, then the object will be this distance from center of screen")]
|
|
[Min(0.0f)]
|
|
public float MaxIndicatorScale = 0.2f;
|
|
|
|
/// <summary>
|
|
/// Multiplier factor to increase or decrease FOV range for testing if object is visible and thus turn off indicator
|
|
/// </summary>
|
|
[Tooltip("Multiplier factor to increase or decrease FOV range for testing if object is visible and thus turn off indicator")]
|
|
[Min(0.1f)]
|
|
public float VisibilityScaleFactor = 0.8f;
|
|
|
|
/// <summary>
|
|
/// The offset from center to place the indicator. If frame of reference is the camera, then the object will be this distance from center of screen
|
|
/// </summary>
|
|
[Tooltip("The offset from center to place the indicator. If frame of reference is the camera, then the object will be this distance from center of screen")]
|
|
[Min(0.0f)]
|
|
public float ViewOffset = 0.3f;
|
|
|
|
private bool indicatorShown = false;
|
|
|
|
protected override void Start()
|
|
{
|
|
base.Start();
|
|
|
|
SetIndicatorVisibility(ShouldShowIndicator());
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
bool showIndicator = ShouldShowIndicator();
|
|
if (showIndicator != indicatorShown)
|
|
{
|
|
SetIndicatorVisibility(showIndicator);
|
|
}
|
|
}
|
|
|
|
private bool ShouldShowIndicator()
|
|
{
|
|
if (DirectionalTarget == null || SolverHandler.TransformTarget == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return !MathUtilities.IsInFOV(DirectionalTarget.position, SolverHandler.TransformTarget,
|
|
VisibilityScaleFactor * CameraCache.Main.fieldOfView, VisibilityScaleFactor * CameraCache.Main.GetHorizontalFieldOfViewDegrees(),
|
|
CameraCache.Main.nearClipPlane, CameraCache.Main.farClipPlane);
|
|
}
|
|
|
|
private void SetIndicatorVisibility(bool showIndicator)
|
|
{
|
|
SolverHandler.UpdateSolvers = showIndicator;
|
|
|
|
foreach (var renderer in GetComponentsInChildren<Renderer>())
|
|
{
|
|
renderer.enabled = showIndicator;
|
|
}
|
|
|
|
indicatorShown = showIndicator;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void SolverUpdate()
|
|
{
|
|
// SolverUpdate is generally called in LateUpdate, at a time when it's possible that the DirectionalTarget
|
|
// has already been destroyed. This ensures that if the object has been destroyed, we don't access invalid
|
|
if (DirectionalTarget == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// This is the frame of reference to use when solving for the position of this.gameobject
|
|
// The frame of reference will likely be the main camera
|
|
var solverReferenceFrame = SolverHandler.TransformTarget;
|
|
|
|
Vector3 origin = solverReferenceFrame.position + solverReferenceFrame.forward;
|
|
|
|
Vector3 trackerToTargetDirection = (DirectionalTarget.position - solverReferenceFrame.position).normalized;
|
|
|
|
// Project the vector (from the frame of reference (SolverHandler target) to the Directional Target) onto the "viewable" plane
|
|
Vector3 indicatorDirection = Vector3.ProjectOnPlane(trackerToTargetDirection, -solverReferenceFrame.forward).normalized;
|
|
|
|
// If the our indicator direction is 0, set the direction to the right.
|
|
// This will only happen if the frame of reference (SolverHandler target) is facing directly away from the directional target.
|
|
if (indicatorDirection == Vector3.zero)
|
|
{
|
|
indicatorDirection = solverReferenceFrame.right;
|
|
}
|
|
|
|
// The final position is translated from the center of the frame of reference plane along the indicator direction vector.
|
|
GoalPosition = origin + indicatorDirection * ViewOffset;
|
|
|
|
// Find the rotation from the facing direction to the target object.
|
|
GoalRotation = Quaternion.LookRotation(solverReferenceFrame.forward, indicatorDirection);
|
|
|
|
// Scale the solver based to be more prominent if the object is far away from the field of view
|
|
float minVisiblityAngle = VisibilityScaleFactor * CameraCache.Main.fieldOfView * 0.5f;
|
|
|
|
float angleToVisbilityFOV = Vector3.Angle(trackerToTargetDirection - solverReferenceFrame.position, solverReferenceFrame.forward) - minVisiblityAngle;
|
|
float visibilityScale = 180f - minVisiblityAngle;
|
|
|
|
GoalScale = Vector3.one * Mathf.Lerp(MinIndicatorScale, MaxIndicatorScale, angleToVisbilityFOV / visibilityScale);
|
|
}
|
|
}
|
|
}
|