VR4Medical/ICI/Library/PackageCache/com.unity.xr.interaction.toolkit@42ef3600567b/Runtime/Utilities/Tweenables/SmartTweenableVariables/SmartFollowQuaternionTweenableVariable.cs
2025-07-29 13:45:50 +03:00

125 lines
5.9 KiB
C#

#if BURST_PRESENT
using Unity.Burst;
#endif
using System;
using Unity.Mathematics;
using UnityEngine.XR.Interaction.Toolkit.Utilities.Tweenables.Primitives;
namespace UnityEngine.XR.Interaction.Toolkit.Utilities.Tweenables.SmartTweenableVariables
{
/// <summary>
/// This class expands on the Quaternion tweenable variable to introduce two concepts:
/// <list type="bullet">
/// <item>
/// <description>A dynamic threshold angle that grows over time in a range,
/// that prevents updating the target so long as the value being assigned to the target is within that threshold.
/// </description>
/// </item>
/// <item>
/// <description>A variable speed tween (<see cref="HandleSmartTween"/>) that inputs a lower and upper range speed for tweening.
/// The closer the value is to the target, the faster the tween.
/// </description>
/// </item>
/// </list>
/// </summary>
/// <seealso cref="SmartFollowVector3TweenableVariable"/>
#if BURST_PRESENT
[BurstCompile]
#endif
[Obsolete("The Affordance System namespace and all associated classes have been deprecated. The existing affordance system will be moved, replaced and updated with a new interaction feedback system in a future version of XRI.")]
public class SmartFollowQuaternionTweenableVariable : QuaternionTweenableVariable
{
/// <summary>
/// Minimum angle offset allowed in degrees.
/// </summary>
public float minAngleAllowed { get; set; }
/// <summary>
/// Maximum angle offset allowed in degrees.
/// </summary>
public float maxAngleAllowed { get; set; }
/// <summary>
/// Time required to elapse before the max angle offset allowed goes from the min angle to the max.
/// </summary>
public float minToMaxDelaySeconds { get; set; }
float m_LastUpdateTime;
/// <summary>
/// Constructor for SmartFollowQuaternionTweenableVariable.
/// </summary>
/// <param name="minAngleAllowed">Minimum angle offset (in degrees) from target before which tween starts.</param>
/// <param name="maxAngleAllowed">Maximum angle offset (in degrees) from target before tween targets, when time threshold is reached.</param>
/// <param name="minToMaxDelaySeconds">Time required to elapse (in seconds) before the max angle offset allowed goes from the min angle offset to the max.</param>
public SmartFollowQuaternionTweenableVariable(
float minAngleAllowed = 0.1f,
float maxAngleAllowed = 5,
float minToMaxDelaySeconds = 3f)
{
this.minAngleAllowed = minAngleAllowed;
this.maxAngleAllowed = maxAngleAllowed;
this.minToMaxDelaySeconds = minToMaxDelaySeconds;
}
/// <summary>
/// Checks if the angle difference between the current target rotation and a new target rotation is within a dynamically determined threshold,
/// based on the time since the last update.
/// </summary>
/// <param name="newTarget">The new target rotation as a Quaternion.</param>
/// <returns>Returns <see langword="true"/> if the angle difference between the current and new targets is within the allowed threshold, <see langword="false"/> otherwise.</returns>
public bool IsNewTargetWithinThreshold(Quaternion newTarget)
{
float newAngleTargetOffset = Quaternion.Angle(target, newTarget);
float timeSinceLastUpdate = Time.unscaledTime - m_LastUpdateTime;
// Widen tolerance zone over time
float allowedTargetAngleOffset = Mathf.Lerp(minAngleAllowed, maxAngleAllowed, Mathf.Clamp01(timeSinceLastUpdate / minToMaxDelaySeconds));
return newAngleTargetOffset > allowedTargetAngleOffset;
}
/// <summary>
/// Updates the target rotation to a new value if it is within a dynamically determined threshold,
/// based on the time since the last update.
/// </summary>
/// <param name="newTarget">The new target rotation as a Quaternion.</param>
/// <returns>Returns <see langword="true"/> if the target rotation is updated, <see langword="false"/> otherwise.</returns>
public bool SetTargetWithinThreshold(Quaternion newTarget)
{
bool isWithinThreshold = IsNewTargetWithinThreshold(newTarget);
if (isWithinThreshold)
target = newTarget;
return isWithinThreshold;
}
/// <inheritdoc />
protected override void OnTargetChanged(Quaternion newTarget)
{
m_LastUpdateTime = Time.unscaledTime;
}
/// <summary>
/// Tween to new target with variable speed according to distance from target.
/// The closer the target is to the current value, the faster the tween.
/// </summary>
/// <param name="deltaTime">Time elapsed since last tween.</param>
/// <param name="lowerSpeed">Lower speed limit new tween target will clamp to.</param>
/// <param name="upperSpeed">Upper speed limit new tween target will clamp to.</param>
public void HandleSmartTween(float deltaTime, float lowerSpeed, float upperSpeed)
{
float angleOffsetDeg = Quaternion.Angle(target, Value);
ComputeNewTweenTarget(deltaTime, angleOffsetDeg, maxAngleAllowed, lowerSpeed, upperSpeed, out float newTweenTarget);
HandleTween(newTweenTarget);
}
#if BURST_PRESENT
[BurstCompile]
#endif
static void ComputeNewTweenTarget(float deltaTime, float angleOffsetDeg, float maxAngleAllowed, float lowerSpeed, float upperSpeed, out float newTweenTarget)
{
float speedMultiplier = (1f - math.clamp(angleOffsetDeg / maxAngleAllowed, 0f, 1f));
newTweenTarget = deltaTime * math.clamp(speedMultiplier * upperSpeed, lowerSpeed, upperSpeed);
}
}
}