// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Utilities { /// /// A ray whose position and direction are stabilized in a way similar to how gaze stabilization /// works in HoloLens. /// /// The ray uses Anatolie Gavrulic's "DynamicExpDecay" filter to stabilize the ray /// this filter adjusts its smoothing factor based on the velocity of the filtered object /// /// The formula is /// Y_smooted += ∆𝑌_𝑟 /// where /// 〖∆𝑌_𝑟=∆𝑌∗〖0.5〗^(∆𝑌/〖Halflife〗) /// /// In code, LERP(signal, oldValue, POW(0.5, ABS(signal – oldValue) / hl) /// public class StabilizedRay { /// /// Half life used for position decay calculations. /// public float HalfLifePosition { get; } = 0.1f; /// /// Half life used for velocity decay calculations. /// public float HalfLifeDirection { get; } = 0.1f; /// /// Computed Stabilized position. /// public Vector3 StabilizedPosition { get; private set; } /// /// Computed stabilized direction. /// public Vector3 StabilizedDirection { get; private set; } private bool isInitialized = false; /// /// HalfLife closer to zero means lerp closer to one. /// public StabilizedRay(float halfLife) { HalfLifePosition = halfLife; HalfLifeDirection = halfLife; } /// /// Add sample to ray stabilizer. /// /// New Sample used to update stabilized ray. public void AddSample(Ray ray) { if (!isInitialized) { StabilizedPosition = ray.origin; StabilizedDirection = ray.direction.normalized; isInitialized = true; } else { StabilizedPosition = DynamicExpDecay(StabilizedPosition, ray.origin, HalfLifePosition); StabilizedDirection = DynamicExpDecay(StabilizedDirection, ray.direction.normalized, HalfLifeDirection); } } /// /// Compute dynamic exponential coefficient. /// /// Half life /// Distance delta /// The dynamic exponential coefficient. public static float DynamicExpCoefficient(float hLife, float delta) { if (hLife == 0) { return 1; } return 1.0f - Mathf.Pow(0.5f, delta / hLife); } /// /// Compute stabilized vector3 given a previously stabilized value, and a new sample, given a half life. /// /// Previous stabilized Vector3. /// New Vector3 sample. /// Half life used for stabilization. /// Stabilized Vector 3. public static Vector3 DynamicExpDecay(Vector3 from, Vector3 to, float hLife) { return Vector3.Lerp(from, to, DynamicExpCoefficient(hLife, Vector3.Distance(to, from))); } } }