Move third party assets to ThirdParty folder
This commit is contained in:
8
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation.meta
vendored
Normal file
8
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1d90ebea4cf4029834b50cc6c8b968a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Avatars.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Avatars.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 569325838cef427db2a7071d0b396e63
|
||||
timeCreated: 1643803757
|
||||
391
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Avatars/UxrFaceGestures.cs
vendored
Normal file
391
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Avatars/UxrFaceGestures.cs
vendored
Normal file
@@ -0,0 +1,391 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFaceGestures.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Avatars
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows to simulate facial gestures like eyes movement/blinking and mouth using
|
||||
/// the microphone input.
|
||||
/// </summary>
|
||||
public class UxrFaceGestures : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[Header("Blinking")] [SerializeField] private bool _blinkEyes = true;
|
||||
[SerializeField] private Transform _eyeLidTopLeft;
|
||||
[SerializeField] private Transform _eyeLidTopRight;
|
||||
[SerializeField] private Transform _eyeLidBottomLeft;
|
||||
[SerializeField] private Transform _eyeLidBottomRight;
|
||||
[SerializeField] private Vector3 _eyeBlinkTopLocalAxis = Vector3.right;
|
||||
[SerializeField] private float _eyeBlinkTopClosedAngle = 30.0f;
|
||||
[SerializeField] private Vector3 _eyeBlinkBottomLocalAxis = Vector3.right;
|
||||
[SerializeField] private float _eyeBlinkBottomClosedAngle;
|
||||
[SerializeField] private float _eyeBlinkDurationMin = 0.05f;
|
||||
[SerializeField] private float _eyeBlinkDurationMax = 0.1f;
|
||||
[SerializeField] private float _eyeBlinkIntervalMin = 1.0f;
|
||||
[SerializeField] private float _eyeBlinkIntervalMax = 5.0f;
|
||||
|
||||
[Header("Eye movement")] [SerializeField] private bool _moveEyes = true;
|
||||
[SerializeField] private Transform _eyeLeft;
|
||||
[SerializeField] private Transform _eyeRight;
|
||||
[SerializeField] [Range(0.0f, 90.0f)] private float _eyeLookStraightAngleRange = 3.0f;
|
||||
[SerializeField] [Range(0.0f, 90.0f)] private float _eyeLookEdgeAngleMin = 15.0f;
|
||||
[SerializeField] [Range(0.0f, 90.0f)] private float _eyeLookEdgeAngleMax = 45.0f;
|
||||
[SerializeField] [Range(0.0f, 1.0f)] private float _eyeLookEdgeProbability = 0.3f;
|
||||
[SerializeField] private float _eyeSwitchLookDurationMin = 0.05f;
|
||||
[SerializeField] private float _eyeSwitchLookDurationMax = 0.1f;
|
||||
[SerializeField] private float _eyeSwitchLookIntervalMin = 0.25f;
|
||||
[SerializeField] private float _eyeSwitchLookIntervalMax = 2.0f;
|
||||
|
||||
[Header("Mouth movement")] [SerializeField] private bool _moveMouthUsingMic = true;
|
||||
[SerializeField] private float _microphoneAmplification = 1.0f;
|
||||
[SerializeField] private Transform _mouthOpenTransform;
|
||||
[SerializeField] private Vector3 _mouthOpenLocalAxis = Vector3.right;
|
||||
[SerializeField] private float _mouthClosedAngle;
|
||||
[SerializeField] private float _mouthMaxOpenAngle = 5.0f;
|
||||
[SerializeField] [Range(0.0f, 0.2f)] private float _mouthRotationDamp = 0.05f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
StartCoroutine(BlinkCoroutine());
|
||||
StartCoroutine(SwitchLookCoroutine());
|
||||
|
||||
if (_moveMouthUsingMic && Microphone.devices.Length > 0)
|
||||
{
|
||||
_microphoneClipRecord = Microphone.Start(null, true, 10, 44100);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the microphone resource if it's in use.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
if (Microphone.IsRecording(null) && _moveMouthUsingMic)
|
||||
{
|
||||
Microphone.End(null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additional initialization.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (_eyeLidTopLeft)
|
||||
{
|
||||
_localRotEyeLidTopLeft = _eyeLidTopLeft.localRotation;
|
||||
}
|
||||
if (_eyeLidTopRight)
|
||||
{
|
||||
_localRotEyeLidTopRight = _eyeLidTopRight.localRotation;
|
||||
}
|
||||
if (_eyeLidBottomLeft)
|
||||
{
|
||||
_localRotEyeLidBottomLeft = _eyeLidBottomLeft.localRotation;
|
||||
}
|
||||
if (_eyeLidBottomLeft)
|
||||
{
|
||||
_localRotEyeLidBottomRight = _eyeLidBottomRight.localRotation;
|
||||
}
|
||||
|
||||
if (_eyeLeft)
|
||||
{
|
||||
_localRotEyeLeft = _eyeLeft.localRotation;
|
||||
}
|
||||
if (_eyeRight)
|
||||
{
|
||||
_localRotEyeRight = _eyeRight.localRotation;
|
||||
}
|
||||
|
||||
if (_mouthOpenTransform)
|
||||
{
|
||||
_localRotMouth = _mouthOpenTransform.localRotation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the mouth if the microphone is being used.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (Microphone.IsRecording(null) && _mouthOpenTransform && _moveMouthUsingMic)
|
||||
{
|
||||
_mouthAngle = Mathf.SmoothDampAngle(_mouthAngle, Mathf.LerpAngle(_mouthClosedAngle, _mouthMaxOpenAngle, GetMicrophoneMaxLevel()), ref _mouthAngleDampSpeed, _mouthRotationDamp);
|
||||
_mouthOpenTransform.localRotation = _localRotMouth * Quaternion.AngleAxis(_mouthAngle, _mouthOpenLocalAxis);
|
||||
}
|
||||
else if (_mouthOpenTransform)
|
||||
{
|
||||
_mouthOpenTransform.localRotation = _localRotMouth;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Coroutines
|
||||
|
||||
/// <summary>
|
||||
/// Blinking coroutine.
|
||||
/// </summary>
|
||||
/// <returns>Coroutine enumerator</returns>
|
||||
private IEnumerator BlinkCoroutine()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Wait until next blink
|
||||
|
||||
yield return new WaitForSeconds(Random.Range(_eyeBlinkIntervalMin, _eyeBlinkIntervalMax));
|
||||
|
||||
if (_blinkEyes == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start blink with random duration
|
||||
|
||||
float blinkDuration = Random.Range(_eyeBlinkDurationMin, _eyeBlinkDurationMax);
|
||||
float startTime = Time.time;
|
||||
|
||||
// Close
|
||||
|
||||
while (Time.time - startTime < blinkDuration * 0.5f)
|
||||
{
|
||||
float t = Mathf.Clamp01((Time.time - startTime) / (blinkDuration * 0.5f));
|
||||
|
||||
Quaternion rotationTop = Quaternion.AngleAxis(_eyeBlinkTopClosedAngle * t, _eyeBlinkTopLocalAxis);
|
||||
Quaternion rotationBottom = Quaternion.AngleAxis(_eyeBlinkBottomClosedAngle * t, _eyeBlinkBottomLocalAxis);
|
||||
|
||||
if (_eyeLidTopLeft)
|
||||
{
|
||||
_eyeLidTopLeft.localRotation = _localRotEyeLidTopLeft * rotationTop;
|
||||
}
|
||||
if (_eyeLidTopRight)
|
||||
{
|
||||
_eyeLidTopRight.localRotation = _localRotEyeLidTopRight * rotationTop;
|
||||
}
|
||||
if (_eyeLidBottomLeft)
|
||||
{
|
||||
_eyeLidBottomLeft.localRotation = _localRotEyeLidBottomLeft * rotationBottom;
|
||||
}
|
||||
if (_eyeLidBottomRight)
|
||||
{
|
||||
_eyeLidBottomRight.localRotation = _localRotEyeLidBottomRight * rotationBottom;
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
Quaternion rotationClosedTop = Quaternion.AngleAxis(_eyeBlinkTopClosedAngle, _eyeBlinkTopLocalAxis);
|
||||
Quaternion rotationClosedBottom = Quaternion.AngleAxis(_eyeBlinkBottomClosedAngle, _eyeBlinkBottomLocalAxis);
|
||||
|
||||
if (_eyeLidTopLeft)
|
||||
{
|
||||
_eyeLidTopLeft.localRotation = _localRotEyeLidTopLeft * rotationClosedTop;
|
||||
}
|
||||
if (_eyeLidTopRight)
|
||||
{
|
||||
_eyeLidTopRight.localRotation = _localRotEyeLidTopRight * rotationClosedTop;
|
||||
}
|
||||
if (_eyeLidBottomLeft)
|
||||
{
|
||||
_eyeLidBottomLeft.localRotation = _localRotEyeLidBottomLeft * rotationClosedBottom;
|
||||
}
|
||||
if (_eyeLidBottomRight)
|
||||
{
|
||||
_eyeLidBottomRight.localRotation = _localRotEyeLidBottomRight * rotationClosedBottom;
|
||||
}
|
||||
|
||||
yield return null;
|
||||
|
||||
// Open
|
||||
|
||||
startTime = Time.time;
|
||||
|
||||
while (Time.time - startTime < blinkDuration * 0.5f)
|
||||
{
|
||||
float t = 1.0f - Mathf.Clamp01((Time.time - startTime) / (blinkDuration * 0.5f));
|
||||
|
||||
Quaternion rotationTop = Quaternion.AngleAxis(_eyeBlinkTopClosedAngle * t, _eyeBlinkTopLocalAxis);
|
||||
Quaternion rotationBottom = Quaternion.AngleAxis(_eyeBlinkBottomClosedAngle * t, _eyeBlinkBottomLocalAxis);
|
||||
|
||||
if (_eyeLidTopLeft)
|
||||
{
|
||||
_eyeLidTopLeft.localRotation = _localRotEyeLidTopLeft * rotationTop;
|
||||
}
|
||||
if (_eyeLidTopRight)
|
||||
{
|
||||
_eyeLidTopRight.localRotation = _localRotEyeLidTopRight * rotationTop;
|
||||
}
|
||||
if (_eyeLidBottomLeft)
|
||||
{
|
||||
_eyeLidBottomLeft.localRotation = _localRotEyeLidBottomLeft * rotationBottom;
|
||||
}
|
||||
if (_eyeLidBottomRight)
|
||||
{
|
||||
_eyeLidBottomRight.localRotation = _localRotEyeLidBottomRight * rotationBottom;
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (_eyeLidTopLeft)
|
||||
{
|
||||
_eyeLidTopLeft.localRotation = _localRotEyeLidTopLeft;
|
||||
}
|
||||
if (_eyeLidTopRight)
|
||||
{
|
||||
_eyeLidTopRight.localRotation = _localRotEyeLidTopRight;
|
||||
}
|
||||
if (_eyeLidBottomLeft)
|
||||
{
|
||||
_eyeLidBottomLeft.localRotation = _localRotEyeLidBottomLeft;
|
||||
}
|
||||
if (_eyeLidBottomRight)
|
||||
{
|
||||
_eyeLidBottomRight.localRotation = _localRotEyeLidBottomRight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that randomly switches where the eyes are looking.
|
||||
/// </summary>
|
||||
/// <returns>Coroutine enumerator</returns>
|
||||
private IEnumerator SwitchLookCoroutine()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Wait until next switch
|
||||
|
||||
yield return new WaitForSeconds(Random.Range(_eyeSwitchLookIntervalMin, _eyeSwitchLookIntervalMax));
|
||||
|
||||
if (_moveEyes == false)
|
||||
{
|
||||
if (_eyeLeft)
|
||||
{
|
||||
_eyeLeft.localRotation = _localRotEyeLeft;
|
||||
}
|
||||
if (_eyeRight)
|
||||
{
|
||||
_eyeRight.localRotation = _localRotEyeRight;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start switch with random duration
|
||||
|
||||
float switchDuration = Random.Range(_eyeSwitchLookDurationMin, _eyeSwitchLookDurationMax);
|
||||
float startTime = Time.time;
|
||||
|
||||
// Rotate
|
||||
|
||||
Quaternion rotation = Quaternion.identity;
|
||||
|
||||
if (Random.value < _eyeLookEdgeProbability)
|
||||
{
|
||||
rotation = Quaternion.RotateTowards(Quaternion.identity, Random.rotation, Random.Range(_eyeLookEdgeAngleMin, _eyeLookEdgeAngleMax));
|
||||
}
|
||||
else
|
||||
{
|
||||
rotation = Quaternion.RotateTowards(Quaternion.identity, Random.rotation, Random.Range(0.0f, _eyeLookStraightAngleRange));
|
||||
}
|
||||
|
||||
Quaternion rotLeft = _eyeLeft.localRotation;
|
||||
Quaternion rotRight = _eyeRight.localRotation;
|
||||
|
||||
while (Time.time - startTime < switchDuration)
|
||||
{
|
||||
float t = Mathf.Clamp01((Time.time - startTime) / switchDuration);
|
||||
|
||||
if (_eyeLeft)
|
||||
{
|
||||
_eyeLeft.localRotation = Quaternion.Slerp(rotLeft, _localRotEyeLeft * rotation, t);
|
||||
}
|
||||
if (_eyeRight)
|
||||
{
|
||||
_eyeRight.localRotation = Quaternion.Slerp(rotRight, _localRotEyeRight * rotation, t);
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the current microphone output level.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Microphone output level, approximately in the [0.0, 1.0] range but it's not clamped and the actual range is
|
||||
/// undefined.
|
||||
/// </returns>
|
||||
private float GetMicrophoneMaxLevel()
|
||||
{
|
||||
float maxLevel = 0;
|
||||
float[] waveData = new float[MicrophoneSampleWindow];
|
||||
int micPosition = Microphone.GetPosition(null) - (MicrophoneSampleWindow + 1);
|
||||
|
||||
if (micPosition < 0)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
_microphoneClipRecord.GetData(waveData, micPosition);
|
||||
|
||||
for (int i = 0; i < MicrophoneSampleWindow; ++i)
|
||||
{
|
||||
float wavePeak = waveData[i] * waveData[i];
|
||||
|
||||
if (maxLevel < wavePeak)
|
||||
{
|
||||
maxLevel = wavePeak;
|
||||
}
|
||||
}
|
||||
|
||||
return maxLevel * 1024.0f * _microphoneAmplification;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private const int MicrophoneSampleWindow = 128;
|
||||
|
||||
private Quaternion _localRotEyeLidTopLeft;
|
||||
private Quaternion _localRotEyeLidTopRight;
|
||||
private Quaternion _localRotEyeLidBottomLeft;
|
||||
private Quaternion _localRotEyeLidBottomRight;
|
||||
|
||||
private Quaternion _localRotEyeLeft;
|
||||
private Quaternion _localRotEyeRight;
|
||||
|
||||
private Quaternion _localRotMouth;
|
||||
private float _mouthAngle;
|
||||
private float _mouthAngleDampSpeed;
|
||||
private AudioClip _microphoneClipRecord;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Avatars/UxrFaceGestures.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Avatars/UxrFaceGestures.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4580b2065b1d39428ae4bb5e6840419
|
||||
timeCreated: 1549387631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Components.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Components.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d938f776bf314dd0b238ec5ea2d84058
|
||||
timeCreated: 1643801512
|
||||
90
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Components/UxrToggleComponent.cs
vendored
Normal file
90
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Components/UxrToggleComponent.cs
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrToggleComponent.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to toggle components enabled state back and forth at random times.
|
||||
/// </summary>
|
||||
public class UxrToggleComponent : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private MonoBehaviour[] _components;
|
||||
[SerializeField] private float _enabledDurationMin;
|
||||
[SerializeField] private float _enabledDurationMax;
|
||||
[SerializeField] private float _disabledDurationMin;
|
||||
[SerializeField] private float _disabledDurationMax;
|
||||
[SerializeField] private bool _useUnscaledTime;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Called each time the component is enabled. Sets up the next toggle time.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_startTime = _useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
_nextToggleTime = GetNextRelativeToggleTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called on each update. Checks if it is time to toggle the components.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
float time = (_useUnscaledTime ? Time.unscaledTime : Time.time) - _startTime;
|
||||
|
||||
if (time > _nextToggleTime)
|
||||
{
|
||||
foreach (MonoBehaviour component in _components)
|
||||
{
|
||||
component.enabled = !component.enabled;
|
||||
}
|
||||
|
||||
_startTime = _useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
_nextToggleTime = GetNextRelativeToggleTime();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next time the components will be toggled
|
||||
/// </summary>
|
||||
/// <returns>Next toggle time in seconds relative to the current time</returns>
|
||||
private float GetNextRelativeToggleTime()
|
||||
{
|
||||
if (_components.Length > 0 && _components[0].enabled)
|
||||
{
|
||||
return Random.Range(_enabledDurationMin, _enabledDurationMax);
|
||||
}
|
||||
if (_components.Length > 0 && !_components[0].enabled)
|
||||
{
|
||||
return Random.Range(_disabledDurationMin, _disabledDurationMax);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private float _startTime;
|
||||
private float _nextToggleTime;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Components/UxrToggleComponent.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Components/UxrToggleComponent.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a67b8fc64f10ba439788082a67721ae
|
||||
timeCreated: 1522850574
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects.meta
vendored
Normal file
8
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c55e0d258b8ace4cba63217ad6fa2f0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
36
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrDelayedDestroy.cs
vendored
Normal file
36
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrDelayedDestroy.cs
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrDelayedDestroy.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to destroy the <see cref="GameObject" /> it is attached to after a variable amount of seconds
|
||||
/// </summary>
|
||||
public class UxrDelayedDestroy : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private float _seconds;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Programs the object destruction.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
Destroy(gameObject, _seconds);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrDelayedDestroy.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrDelayedDestroy.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af3a08495f6a01846a24adb624144bb7
|
||||
timeCreated: 1534242279
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
313
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrObjectBlink.cs
vendored
Normal file
313
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrObjectBlink.cs
vendored
Normal file
@@ -0,0 +1,313 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrObjectBlink.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to make objects blink using their material's emission channel.
|
||||
/// </summary>
|
||||
public class UxrObjectBlink : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private MeshRenderer _renderer;
|
||||
[SerializeField] private int _materialSlot = -1;
|
||||
[SerializeField] private Color _colorNormal = Color.black;
|
||||
[SerializeField] private Color _colorHighlight = Color.white;
|
||||
[SerializeField] private float _blinksPerSec = 4.0f;
|
||||
[SerializeField] private float _durationSeconds = -1.0f;
|
||||
[SerializeField] private bool _useUnscaledTime;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the object is currently blinking.
|
||||
/// </summary>
|
||||
public bool IsBlinking { get; private set; } = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Starts a blinking animation using the emission material of an object.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The GameObject to blink</param>
|
||||
/// <param name="emissionColor">The emission color</param>
|
||||
/// <param name="blinksPerSec">The blink frequency</param>
|
||||
/// <param name="durationSeconds">Total duration of the blinking animation</param>
|
||||
/// <param name="materialSlot">
|
||||
/// -1 to target all renderer materials if there is more than one. An index between [0, materialCount
|
||||
/// - 1] to target a specific material only.
|
||||
/// </param>
|
||||
/// <param name="useUnscaledTime">
|
||||
/// Whether to use unscaled time (<see cref="Time.unscaledTime" />) or not (
|
||||
/// <see cref="Time.time" />).
|
||||
/// </param>
|
||||
/// <returns>Animation component</returns>
|
||||
public static UxrObjectBlink StartBlinking(GameObject gameObject, Color emissionColor, float blinksPerSec, float durationSeconds, int materialSlot = -1, bool useUnscaledTime = false)
|
||||
{
|
||||
if (gameObject == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
UxrObjectBlink blinkComponent = gameObject.GetOrAddComponent<UxrObjectBlink>();
|
||||
|
||||
blinkComponent.CheckInitialize();
|
||||
blinkComponent.StartBlinkingInternal(emissionColor, blinksPerSec, durationSeconds, materialSlot, useUnscaledTime);
|
||||
|
||||
return blinkComponent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops a blinking animation on an object if it has any.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">GameObject to stop the animation from</param>
|
||||
public static void StopBlinking(GameObject gameObject)
|
||||
{
|
||||
if (gameObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameObject.TryGetComponent<UxrObjectBlink>(out var blinkComponent))
|
||||
{
|
||||
blinkComponent.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given GameObject has any blinking animation running.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">GameObject to check</param>
|
||||
/// <returns>Whether the given GameObject has any blinking animation running</returns>
|
||||
public static bool CheckBlinking(GameObject gameObject)
|
||||
{
|
||||
if (gameObject == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UxrObjectBlink blinkComponent = gameObject.GetComponent<UxrObjectBlink>();
|
||||
|
||||
return blinkComponent != null && blinkComponent.IsBlinking;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the blinking animation parameters.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Renderer whose material will be animated</param>
|
||||
/// <param name="colorNormal">The emission color when it is not blinking</param>
|
||||
/// <param name="colorHighlight">The fully blinking color</param>
|
||||
/// <param name="blinksPerSec">The blinking frequency</param>
|
||||
/// <param name="durationSeconds">The total duration of the animation in seconds</param>
|
||||
/// <param name="materialSlot">
|
||||
/// -1 to target all renderer materials if there is more than one. An index between [0, materialCount
|
||||
/// - 1] to target a specific material only.
|
||||
/// </param>
|
||||
/// <param name="useUnscaledTime">
|
||||
/// Whether to use unscaled time (<see cref="Time.unscaledTime" />) or not (
|
||||
/// <see cref="Time.time" />).
|
||||
/// </param>
|
||||
public void Setup(MeshRenderer renderer, Color colorNormal, Color colorHighlight, float blinksPerSec = 4.0f, float durationSeconds = -1.0f, int materialSlot = -1, bool useUnscaledTime = false)
|
||||
{
|
||||
_renderer = renderer;
|
||||
_colorNormal = colorNormal;
|
||||
_colorHighlight = colorHighlight;
|
||||
_blinksPerSec = blinksPerSec;
|
||||
_durationSeconds = durationSeconds;
|
||||
_materialSlot = materialSlot;
|
||||
_useUnscaledTime = useUnscaledTime;
|
||||
IsBlinking = false;
|
||||
IsInitialized = false;
|
||||
CheckInitialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts or restarts the blinking animation using the current parameters.
|
||||
/// </summary>
|
||||
public void StartBlinkingWithCurrentParameters()
|
||||
{
|
||||
CheckInitialize();
|
||||
StartBlinkingInternal(_colorHighlight, _blinksPerSec, _durationSeconds, _materialSlot, _useUnscaledTime);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
CheckInitialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When re-enabled, starts blinking again with the current parameters.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
StartBlinkingWithCurrentParameters();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops blinking.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
StopBlinkingInternal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the blinking animation if active.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (IsBlinking && _renderer != null)
|
||||
{
|
||||
float timer = CurrentTime - _blinkStartTime;
|
||||
|
||||
if (_durationSeconds >= 0.0f && timer >= _durationSeconds)
|
||||
{
|
||||
StopBlinkingInternal();
|
||||
}
|
||||
else
|
||||
{
|
||||
float blend = (Mathf.Sin(timer * Mathf.PI * _blinksPerSec * 2.0f) + 1.0f) * 0.5f;
|
||||
|
||||
Material[] materials = _renderer.materials;
|
||||
|
||||
for (int i = 0; i < materials.Length; i++)
|
||||
{
|
||||
if (i == _materialSlot || _materialSlot < 0)
|
||||
{
|
||||
materials[i].SetColor(UxrConstants.Shaders.EmissionColorVarName, Color.Lerp(_colorNormal, _colorHighlight, blend));
|
||||
}
|
||||
}
|
||||
|
||||
_renderer.materials = materials;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component if necessary.
|
||||
/// </summary>
|
||||
private void CheckInitialize()
|
||||
{
|
||||
if (!IsInitialized)
|
||||
{
|
||||
if (_renderer == null)
|
||||
{
|
||||
_renderer = GetComponent<MeshRenderer>();
|
||||
}
|
||||
|
||||
if (_renderer != null)
|
||||
{
|
||||
_originalMaterials = _renderer.sharedMaterials;
|
||||
}
|
||||
|
||||
IsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts blinking.
|
||||
/// </summary>
|
||||
/// <param name="emissionColor">Emission color</param>
|
||||
/// <param name="blinksPerSec">Blinking frequency</param>
|
||||
/// <param name="durationSeconds">Total duration in seconds</param>
|
||||
/// <param name="materialSlot">The material(s) to target</param>
|
||||
/// <param name="useUnscaledTime">Whether to use unscaled time or not</param>
|
||||
private void StartBlinkingInternal(Color emissionColor, float blinksPerSec, float durationSeconds, int materialSlot, bool useUnscaledTime)
|
||||
{
|
||||
if (_renderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_useUnscaledTime = useUnscaledTime;
|
||||
_blinkStartTime = CurrentTime;
|
||||
_colorHighlight = emissionColor;
|
||||
_blinksPerSec = blinksPerSec;
|
||||
_durationSeconds = durationSeconds;
|
||||
_materialSlot = materialSlot;
|
||||
|
||||
Material[] materials = _renderer.materials;
|
||||
|
||||
for (int i = 0; i < materials.Length; i++)
|
||||
{
|
||||
if (i == _materialSlot || _materialSlot < 0)
|
||||
{
|
||||
materials[i].EnableKeyword(UxrConstants.Shaders.EmissionKeyword);
|
||||
}
|
||||
}
|
||||
|
||||
_renderer.materials = materials;
|
||||
IsBlinking = true;
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops blinking.
|
||||
/// </summary>
|
||||
private void StopBlinkingInternal()
|
||||
{
|
||||
if (_renderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsBlinking = false;
|
||||
|
||||
RestoreOriginalSharedMaterial();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the original (shared) material.
|
||||
/// </summary>
|
||||
private void RestoreOriginalSharedMaterial()
|
||||
{
|
||||
if (_renderer)
|
||||
{
|
||||
_renderer.sharedMaterials = _originalMaterials;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private float CurrentTime => _useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
|
||||
private bool IsInitialized { get; set; }
|
||||
|
||||
private float _blinkStartTime;
|
||||
private Material[] _originalMaterials;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
12
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrObjectBlink.cs.meta
vendored
Normal file
12
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrObjectBlink.cs.meta
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6cff78ce55139e48a3e08257187ea94
|
||||
timeCreated: 1504781678
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,43 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrObjectFade.ObjectEntry.MaterialEntry.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.GameObjects
|
||||
{
|
||||
public partial class UxrObjectFade
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
private partial class ObjectEntry
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores information of a material in a fade animation.
|
||||
/// </summary>
|
||||
private struct MaterialEntry
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the initial material color.
|
||||
/// </summary>
|
||||
public Color StartColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the shader transparency was enabled.
|
||||
/// </summary>
|
||||
public bool ShaderChanged { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e51e06febf0347f899cb77675622506d
|
||||
timeCreated: 1643744815
|
||||
123
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrObjectFade.ObjectEntry.cs
vendored
Normal file
123
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrObjectFade.ObjectEntry.cs
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrObjectFade.ObjectEntry.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Extensions.System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace UltimateXR.Animation.GameObjects
|
||||
{
|
||||
public partial class UxrObjectFade
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores information about an object in a fade animation.
|
||||
/// </summary>
|
||||
private partial class ObjectEntry
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Renderer component</param>
|
||||
public ObjectEntry(Renderer renderer)
|
||||
{
|
||||
Renderer = renderer;
|
||||
SharedMaterials = renderer.sharedMaterials;
|
||||
Materials = renderer.materials;
|
||||
MaterialEntries = new MaterialEntry[Materials.Length];
|
||||
|
||||
for (int i = 0; i < Materials.Length; ++i)
|
||||
{
|
||||
MaterialEntries[i].StartColor = Materials[i].color;
|
||||
MaterialEntries[i].ShaderChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Changes the material transparency.
|
||||
/// </summary>
|
||||
/// <param name="startQuantity">Start alpha</param>
|
||||
/// <param name="endQuantity">End alpha</param>
|
||||
/// <param name="fadeT">Interpolation factor [0.0, 1.0]</param>
|
||||
public void Fade(float startQuantity, float endQuantity, float fadeT)
|
||||
{
|
||||
for (int i = 0; i < Materials.Length; ++i)
|
||||
{
|
||||
if (!MaterialEntries[i].ShaderChanged)
|
||||
{
|
||||
ChangeStandardMaterialRenderMode(Materials[i]);
|
||||
MaterialEntries[i].ShaderChanged = true;
|
||||
}
|
||||
|
||||
Color color = MaterialEntries[i].StartColor;
|
||||
color.a *= Mathf.Lerp(startQuantity, endQuantity, fadeT);
|
||||
Materials[i].color = color;
|
||||
}
|
||||
|
||||
Renderer.materials = Materials;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the original material(s).
|
||||
/// </summary>
|
||||
public void Restore()
|
||||
{
|
||||
Renderer.sharedMaterials = SharedMaterials;
|
||||
MaterialEntries.ForEach(m => m.ShaderChanged = false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Enables transparency on a material.
|
||||
/// </summary>
|
||||
/// <param name="material">Material to enable transparency on</param>
|
||||
private void ChangeStandardMaterialRenderMode(Material material)
|
||||
{
|
||||
if (material.HasProperty(UxrConstants.Shaders.SurfaceModeVarName))
|
||||
{
|
||||
// Universal render pipeline
|
||||
material.SetInt(UxrConstants.Shaders.SurfaceModeVarName, UxrConstants.Shaders.SurfaceModeTransparent);
|
||||
material.SetInt(UxrConstants.Shaders.BlendModeVarName, UxrConstants.Shaders.BlendModeAlpha);
|
||||
material.renderQueue = (int)RenderQueue.Transparent;
|
||||
}
|
||||
else if (material.IsKeywordEnabled(UxrConstants.Shaders.AlphaBlendOnKeyword) == false)
|
||||
{
|
||||
// Built-in render pipeline
|
||||
material.SetInt(UxrConstants.Shaders.SrcBlendVarName, (int)BlendMode.SrcAlpha);
|
||||
material.SetInt(UxrConstants.Shaders.DstBlendVarName, (int)BlendMode.OneMinusSrcAlpha);
|
||||
material.SetInt(UxrConstants.Shaders.ZWriteVarName, 0);
|
||||
material.DisableKeyword(UxrConstants.Shaders.AlphaTestOnKeyword);
|
||||
material.EnableKeyword(UxrConstants.Shaders.AlphaBlendOnKeyword);
|
||||
material.DisableKeyword(UxrConstants.Shaders.AlphaPremultiplyOnKeyword);
|
||||
material.renderQueue = (int)RenderQueue.Transparent;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private MaterialEntry[] MaterialEntries { get; }
|
||||
private Renderer Renderer { get; }
|
||||
private Material[] Materials { get; }
|
||||
private Material[] SharedMaterials { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: edc5622f479147cd8ef96832732a9ff0
|
||||
timeCreated: 1644846685
|
||||
175
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrObjectFade.cs
vendored
Normal file
175
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrObjectFade.cs
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrObjectFade.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to fade an object out by making the material progressively more transparent.
|
||||
/// </summary>
|
||||
public partial class UxrObjectFade : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _recursively = true;
|
||||
[SerializeField] private float _delaySeconds;
|
||||
[SerializeField] private float _duration = 1.0f;
|
||||
[SerializeField] private float _startQuantity = 1.0f;
|
||||
[SerializeField] private float _endQuantity;
|
||||
[SerializeField] private bool _useUnscaledTime;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Starts a fade animation.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">GameObject whose material transparency will be enabled and animated.</param>
|
||||
/// <param name="startAlphaQuantity">Start alpha</param>
|
||||
/// <param name="endFadeQuantity">End alpha</param>
|
||||
/// <param name="delaySeconds">Seconds to wait before the animation starts</param>
|
||||
/// <param name="durationSeconds">Fade duration in seconds</param>
|
||||
/// <param name="recursively">Whether to also process all other child objects in the hierarchy</param>
|
||||
/// <param name="useUnscaledTime">
|
||||
/// Whether to use unscaled time (<see cref="Time.unscaledTime" />) or not (
|
||||
/// <see cref="Time.time" />)
|
||||
/// </param>
|
||||
/// <param name="finishedCallback">Optional callback executed when the animation finished</param>
|
||||
/// <returns>Animation component</returns>
|
||||
public static UxrObjectFade Fade(GameObject gameObject,
|
||||
float startAlphaQuantity,
|
||||
float endFadeQuantity,
|
||||
float delaySeconds,
|
||||
float durationSeconds,
|
||||
bool recursively = true,
|
||||
bool useUnscaledTime = false,
|
||||
Action finishedCallback = null)
|
||||
{
|
||||
UxrObjectFade objectFade = gameObject.GetOrAddComponent<UxrObjectFade>();
|
||||
|
||||
objectFade._startQuantity = startAlphaQuantity;
|
||||
objectFade._endQuantity = endFadeQuantity;
|
||||
objectFade._delaySeconds = delaySeconds;
|
||||
objectFade._duration = durationSeconds;
|
||||
objectFade._recursively = recursively;
|
||||
objectFade._useUnscaledTime = useUnscaledTime;
|
||||
objectFade._finishedCallback = finishedCallback;
|
||||
|
||||
objectFade.CheckInitialize(true);
|
||||
|
||||
return objectFade;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
CheckInitialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts or re-starts the animation.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_fadeStartTime = CurrentTime;
|
||||
_finished = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the animation and restores the material.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
foreach (ObjectEntry objectEntry in _objects)
|
||||
{
|
||||
objectEntry.Restore();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the animation.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (_finished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float fadeTime = CurrentTime - _fadeStartTime - _delaySeconds;
|
||||
|
||||
if (fadeTime <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float fadeT = Mathf.Clamp01(fadeTime / _duration);
|
||||
|
||||
foreach (ObjectEntry entry in _objects)
|
||||
{
|
||||
entry.Fade(_startQuantity, _endQuantity, fadeT);
|
||||
}
|
||||
|
||||
if (fadeTime > _duration)
|
||||
{
|
||||
_finishedCallback?.Invoke();
|
||||
_finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component if necessary.
|
||||
/// </summary>
|
||||
/// <param name="forceInitialize">Forces initializing the component even if it already may have been initialized</param>
|
||||
private void CheckInitialize(bool forceInitialize = false)
|
||||
{
|
||||
if (_objects.Count == 0 || forceInitialize)
|
||||
{
|
||||
Renderer[] objectRenderers = _recursively ? gameObject.GetComponentsInChildren<Renderer>() : new[] { gameObject.GetComponent<Renderer>() };
|
||||
|
||||
foreach (Renderer renderer in objectRenderers)
|
||||
{
|
||||
_objects.Add(new ObjectEntry(renderer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private float CurrentTime => _useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
|
||||
private readonly List<ObjectEntry> _objects = new List<ObjectEntry>();
|
||||
|
||||
private float _fadeStartTime;
|
||||
private bool _finished;
|
||||
private Action _finishedCallback;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
12
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrObjectFade.cs.meta
vendored
Normal file
12
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrObjectFade.cs.meta
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 832e898c6d705a84692abd8a162a48e0
|
||||
timeCreated: 1505585796
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
87
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrToggleObject.cs
vendored
Normal file
87
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrToggleObject.cs
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrToggleObject.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to toggle <see cref="GameObject" /> active state back and forth at random times.
|
||||
/// </summary>
|
||||
public class UxrToggleObject : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private GameObject _gameObject;
|
||||
[SerializeField] private float _enabledDurationMin;
|
||||
[SerializeField] private float _enabledDurationMax;
|
||||
[SerializeField] private float _disabledDurationMin;
|
||||
[SerializeField] private float _disabledDurationMax;
|
||||
[SerializeField] private bool _useUnscaledTime;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Called each time the component is enabled. Sets up the next toggle time.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_startTime = _useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
_nextToggleTime = GetNextRelativeToggleTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called on each update. Checks if it is time to toggle the GameObjects.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
float time = (_useUnscaledTime ? Time.unscaledTime : Time.time) - _startTime;
|
||||
|
||||
if (time > _nextToggleTime)
|
||||
{
|
||||
_gameObject.SetActive(!_gameObject.activeSelf);
|
||||
|
||||
_startTime = _useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
_nextToggleTime = GetNextRelativeToggleTime();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next time the objects will be toggled.
|
||||
/// </summary>
|
||||
/// <returns>Next toggle time in seconds relative to the current time</returns>
|
||||
private float GetNextRelativeToggleTime()
|
||||
{
|
||||
if (_gameObject && _gameObject.activeSelf)
|
||||
{
|
||||
return Random.Range(_enabledDurationMin, _enabledDurationMax);
|
||||
}
|
||||
if (_gameObject && !_gameObject.activeSelf)
|
||||
{
|
||||
return Random.Range(_disabledDurationMin, _disabledDurationMax);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private float _startTime;
|
||||
private float _nextToggleTime;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrToggleObject.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrToggleObject.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 161147edff113284a819edef7ac1d57c
|
||||
timeCreated: 1522850574
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
149
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrToggleObjectsUsingButtons.cs
vendored
Normal file
149
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/GameObjects/UxrToggleObjectsUsingButtons.cs
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrToggleObjectsUsingButtons.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Devices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to enable/disable GameObjects based on input from the VR controller buttons.
|
||||
/// </summary>
|
||||
public class UxrToggleObjectsUsingButtons : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private List<GameObject> _objectList;
|
||||
[SerializeField] private bool _startEnabled = true;
|
||||
[SerializeField] private UxrHandSide _controllerHand = UxrHandSide.Left;
|
||||
[SerializeField] private UxrInputButtons _buttonsEnable = UxrInputButtons.Button1;
|
||||
[SerializeField] private UxrButtonEventType _buttonsEventEnable = UxrButtonEventType.PressDown;
|
||||
[SerializeField] private UxrInputButtons _buttonsDisable = UxrInputButtons.Button1;
|
||||
[SerializeField] private UxrButtonEventType _buttonsEventsDisable = UxrButtonEventType.TouchDown;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object list to enable/disable.
|
||||
/// </summary>
|
||||
public List<GameObject> ObjectList
|
||||
{
|
||||
get => _objectList;
|
||||
set => _objectList = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets which controller hand is responsible for the input.
|
||||
/// </summary>
|
||||
public UxrHandSide ControllerHand
|
||||
{
|
||||
get => _controllerHand;
|
||||
set => _controllerHand = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the button(s) used to enable.
|
||||
/// </summary>
|
||||
public UxrInputButtons ButtonsToEnable
|
||||
{
|
||||
get => _buttonsEnable;
|
||||
set => _buttonsEnable = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the button event to enable.
|
||||
/// </summary>
|
||||
public UxrButtonEventType EnableButtonEvent
|
||||
{
|
||||
get => _buttonsEventEnable;
|
||||
set => _buttonsEventEnable = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the button(s) to disable.
|
||||
/// </summary>
|
||||
public UxrInputButtons ButtonsToDisable
|
||||
{
|
||||
get => _buttonsDisable;
|
||||
set => _buttonsDisable = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the button event to disable.
|
||||
/// </summary>
|
||||
public UxrButtonEventType DisableButtonEvent
|
||||
{
|
||||
get => _buttonsEventsDisable;
|
||||
set => _buttonsEventsDisable = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Called at the beginning. Sets the object initial state.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_state = _startEnabled;
|
||||
SetObjectsState(_startEnabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called each frame. Checks for VR controller button events and toggles states.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (!UxrAvatar.LocalAvatar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_state == false && UxrAvatar.LocalAvatarInput.GetButtonsEvent(ControllerHand, ButtonsToEnable, EnableButtonEvent))
|
||||
{
|
||||
SetObjectsState(true);
|
||||
}
|
||||
else if (_state && UxrAvatar.LocalAvatarInput.GetButtonsEvent(ControllerHand, ButtonsToDisable, DisableButtonEvent))
|
||||
{
|
||||
SetObjectsState(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sets the object state of the list of objects in this component.
|
||||
/// </summary>
|
||||
/// <param name="value">State they should be changed to</param>
|
||||
private void SetObjectsState(bool value)
|
||||
{
|
||||
_state = value;
|
||||
|
||||
foreach (GameObject obj in ObjectList)
|
||||
{
|
||||
obj.SetActive(value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private bool _state;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29eb44ed5f72f6d4f8341d2397b451a1
|
||||
timeCreated: 1522133718
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bad62e3862af46c8a36599131b4b2118
|
||||
timeCreated: 1643803875
|
||||
562
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmIKSolver.cs
vendored
Normal file
562
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmIKSolver.cs
vendored
Normal file
@@ -0,0 +1,562 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrArmIKSolver.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Avatar.Rig;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Math;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
/// <summary>
|
||||
/// IK component that implements basic Inverse Kinematics for an arm.
|
||||
/// </summary>
|
||||
public class UxrArmIKSolver : UxrIKSolver
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[Header("General")] [SerializeField] private UxrArmOverExtendMode _overExtendMode = UxrArmOverExtendMode.LimitHandReach;
|
||||
|
||||
[Header("Clavicle")] [SerializeField] [Range(0, 1)] private float _clavicleDeformation = DefaultClavicleDeformation;
|
||||
[SerializeField] private float _clavicleRangeOfMotionAngle = DefaultClavicleRangeOfMotionAngle;
|
||||
[SerializeField] private bool _clavicleAutoComputeBias = true;
|
||||
[SerializeField] private Vector3 _clavicleDeformationAxesBias = Vector3.zero;
|
||||
[SerializeField] private Vector3 _clavicleDeformationAxesScale = new Vector3(1.0f, 0.8f, 1.0f);
|
||||
|
||||
[Header("Arm (shoulder), forearm & hand")] [SerializeField] private float _armRangeOfMotionAngle = DefaultArmRangeOfMotionAngle;
|
||||
[SerializeField] [Range(0, 1)] private float _relaxedElbowAperture = DefaultElbowAperture;
|
||||
[SerializeField] [Range(0, 1)] private float _elbowApertureRotation = DefaultElbowApertureRotation;
|
||||
|
||||
[SerializeField] private bool _smooth = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
public const float DefaultClavicleDeformation = 0.4f;
|
||||
public const float DefaultClavicleRangeOfMotionAngle = 30.0f;
|
||||
public const float DefaultArmRangeOfMotionAngle = 100.0f;
|
||||
public const float DefaultElbowAperture = 0.5f;
|
||||
public const float DefaultElbowApertureRotation = 0.3f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the clavicle bone.
|
||||
/// </summary>
|
||||
public Transform Clavicle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the arm bone.
|
||||
/// </summary>
|
||||
public Transform Arm { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the forearm bone.
|
||||
/// </summary>
|
||||
public Transform Forearm { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hand bone.
|
||||
/// </summary>
|
||||
public Transform Hand { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether it is the left or right arm.
|
||||
/// </summary>
|
||||
public UxrHandSide Side => _side;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how far [0.0, 1.0] the elbow will from the body when solving the IK. Lower values will bring the elbow
|
||||
/// closer to the body.
|
||||
/// </summary>
|
||||
public float RelaxedElbowAperture
|
||||
{
|
||||
get => _relaxedElbowAperture;
|
||||
set => _relaxedElbowAperture = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets what happens when the real hand makes the VR arm to over-extend. This may happen if the user has a
|
||||
/// longer arm than the VR model, if the controller is placed far away or if the avatar is grabbing an object with
|
||||
/// constraints that lock the hand position.
|
||||
/// </summary>
|
||||
public UxrArmOverExtendMode OverExtendMode
|
||||
{
|
||||
get => _overExtendMode;
|
||||
set => _overExtendMode = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides UxrIKSolver
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Initialized => _initialized;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Solves a pass in the Inverse Kinematics.
|
||||
/// </summary>
|
||||
/// <param name="armSolveOptions">Arm solving options</param>
|
||||
/// <param name="armOverExtendMode">What happens when the hand moves farther than the actual arm length</param>
|
||||
public void SolveIKPass(UxrArmSolveOptions armSolveOptions, UxrArmOverExtendMode armOverExtendMode)
|
||||
{
|
||||
if (Hand == null || Forearm == null || Arm == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 localClaviclePos = ToLocalAvatarPos(Clavicle.position);
|
||||
Vector3 localForearmPos = ToLocalAvatarPos(Forearm.position);
|
||||
Vector3 localHandPos = ToLocalAvatarPos(Hand.position);
|
||||
|
||||
if (Clavicle != null)
|
||||
{
|
||||
if (armSolveOptions.HasFlag(UxrArmSolveOptions.ResetClavicle))
|
||||
{
|
||||
Clavicle.transform.localRotation = _clavicleUniversalLocalAxes.InitialLocalRotation;
|
||||
}
|
||||
|
||||
if (armSolveOptions.HasFlag(UxrArmSolveOptions.SolveClavicle))
|
||||
{
|
||||
// Compute the rotation to make the clavicle look at the elbow.
|
||||
// Computations are performed in local avatar space to allow avatars with pitch/roll and improve precision.
|
||||
|
||||
Vector3 avatarClavicleLookAt = (localForearmPos - localClaviclePos).normalized;
|
||||
avatarClavicleLookAt = Vector3.Scale(avatarClavicleLookAt, _clavicleDeformationAxesScale) + _clavicleDeformationAxesBias;
|
||||
|
||||
Quaternion avatarClavicleRotation = ToLocalAvatarRot(Clavicle.rotation);
|
||||
|
||||
Quaternion avatarClavicleRotationLookAt = Quaternion.Slerp(avatarClavicleRotation,
|
||||
Quaternion.LookRotation(avatarClavicleLookAt) * _clavicleUniversalLocalAxes.UniversalToActualAxesRotation,
|
||||
_clavicleDeformation);
|
||||
|
||||
float deformationAngle = Quaternion.Angle(avatarClavicleRotationLookAt, avatarClavicleRotation);
|
||||
|
||||
if (deformationAngle > _clavicleRangeOfMotionAngle)
|
||||
{
|
||||
avatarClavicleRotationLookAt = Quaternion.Slerp(avatarClavicleRotation, avatarClavicleRotationLookAt, _clavicleRangeOfMotionAngle / deformationAngle);
|
||||
}
|
||||
|
||||
// Smooth out:
|
||||
|
||||
float totalDegrees = Quaternion.Angle(_lastClavicleLocalRotation, avatarClavicleRotationLookAt);
|
||||
float degreesRot = ClavicleMaxDegreesPerSecond * Time.deltaTime;
|
||||
|
||||
if (_smooth == false)
|
||||
{
|
||||
_lastClavicleRotationInitialized = false;
|
||||
}
|
||||
|
||||
if (_lastClavicleRotationInitialized == false || totalDegrees < 0.001f)
|
||||
{
|
||||
Clavicle.rotation = ToWorldRot(avatarClavicleRotationLookAt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Clavicle.rotation = Quaternion.Slerp(ToWorldRot(_lastClavicleLocalRotation),
|
||||
ToWorldRot(avatarClavicleRotationLookAt),
|
||||
Mathf.Clamp01(degreesRot / totalDegrees));
|
||||
}
|
||||
}
|
||||
|
||||
Hand.position = ToWorldPos(localHandPos);
|
||||
}
|
||||
|
||||
// Find the plane of intersection between 2 spheres (sphere with "upper arm" radius and sphere with "forearm" radius).
|
||||
// Computations are performed in local avatar space to allow avatars with pitch/roll and improve precision.
|
||||
|
||||
localForearmPos = ToLocalAvatarPos(Forearm.position);
|
||||
Vector3 localArmPos = ToLocalAvatarPos(Arm.position);
|
||||
|
||||
float a = 2.0f * (localHandPos.x - localArmPos.x);
|
||||
float b = 2.0f * (localHandPos.y - localArmPos.y);
|
||||
float c = 2.0f * (localHandPos.z - localArmPos.z);
|
||||
float d = localArmPos.x * localArmPos.x - localHandPos.x * localHandPos.x + localArmPos.y * localArmPos.y - localHandPos.y * localHandPos.y +
|
||||
localArmPos.z * localArmPos.z - localHandPos.z * localHandPos.z - _upperArmLocalLength * _upperArmLocalLength + _forearmLocalLength * _forearmLocalLength;
|
||||
|
||||
// Find the center of the circle intersecting the 2 spheres. Check if the intersection exists (hand may be stretched over the limits)
|
||||
float t = (localArmPos.x * a + localArmPos.y * b + localArmPos.z * c + d) / (a * (localArmPos.x - localHandPos.x) + b * (localArmPos.y - localHandPos.y) + c * (localArmPos.z - localHandPos.z));
|
||||
|
||||
Vector3 localArmToCenter = (localHandPos - localArmPos) * t;
|
||||
Vector3 localCenter = localForearmPos;
|
||||
float safeDistance = 0.001f;
|
||||
float maxHandDistance = _upperArmLocalLength + _forearmLocalLength - safeDistance;
|
||||
float circleRadius = 0.0f;
|
||||
|
||||
if (localArmToCenter.magnitude + _forearmLocalLength > maxHandDistance)
|
||||
{
|
||||
// Too far from shoulder and arm is over-extending. Solve depending on selected mode, but some are applied at the end of this method.
|
||||
localArmToCenter = localArmToCenter.normalized * (_upperArmLocalLength - safeDistance * 0.5f);
|
||||
localCenter = localArmPos + localArmToCenter;
|
||||
|
||||
if (armOverExtendMode == UxrArmOverExtendMode.LimitHandReach)
|
||||
{
|
||||
// Clamp hand distance
|
||||
Hand.position = ToWorldPos(localArmPos + localArmToCenter.normalized * maxHandDistance);
|
||||
}
|
||||
|
||||
float angleRadians = Mathf.Acos((localCenter - localArmPos).magnitude / _upperArmLocalLength);
|
||||
circleRadius = Mathf.Sin(angleRadians) * _upperArmLocalLength;
|
||||
}
|
||||
else if (localArmToCenter.magnitude < 0.04f)
|
||||
{
|
||||
// Too close to shoulder: keep current elbow position.
|
||||
localArmToCenter = localForearmPos - localArmPos;
|
||||
localCenter = localForearmPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
localCenter = localArmPos + localArmToCenter;
|
||||
|
||||
// Find the circle radius
|
||||
float angleRadians = Mathf.Acos((localCenter - localArmPos).magnitude / _upperArmLocalLength);
|
||||
circleRadius = Mathf.Sin(angleRadians) * _upperArmLocalLength;
|
||||
}
|
||||
|
||||
Vector3 finalLocalHandPosition = ToLocalAvatarPos(Hand.position);
|
||||
Quaternion finalHandRotation = Hand.rotation;
|
||||
|
||||
// Compute the point inside this circle using the elbowAperture parameter.
|
||||
// Possible range is from bottom to exterior (far left or far right for left arm and right arm respectively).
|
||||
Vector3 planeNormal = -new Vector3(a, b, c);
|
||||
|
||||
Transform otherArm = _side == UxrHandSide.Left ? Avatar.AvatarRig.RightArm.UpperArm : Avatar.AvatarRig.LeftArm.UpperArm;
|
||||
Vector3 otherLocalArmPos = otherArm != null ? ToLocalAvatarPos(otherArm.position) : Vector3.zero;
|
||||
|
||||
if (otherArm == null)
|
||||
{
|
||||
otherLocalArmPos = ToLocalAvatarPos(Arm.position);
|
||||
otherLocalArmPos.x = -otherLocalArmPos.x;
|
||||
}
|
||||
|
||||
Quaternion rotToShoulder = Quaternion.LookRotation(Vector3.Cross((localArmPos - otherLocalArmPos) * (_side == UxrHandSide.Left ? -1.0f : 1.0f), Vector3.up).normalized, Vector3.up);
|
||||
Vector3 armToHand = (finalLocalHandPosition - localArmPos).normalized;
|
||||
Quaternion rotArmForward = rotToShoulder * Quaternion.LookRotation(Quaternion.Inverse(rotToShoulder) * localArmToCenter, Quaternion.Inverse(rotToShoulder) * armToHand);
|
||||
|
||||
Vector3 vectorFromCenterSide = Vector3.Cross(_side == UxrHandSide.Left ? rotArmForward * Vector3.up : rotArmForward * -Vector3.up, planeNormal);
|
||||
|
||||
if (otherArm != null)
|
||||
{
|
||||
bool isBack = Vector3.Cross(localArmPos - otherLocalArmPos, localCenter - localArmPos).y * (_side == UxrHandSide.Left ? -1.0f : 1.0f) > 0.0f;
|
||||
|
||||
/*
|
||||
* Do stuff with isBack
|
||||
*/
|
||||
}
|
||||
|
||||
// Compute elbow aperture value [0.0, 1.0] depending on the relaxedElbowAperture parameter and the current wrist torsion
|
||||
float wristDegrees = _side == UxrHandSide.Left ? -Avatar.AvatarRigInfo.GetArmInfo(UxrHandSide.Left).WristTorsionInfo.WristTorsionAngle : Avatar.AvatarRigInfo.GetArmInfo(UxrHandSide.Right).WristTorsionInfo.WristTorsionAngle;
|
||||
float elbowApertureBiasDueToWrist = wristDegrees / WristTorsionDegreesFactor * _elbowApertureRotation;
|
||||
float elbowAperture = Mathf.Clamp01(_relaxedElbowAperture + elbowApertureBiasDueToWrist);
|
||||
|
||||
_elbowAperture = _elbowAperture < 0.0f ? elbowAperture : Mathf.SmoothDampAngle(_elbowAperture, elbowAperture, ref _elbowApertureVelocity, ElbowApertureRotationSmoothTime);
|
||||
|
||||
// Now compute the elbow position using it
|
||||
Vector3 vectorFromCenterBottom = _side == UxrHandSide.Left ? Vector3.Cross(vectorFromCenterSide, planeNormal) : Vector3.Cross(planeNormal, vectorFromCenterSide);
|
||||
|
||||
Vector3 elbowPosition = localCenter + Vector3.Lerp(vectorFromCenterBottom, vectorFromCenterSide, _elbowAperture).normalized * circleRadius;
|
||||
|
||||
// Compute the desired rotation
|
||||
Vector3 armForward = (elbowPosition - localArmPos).normalized;
|
||||
|
||||
// Check range of motion of the arm
|
||||
if (Arm.parent != null)
|
||||
{
|
||||
Vector3 armNeutralForward = ToLocalAvatarDir(Arm.parent.TransformDirection(_armNeutralForwardInParent));
|
||||
|
||||
if (Vector3.Angle(armForward, armNeutralForward) > _armRangeOfMotionAngle)
|
||||
{
|
||||
armForward = Vector3.RotateTowards(armNeutralForward, armForward, _armRangeOfMotionAngle * Mathf.Deg2Rad, 0.0f);
|
||||
elbowPosition = localArmPos + armForward * _upperArmLocalLength;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the position and rotation of the rest
|
||||
Vector3 forearmForward = (ToLocalAvatarPos(Hand.position) - elbowPosition).normalized;
|
||||
float elbowAngle = Vector3.Angle(armForward, forearmForward);
|
||||
Vector3 elbowAxis = elbowAngle > ElbowMinAngleThreshold ? Vector3.Cross(forearmForward, armForward).normalized : Vector3.up;
|
||||
|
||||
elbowAxis = _side == UxrHandSide.Left ? -elbowAxis : elbowAxis;
|
||||
|
||||
Quaternion armRotationTarget = Quaternion.LookRotation(armForward, elbowAxis);
|
||||
Quaternion forearmRotationTarget = Quaternion.LookRotation(forearmForward, elbowAxis);
|
||||
|
||||
// Transform from top hierarchy to bottom to avoid jitter. Since we consider Z forward and Y the elbow rotation axis, we also
|
||||
// need to transform from this "universal" space to the actual axes the model uses.
|
||||
Arm.rotation = ToWorldRot(armRotationTarget * _armUniversalLocalAxes.UniversalToActualAxesRotation);
|
||||
|
||||
if (Vector3.Distance(finalLocalHandPosition, localArmPos) > maxHandDistance)
|
||||
{
|
||||
// Arm over extended: solve if the current mode is one of the remaining 2 to handle:
|
||||
if (armOverExtendMode == UxrArmOverExtendMode.ExtendUpperArm)
|
||||
{
|
||||
// Move the elbow away to reach the hand. This will stretch the arm.
|
||||
elbowPosition = finalLocalHandPosition - (finalLocalHandPosition - elbowPosition).normalized * _forearmLocalLength;
|
||||
}
|
||||
else if (armOverExtendMode == UxrArmOverExtendMode.ExtendArm)
|
||||
{
|
||||
// Stretch both the arm and forearm
|
||||
Vector3 elbowPosition2 = finalLocalHandPosition - (finalLocalHandPosition - elbowPosition).normalized * _forearmLocalLength;
|
||||
elbowPosition = (elbowPosition + elbowPosition2) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
Forearm.SetPositionAndRotation(ToWorldPos(elbowPosition), ToWorldRot(forearmRotationTarget * _forearmUniversalLocalAxes.UniversalToActualAxesRotation));
|
||||
Hand.SetPositionAndRotation(ToWorldPos(finalLocalHandPosition), finalHandRotation);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Computes internal IK parameters.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
ComputeParameters();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to events.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Stores the clavicle orientation to smooth it out the next frame.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
if (Clavicle != null)
|
||||
{
|
||||
_lastClavicleLocalRotation = Quaternion.Inverse(Avatar.transform.rotation) * Clavicle.rotation;
|
||||
_lastClavicleRotationInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrIKSolver
|
||||
|
||||
/// <summary>
|
||||
/// Solves the IK for the current frame.
|
||||
/// </summary>
|
||||
protected override void InternalSolveIK()
|
||||
{
|
||||
if (Avatar == null || Avatar.AvatarController == null || !Avatar.AvatarController.Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Clavicle != null)
|
||||
{
|
||||
// If we have a clavicle, perform another pass this time taking it into account.
|
||||
// The first pass won't clamp the hand distance because thanks to the clavicle rotation there is a little more reach.
|
||||
SolveIKPass(UxrArmSolveOptions.ResetClavicle, UxrArmOverExtendMode.ExtendForearm);
|
||||
SolveIKPass(UxrArmSolveOptions.ResetClavicle | UxrArmSolveOptions.SolveClavicle, _overExtendMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
SolveIKPass(UxrArmSolveOptions.None, _overExtendMode);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a point from world space to local avatar space.
|
||||
/// </summary>
|
||||
/// <param name="pos">World space position</param>
|
||||
/// <returns>Avatar space position</returns>
|
||||
private Vector3 ToLocalAvatarPos(Vector3 pos)
|
||||
{
|
||||
return Avatar.transform.InverseTransformPoint(pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a point from local avatar space to world space.
|
||||
/// </summary>
|
||||
/// <param name="pos">Avatar space position</param>
|
||||
/// <returns>World space position</returns>
|
||||
private Vector3 ToWorldPos(Vector3 pos)
|
||||
{
|
||||
return Avatar.transform.TransformPoint(pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a direction from world space to local avatar space.
|
||||
/// </summary>
|
||||
/// <param name="dir">World space direction</param>
|
||||
/// <returns>Avatar space direction</returns>
|
||||
private Vector3 ToLocalAvatarDir(Vector3 dir)
|
||||
{
|
||||
return Avatar.transform.InverseTransformDirection(dir);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a rotation from world space to local avatar space.
|
||||
/// </summary>
|
||||
/// <param name="rot">World space rotation</param>
|
||||
/// <returns>Avatar space rotation</returns>
|
||||
private Quaternion ToLocalAvatarRot(Quaternion rot)
|
||||
{
|
||||
return Quaternion.Inverse(Avatar.transform.rotation) * rot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a rotation from local avatar space to world space.
|
||||
/// </summary>
|
||||
/// <param name="rot">Avatar space rotation</param>
|
||||
/// <returns>World space rotation</returns>
|
||||
private Quaternion ToWorldRot(Quaternion rot)
|
||||
{
|
||||
return Avatar.transform.rotation * rot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the internal parameters for the IK.
|
||||
/// </summary>
|
||||
private void ComputeParameters()
|
||||
{
|
||||
if (Avatar == null)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.AnimationModule} {nameof(UxrArmIKSolver)} can't find {nameof(UxrAvatar)} component upwards in the hierarchy. Component is located in {this.GetPathUnderScene()}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_side = transform.HasParent(Avatar.AvatarRig.LeftArm.Clavicle ?? Avatar.AvatarRig.LeftArm.UpperArm) ? UxrHandSide.Left : UxrHandSide.Right;
|
||||
|
||||
// Set up references
|
||||
if (Clavicle == null)
|
||||
{
|
||||
Clavicle = _side == UxrHandSide.Left ? Avatar.AvatarRig.LeftArm.Clavicle : Avatar.AvatarRig.RightArm.Clavicle;
|
||||
}
|
||||
|
||||
if (Arm == null)
|
||||
{
|
||||
Arm = _side == UxrHandSide.Left ? Avatar.AvatarRig.LeftArm.UpperArm : Avatar.AvatarRig.RightArm.UpperArm;
|
||||
}
|
||||
|
||||
if (Forearm == null)
|
||||
{
|
||||
Forearm = _side == UxrHandSide.Left ? Avatar.AvatarRig.LeftArm.Forearm : Avatar.AvatarRig.RightArm.Forearm;
|
||||
}
|
||||
|
||||
if (Hand == null)
|
||||
{
|
||||
Hand = Avatar.GetHandBone(_side);
|
||||
}
|
||||
|
||||
UxrAvatarArm arm = Avatar.GetArm(_side);
|
||||
|
||||
if (arm != null && arm.UpperArm && arm.Forearm && arm.Hand.Wrist)
|
||||
{
|
||||
// Compute lengths in local avatar coordinates in case avatar has scaling.
|
||||
// We use special treatment for the local hand in case that the component is being added at runtime and the hand is driven by a multiplayer NetworkTransform component that already moved it.
|
||||
|
||||
Vector3 localUpperArm = ToLocalAvatarPos(arm.UpperArm.position);
|
||||
Vector3 localForearm = ToLocalAvatarPos(arm.Forearm.position);
|
||||
Vector3 localHand = ToLocalAvatarPos(arm.Hand.Wrist.transform.parent.TransformPoint(Avatar.AvatarRigInfo.GetArmInfo(_side).HandUniversalLocalAxes.InitialLocalPosition));
|
||||
|
||||
_upperArmLocalLength = Vector3.Distance(localUpperArm, localForearm);
|
||||
_forearmLocalLength = Vector3.Distance(localForearm, localHand);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.AnimationModule} {nameof(UxrArmIKSolver)} can't find one or more of the following bones: upper arm, forearm, wrist. Component is located in {this.GetPathUnderScene()}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_clavicleUniversalLocalAxes = Avatar.AvatarRigInfo.GetArmInfo(_side).ClavicleUniversalLocalAxes;
|
||||
_armUniversalLocalAxes = Avatar.AvatarRigInfo.GetArmInfo(_side).ArmUniversalLocalAxes;
|
||||
_forearmUniversalLocalAxes = Avatar.AvatarRigInfo.GetArmInfo(_side).ForearmUniversalLocalAxes;
|
||||
|
||||
// Compute arm range of motion neutral direction
|
||||
_armNeutralForwardInParent = Vector3.forward;
|
||||
_armNeutralForwardInParent = Quaternion.AngleAxis(30.0f * (_side == UxrHandSide.Left ? -1.0f : 1.0f), Vector3.up) * _armNeutralForwardInParent;
|
||||
_armNeutralForwardInParent = Quaternion.AngleAxis(30.0f, Vector3.right) * _armNeutralForwardInParent;
|
||||
|
||||
if (Arm.parent != null)
|
||||
{
|
||||
_armNeutralForwardInParent = Arm.parent.InverseTransformDirection(Avatar.transform.TransformDirection(_armNeutralForwardInParent));
|
||||
}
|
||||
|
||||
if (Clavicle && Avatar)
|
||||
{
|
||||
// If we have a clavicle, set it up too
|
||||
if (_clavicleAutoComputeBias)
|
||||
{
|
||||
Vector3 clavicleLookAt = (Forearm.position - Clavicle.position).normalized;
|
||||
Avatar.transform.InverseTransformDirection(clavicleLookAt);
|
||||
|
||||
_clavicleDeformationAxesBias = new Vector3(0.0f, -clavicleLookAt.y + 0.25f, -clavicleLookAt.z);
|
||||
}
|
||||
}
|
||||
|
||||
_elbowAperture = -1.0f;
|
||||
_elbowApertureVelocity = 0.0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private const float ClavicleMaxDegreesPerSecond = 360.0f;
|
||||
private const float WristTorsionDegreesFactor = 150.0f;
|
||||
private const float ElbowApertureRotationSmoothTime = 0.1f;
|
||||
private const float ElbowMinAngleThreshold = 3.0f;
|
||||
|
||||
private UxrHandSide _side;
|
||||
private bool _initialized;
|
||||
private UxrUniversalLocalAxes _clavicleUniversalLocalAxes;
|
||||
private UxrUniversalLocalAxes _armUniversalLocalAxes;
|
||||
private UxrUniversalLocalAxes _forearmUniversalLocalAxes;
|
||||
private float _upperArmLocalLength;
|
||||
private float _forearmLocalLength;
|
||||
private float _elbowAperture = -1.0f;
|
||||
private float _elbowApertureVelocity;
|
||||
private Vector3 _armNeutralForwardInParent = Vector3.zero;
|
||||
private Quaternion _lastClavicleLocalRotation = Quaternion.identity;
|
||||
private bool _lastClavicleRotationInitialized;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
12
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmIKSolver.cs.meta
vendored
Normal file
12
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmIKSolver.cs.meta
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61a25f8a356cfe1489a26b996a2d81de
|
||||
timeCreated: 1510133937
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
34
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmOverExtendMode.cs
vendored
Normal file
34
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmOverExtendMode.cs
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrArmOverExtendMode.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different solutions that can be used when an avatar with visible arms moves a hand farther than the
|
||||
/// actual length of the arm.
|
||||
/// </summary>
|
||||
public enum UxrArmOverExtendMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Hand reach will be limited to what the actual arm length permits.
|
||||
/// </summary>
|
||||
LimitHandReach,
|
||||
|
||||
/// <summary>
|
||||
/// Stretch the forearm.
|
||||
/// </summary>
|
||||
ExtendForearm,
|
||||
|
||||
/// <summary>
|
||||
/// Stretch the upper arm.
|
||||
/// </summary>
|
||||
ExtendUpperArm,
|
||||
|
||||
/// <summary>
|
||||
/// Stretch both the upper arm and forearm.
|
||||
/// </summary>
|
||||
ExtendArm
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmOverExtendMode.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmOverExtendMode.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d2ff8c20c3e488b83833ab43415e40a
|
||||
timeCreated: 1643116515
|
||||
33
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmSolveOptions.cs
vendored
Normal file
33
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmSolveOptions.cs
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrArmSolveOptions.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
/// <summary>
|
||||
/// Different clavicle options supported by <see cref="UxrArmIKSolver.SolveIKPass" /> when clavicle data is present in
|
||||
/// the rig.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum UxrArmSolveOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// No options.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Reset the clavicle position.
|
||||
/// </summary>
|
||||
ResetClavicle = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Solve the clavicle position. Can be used together with <see cref="ResetClavicle" /> so that the clavicle is solved
|
||||
/// without using the current position data.
|
||||
/// </summary>
|
||||
SolveClavicle = 1 << 1
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmSolveOptions.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrArmSolveOptions.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9663654228ad49fcb30c5657e7c2ec76
|
||||
timeCreated: 1645360837
|
||||
59
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrBodyIK.IndependentBoneInfo.cs
vendored
Normal file
59
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrBodyIK.IndependentBoneInfo.cs
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrBodyIK.IndependentBoneInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
public sealed partial class UxrBodyIK
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Independent bones are bones that are driven externally and not using IK, such as the hands (wrist bones), which are
|
||||
/// driven by the tracked input controllers. They need to be kept track of when parent bones are modified by Inverse
|
||||
/// Kinematics to make sure that they are kept in the same place afterwards. Otherwise due to parenting the position
|
||||
/// they should have would change.
|
||||
/// </summary>
|
||||
private class IndependentBoneInfo
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bone transform.
|
||||
/// </summary>
|
||||
public Transform Transform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the correct current position.
|
||||
/// </summary>
|
||||
public Vector3 Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the correct current position.
|
||||
/// </summary>
|
||||
public Quaternion Rotation { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="transform">Bone transform</param>
|
||||
public IndependentBoneInfo(Transform transform)
|
||||
{
|
||||
Transform = transform;
|
||||
Position = transform.position;
|
||||
Rotation = transform.rotation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67c356fd5432455fa75bee27571d5a2a
|
||||
timeCreated: 1643734039
|
||||
592
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrBodyIK.cs
vendored
Normal file
592
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrBodyIK.cs
vendored
Normal file
@@ -0,0 +1,592 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrBodyIK.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Avatar.Rig;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Math;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that provides functionality to compute Inverse Kinematics for a humanoid body.
|
||||
/// </summary>
|
||||
public sealed partial class UxrBodyIK
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the object was initialized.
|
||||
/// </summary>
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the IK object.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar that the IK will be computed for</param>
|
||||
/// <param name="settings">IK settings to use</param>
|
||||
/// <param name="usesExternalArmIK">Whether the avatar uses an arm IK</param>
|
||||
/// <param name="usesExternalLegIK">Whether the avatar uses leg IK</param>
|
||||
public void Initialize(UxrAvatar avatar, UxrBodyIKSettings settings, bool usesExternalArmIK, bool usesExternalLegIK)
|
||||
{
|
||||
Initialized = true;
|
||||
_avatar = avatar;
|
||||
_avatarTransform = avatar.transform;
|
||||
_settings = settings;
|
||||
|
||||
// Get body root
|
||||
|
||||
if (avatar.AvatarRig.Head.Head == null)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelAvatar >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.AvatarModule} Avatar {avatar.name} has no head setup in the {nameof(UxrAvatar)}'s Rig field");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_independentBones = new List<IndependentBoneInfo>();
|
||||
|
||||
List<Transform> bodyTransforms = new List<Transform>();
|
||||
|
||||
if (avatar.AvatarRig.UpperChest)
|
||||
{
|
||||
bodyTransforms.Add(avatar.AvatarRig.UpperChest);
|
||||
_upperChestUniversalLocalAxes = new UxrUniversalLocalAxes(avatar.AvatarRig.UpperChest, _avatarTransform);
|
||||
}
|
||||
|
||||
if (avatar.AvatarRig.Chest)
|
||||
{
|
||||
bodyTransforms.Add(avatar.AvatarRig.Chest);
|
||||
_chestUniversalLocalAxes = new UxrUniversalLocalAxes(avatar.AvatarRig.Chest, _avatarTransform);
|
||||
}
|
||||
|
||||
if (avatar.AvatarRig.Spine)
|
||||
{
|
||||
bodyTransforms.Add(avatar.AvatarRig.Spine);
|
||||
_spineUniversalLocalAxes = new UxrUniversalLocalAxes(avatar.AvatarRig.Spine, _avatarTransform);
|
||||
}
|
||||
|
||||
if (avatar.AvatarRig.Hips)
|
||||
{
|
||||
bodyTransforms.Add(avatar.AvatarRig.Hips);
|
||||
}
|
||||
|
||||
if (avatar.AvatarRig.Head.Neck && avatar.AvatarRig.Head.Neck != null && avatar.AvatarRig.Head.Neck != _avatarTransform && !bodyTransforms.Contains(avatar.AvatarRig.Head.Neck.parent))
|
||||
{
|
||||
bodyTransforms.Add(avatar.AvatarRig.Head.Neck.parent);
|
||||
}
|
||||
|
||||
if (avatar.AvatarRig.UpperChest && avatar.AvatarRig.UpperChest.parent != null && avatar.AvatarRig.UpperChest.parent != _avatarTransform && !bodyTransforms.Contains(avatar.AvatarRig.UpperChest.parent))
|
||||
{
|
||||
bodyTransforms.Add(avatar.AvatarRig.UpperChest.parent);
|
||||
}
|
||||
|
||||
if (avatar.AvatarRig.Chest && avatar.AvatarRig.Chest.parent != null && avatar.AvatarRig.Chest.parent != _avatarTransform && !bodyTransforms.Contains(avatar.AvatarRig.Chest.parent))
|
||||
{
|
||||
bodyTransforms.Add(avatar.AvatarRig.Chest.parent);
|
||||
}
|
||||
|
||||
if (avatar.AvatarRig.Spine && avatar.AvatarRig.Spine.parent != null && avatar.AvatarRig.Spine.parent != _avatarTransform && !bodyTransforms.Contains(avatar.AvatarRig.Spine.parent))
|
||||
{
|
||||
bodyTransforms.Add(avatar.AvatarRig.Spine.parent);
|
||||
}
|
||||
|
||||
if (avatar.AvatarRig.Hips && avatar.AvatarRig.Hips.parent != null && avatar.AvatarRig.Hips.parent != _avatarTransform && !bodyTransforms.Contains(avatar.AvatarRig.Hips.parent))
|
||||
{
|
||||
bodyTransforms.Add(avatar.AvatarRig.Hips.parent);
|
||||
}
|
||||
|
||||
_avatarBodyRoot = TransformExt.GetCommonRootTransformFromSet(bodyTransforms.ToArray());
|
||||
|
||||
if (_avatarBodyRoot == null)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelAvatar >= UxrLogLevel.Warnings)
|
||||
{
|
||||
Debug.LogWarning($"{UxrConstants.AvatarModule} No common avatar body root found. If there is an avatar body it will not follow the head position.");
|
||||
}
|
||||
|
||||
_avatarBodyRoot = new GameObject("Dummy Root").transform;
|
||||
_avatarBodyRoot.SetParent(_avatarTransform);
|
||||
_avatarBodyRoot.SetPositionAndRotation(_avatarTransform.position, _avatarTransform.rotation);
|
||||
}
|
||||
|
||||
// Neck
|
||||
|
||||
_avatarNeck = avatar.AvatarRig.Head.Neck;
|
||||
|
||||
if (_avatarNeck == null)
|
||||
{
|
||||
_avatarNeck = new GameObject("Dummy Neck").transform;
|
||||
_avatarNeck.SetParent(_avatarBodyRoot);
|
||||
_avatarNeck.SetPositionAndRotation(_avatarTransform.position + _avatarTransform.up * _settings.NeckBaseHeight + _avatarTransform.forward * _settings.NeckForwardOffset, _avatarTransform.rotation);
|
||||
|
||||
if (avatar.AvatarRig.Head.Head != null)
|
||||
{
|
||||
avatar.AvatarRig.Head.Head.SetParent(_avatarNeck);
|
||||
}
|
||||
}
|
||||
|
||||
_neckUniversalLocalAxes = new UxrUniversalLocalAxes(_avatarNeck, _avatarTransform);
|
||||
|
||||
// Head
|
||||
|
||||
_headUniversalLocalAxes = new UxrUniversalLocalAxes(avatar.AvatarRig.Head.Head, _avatarTransform);
|
||||
_avatarHead = avatar.AvatarRig.Head.Head;
|
||||
|
||||
// Eyes
|
||||
|
||||
_avatarEyes = new GameObject("Dummy Eyes").transform;
|
||||
_avatarEyes.SetParent(_avatarHead);
|
||||
_avatarEyes.SetPositionAndRotation(_avatarTransform.position + _avatarTransform.up * _settings.EyesBaseHeight + _avatarTransform.forward * _settings.EyesForwardOffset, _avatarTransform.rotation);
|
||||
|
||||
// Avatar Forward
|
||||
|
||||
_avatarForward = new GameObject("Dummy Forward").transform;
|
||||
_avatarForward.SetParent(_avatarTransform);
|
||||
_avatarForward.SetPositionAndRotation(_avatarHead.position - avatar.transform.up * _avatarHead.position.y, _avatarTransform.rotation);
|
||||
|
||||
_avatarBodyRoot.SetParent(_avatarForward);
|
||||
|
||||
// Initialize
|
||||
|
||||
_neckPosRelativeToEyes = Quaternion.Inverse(_avatarEyes.rotation) * (_avatarNeck.position - _avatarEyes.position);
|
||||
_neckRotRelativeToEyes = Quaternion.Inverse(_avatarEyes.rotation) * _avatarNeck.rotation;
|
||||
_avatarForwardPosRelativeToNeck = _avatarForward.position - _avatarNeck.transform.position;
|
||||
_avatarForwardTarget = _avatarForward.forward;
|
||||
_straightSpineForward = _avatarForward.forward;
|
||||
|
||||
if (usesExternalArmIK)
|
||||
{
|
||||
if (avatar.LeftHandBone)
|
||||
{
|
||||
_independentBones.Add(new IndependentBoneInfo(avatar.LeftHandBone));
|
||||
}
|
||||
|
||||
if (avatar.RightHandBone)
|
||||
{
|
||||
_independentBones.Add(new IndependentBoneInfo(avatar.RightHandBone));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the Pre-pass in the IK solving.
|
||||
/// </summary>
|
||||
public void PreSolveAvatarIK()
|
||||
{
|
||||
if (_avatarHead == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Push transforms. These transforms hang from the body and need to be kept in place because
|
||||
// due to parenting their absolute position/orientation will be altered
|
||||
|
||||
foreach (IndependentBoneInfo boneInfo in _independentBones)
|
||||
{
|
||||
boneInfo.Rotation = boneInfo.Transform.rotation;
|
||||
boneInfo.Position = boneInfo.Transform.position;
|
||||
}
|
||||
|
||||
// Reset upper body
|
||||
|
||||
_avatarNeck.localPosition = _neckUniversalLocalAxes.InitialLocalPosition;
|
||||
_avatarHead.localPosition = _headUniversalLocalAxes.InitialLocalPosition;
|
||||
_avatarNeck.localRotation = _neckUniversalLocalAxes.InitialLocalRotation;
|
||||
_avatarHead.localRotation = _headUniversalLocalAxes.InitialLocalRotation;
|
||||
|
||||
if (_avatar.AvatarRig.UpperChest)
|
||||
{
|
||||
_avatar.AvatarRig.UpperChest.localRotation = _upperChestUniversalLocalAxes.InitialLocalRotation;
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.Chest)
|
||||
{
|
||||
_avatar.AvatarRig.Chest.localRotation = _chestUniversalLocalAxes.InitialLocalRotation;
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.Spine)
|
||||
{
|
||||
_avatar.AvatarRig.Spine.localRotation = _spineUniversalLocalAxes.InitialLocalRotation;
|
||||
}
|
||||
|
||||
// Compute neck position/rotation to make the avatar eyes match the camera
|
||||
|
||||
Transform cameraTransform = _avatar.CameraComponent.transform;
|
||||
Vector3 localAvatarPivotPos = _avatar.transform.InverseTransformPoint(_avatarForward.position);
|
||||
Vector3 neckPosition = GetWorldPosFromOffset(cameraTransform, cameraTransform, _neckPosRelativeToEyes);
|
||||
Quaternion neckRotation = cameraTransform.rotation * _neckRotRelativeToEyes;
|
||||
|
||||
_avatarNeck.SetPositionAndRotation(neckPosition, neckRotation);
|
||||
|
||||
// Update avatar pivot
|
||||
|
||||
_avatarForward.position = GetWorldPosFromOffset(_avatarForward, _avatarNeck, _avatarForwardPosRelativeToNeck);
|
||||
bool smoothForwardRotation = true;
|
||||
|
||||
if (Vector3.Angle(cameraTransform.forward, _avatar.transform.up) > CameraUpsideDownAngleThreshold && Vector3.Angle(cameraTransform.forward, -_avatar.transform.up) > CameraUpsideDownAngleThreshold)
|
||||
{
|
||||
// _straightSpineForward contains the forward direction where the avatar looks (vector.y is 0).
|
||||
// This is different from _avatarForward.forward because avatarForward allows the head to rotate
|
||||
// some degrees without rotating the whole body along with it.
|
||||
_straightSpineForward = Vector3.ProjectOnPlane(cameraTransform.forward, _avatar.transform.up);
|
||||
|
||||
if (Vector3.Angle(cameraTransform.forward, _avatarForward.forward) > 90.0f)
|
||||
{
|
||||
// Bad orientation, fix instantly.
|
||||
smoothForwardRotation = false;
|
||||
}
|
||||
}
|
||||
|
||||
float bodyRotationAngle = Vector3.Angle(_straightSpineForward, _avatarForward.forward);
|
||||
if (bodyRotationAngle > _settings.HeadFreeRangeTorsion)
|
||||
{
|
||||
float radians = (bodyRotationAngle - _settings.HeadFreeRangeTorsion) * Mathf.Deg2Rad;
|
||||
_avatarForwardTarget = Vector3.RotateTowards(_avatarForwardTarget, _straightSpineForward, radians, 0.0f);
|
||||
}
|
||||
|
||||
// Update avatar forward direction
|
||||
|
||||
float rotationSpeedMultiplier = Vector3.Angle(_avatarForward.forward, _avatarForwardTarget) / 30.0f;
|
||||
float maxForwardDegreesDelta = AvatarRotationDegreesPerSecond * rotationSpeedMultiplier * _settings.BodyPivotRotationSpeed * Time.deltaTime;
|
||||
|
||||
_avatarForward.rotation = Quaternion.RotateTowards(Quaternion.LookRotation(_avatarForward.forward, _avatar.transform.up),
|
||||
Quaternion.LookRotation(_avatarForwardTarget, _avatar.transform.up),
|
||||
smoothForwardRotation ? maxForwardDegreesDelta : 180.0f);
|
||||
|
||||
// Since the avatar pivot is parent of all body nodes, move the neck back to its position
|
||||
_avatarNeck.SetPositionAndRotation(neckPosition, neckRotation);
|
||||
|
||||
if (_settings.LockBodyPivot)
|
||||
{
|
||||
_avatarForward.position = _avatar.transform.TransformPoint(localAvatarPivotPos);
|
||||
}
|
||||
|
||||
// We've computed the avatar head orientation using only the neck. Now redistribute it using the _neckHeadBalance parameter so that
|
||||
// we can get a smoother head rotation by using both the neck and the head.
|
||||
|
||||
if (_settings.NeckHeadBalance > 0.0f)
|
||||
{
|
||||
Quaternion neckUniversalRotation = Quaternion.Inverse(_avatarForward.rotation) * _neckUniversalLocalAxes.UniversalRotation * Quaternion.Inverse(_neckUniversalLocalAxes.InitialUniversalLocalReferenceRotation);
|
||||
|
||||
Vector3 headPosition = _avatarHead.position;
|
||||
Quaternion headRotation = _avatarHead.rotation;
|
||||
|
||||
// Compute partial neck rotation
|
||||
neckRotation = _avatarForward.rotation * (Quaternion.Slerp(Quaternion.identity, neckUniversalRotation, 1.0f - _settings.NeckHeadBalance) *
|
||||
_neckUniversalLocalAxes.InitialUniversalLocalReferenceRotation *
|
||||
_neckUniversalLocalAxes.UniversalToActualAxesRotation);
|
||||
|
||||
_avatarNeck.rotation = neckRotation;
|
||||
_avatarHead.rotation = headRotation;
|
||||
|
||||
_avatarForward.position -= _avatarHead.position - headPosition;
|
||||
|
||||
neckPosition = _avatarNeck.position;
|
||||
}
|
||||
|
||||
// Check influence of head rotation on chest/spine transforms
|
||||
|
||||
if (!_settings.LockBodyPivot)
|
||||
{
|
||||
// Compute head rotation without the rotation around the Y axis:
|
||||
Quaternion headPropagateRotation = Quaternion.Inverse(Quaternion.LookRotation(_straightSpineForward, _avatar.transform.up)) * _headUniversalLocalAxes.UniversalRotation;
|
||||
|
||||
// Remove the rotation that the head can do without propagation to chest/spine:
|
||||
headPropagateRotation = Quaternion.RotateTowards(headPropagateRotation, Quaternion.identity, _settings.HeadFreeRangeBend);
|
||||
|
||||
// Compute influence on chest/spine elements:
|
||||
|
||||
float totalWeight = 0.0f;
|
||||
|
||||
if (_avatar.AvatarRig.UpperChest)
|
||||
{
|
||||
totalWeight += _settings.UpperChestBend;
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.Chest)
|
||||
{
|
||||
totalWeight += _settings.ChestBend;
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.Spine)
|
||||
{
|
||||
totalWeight += _settings.SpineBend;
|
||||
}
|
||||
|
||||
// Propagate head rotation:
|
||||
|
||||
if (totalWeight > 0.0f)
|
||||
{
|
||||
if (totalWeight < 1.0f)
|
||||
{
|
||||
totalWeight = 1.0f;
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.UpperChest)
|
||||
{
|
||||
_avatar.AvatarRig.UpperChest.rotation = _avatarForward.rotation *
|
||||
(Quaternion.Slerp(Quaternion.identity, headPropagateRotation, _settings.UpperChestBend / totalWeight) *
|
||||
_upperChestUniversalLocalAxes.InitialUniversalLocalReferenceRotation * _upperChestUniversalLocalAxes.UniversalToActualAxesRotation);
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.Chest)
|
||||
{
|
||||
_avatar.AvatarRig.Chest.rotation = _avatarForward.rotation *
|
||||
(Quaternion.Slerp(Quaternion.identity, headPropagateRotation, _settings.ChestBend / totalWeight) *
|
||||
_chestUniversalLocalAxes.InitialUniversalLocalReferenceRotation * _chestUniversalLocalAxes.UniversalToActualAxesRotation);
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.Spine)
|
||||
{
|
||||
_avatar.AvatarRig.Spine.rotation = _avatarForward.rotation *
|
||||
(Quaternion.Slerp(Quaternion.identity, headPropagateRotation, _settings.SpineBend / totalWeight) *
|
||||
_spineUniversalLocalAxes.InitialUniversalLocalReferenceRotation * _spineUniversalLocalAxes.UniversalToActualAxesRotation);
|
||||
}
|
||||
}
|
||||
|
||||
// Make whole avatar move back so that head remains with the same position/orientation
|
||||
|
||||
_avatarNeck.rotation = neckRotation;
|
||||
_avatarForward.position -= _avatarNeck.position - neckPosition;
|
||||
_avatarNeck.position = neckPosition;
|
||||
}
|
||||
|
||||
// If the avatar moves, straighten the forward direction
|
||||
|
||||
float avatarMovedDistance = Vector3.Distance(localAvatarPivotPos, _avatarForward.position);
|
||||
|
||||
if (avatarMovedDistance / Time.deltaTime > AvatarStraighteningMinSpeed)
|
||||
{
|
||||
float degreesToStraighten = avatarMovedDistance * DegreesStraightenedPerMeterMoved;
|
||||
_avatarForwardTarget = Vector3.RotateTowards(_avatarForwardTarget, _straightSpineForward, degreesToStraighten * Mathf.Deg2Rad, 0.0f);
|
||||
}
|
||||
|
||||
// Pop independent transforms (usually hands and other transforms with their own sensors)
|
||||
|
||||
foreach (IndependentBoneInfo boneInfo in _independentBones)
|
||||
{
|
||||
boneInfo.Transform.SetPositionAndRotation(boneInfo.Position, boneInfo.Rotation);
|
||||
}
|
||||
|
||||
if (_settings.LockBodyPivot)
|
||||
{
|
||||
_avatarForward.position = _avatar.transform.TransformPoint(localAvatarPivotPos);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the post-pass in the IK solving.
|
||||
/// </summary>
|
||||
public void PostSolveAvatarIK()
|
||||
{
|
||||
if (_avatarHead == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Push transforms. These transforms hang from the body and need to be kept in place because
|
||||
// due to parenting their absolute position/orientation will be altered
|
||||
|
||||
foreach (IndependentBoneInfo boneInfo in _independentBones)
|
||||
{
|
||||
boneInfo.Rotation = boneInfo.Transform.rotation;
|
||||
boneInfo.Position = boneInfo.Transform.position;
|
||||
}
|
||||
|
||||
Vector3 neckPosition = _avatarNeck.position;
|
||||
Quaternion neckRotation = _avatarNeck.rotation;
|
||||
|
||||
// Compute torso rotation
|
||||
|
||||
float torsoRotation = 0.0f; // No degrees, just an abstract quantity we use later to multiply by a factor and get degrees
|
||||
float totalWeight = 0.0f;
|
||||
|
||||
if (_avatar.AvatarRig.UpperChest)
|
||||
{
|
||||
torsoRotation = GetArmTorsoRotationWeight(_avatar.AvatarRig.UpperChest, _upperChestUniversalLocalAxes, _avatar.AvatarRig.LeftArm, UxrHandSide.Left) +
|
||||
GetArmTorsoRotationWeight(_avatar.AvatarRig.UpperChest, _upperChestUniversalLocalAxes, _avatar.AvatarRig.RightArm, UxrHandSide.Right);
|
||||
|
||||
totalWeight += _settings.UpperChestTorsion;
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.Chest)
|
||||
{
|
||||
torsoRotation = GetArmTorsoRotationWeight(_avatar.AvatarRig.Chest, _chestUniversalLocalAxes, _avatar.AvatarRig.LeftArm, UxrHandSide.Left) +
|
||||
GetArmTorsoRotationWeight(_avatar.AvatarRig.Chest, _chestUniversalLocalAxes, _avatar.AvatarRig.RightArm, UxrHandSide.Right);
|
||||
|
||||
totalWeight += _settings.ChestTorsion;
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.Spine)
|
||||
{
|
||||
torsoRotation = GetArmTorsoRotationWeight(_avatar.AvatarRig.Spine, _spineUniversalLocalAxes, _avatar.AvatarRig.LeftArm, UxrHandSide.Left) +
|
||||
GetArmTorsoRotationWeight(_avatar.AvatarRig.Spine, _spineUniversalLocalAxes, _avatar.AvatarRig.RightArm, UxrHandSide.Right);
|
||||
|
||||
totalWeight += _settings.SpineTorsion;
|
||||
}
|
||||
|
||||
torsoRotation *= 0.5f; // Because range is [-2, +2] since both hands have [-1, 1] range.
|
||||
|
||||
// Rotate upper body elements
|
||||
|
||||
float maxRotationDegrees = 70.0f;
|
||||
|
||||
if (totalWeight > 0.0f)
|
||||
{
|
||||
if (totalWeight < 1.0f)
|
||||
{
|
||||
totalWeight = 1.0f;
|
||||
}
|
||||
|
||||
float upperChestTorsionAngle = maxRotationDegrees * torsoRotation * (_settings.UpperChestTorsion / totalWeight);
|
||||
float chestTorsionAngle = maxRotationDegrees * torsoRotation * (_settings.ChestTorsion / totalWeight);
|
||||
float spineTorsionAngle = maxRotationDegrees * torsoRotation * (_settings.SpineTorsion / totalWeight);
|
||||
|
||||
_upperChestTorsionAngle = Mathf.SmoothDampAngle(_upperChestTorsionAngle, upperChestTorsionAngle, ref _upperChestTorsionSpeed, BodyTorsionSmoothTime);
|
||||
_chestTorsionAngle = Mathf.SmoothDampAngle(_chestTorsionAngle, chestTorsionAngle, ref _chestTorsionSpeed, BodyTorsionSmoothTime);
|
||||
_spineTorsionAngle = Mathf.SmoothDampAngle(_spineTorsionAngle, spineTorsionAngle, ref _spineTorsionSpeed, BodyTorsionSmoothTime);
|
||||
|
||||
if (_avatar.AvatarRig.UpperChest)
|
||||
{
|
||||
_avatar.AvatarRig.UpperChest.localRotation *= Quaternion.AngleAxis(_spineTorsionAngle, _upperChestUniversalLocalAxes.LocalUp);
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.Chest)
|
||||
{
|
||||
_avatar.AvatarRig.Chest.localRotation *= Quaternion.AngleAxis(_chestTorsionAngle, _chestUniversalLocalAxes.LocalUp);
|
||||
}
|
||||
|
||||
if (_avatar.AvatarRig.Spine)
|
||||
{
|
||||
_avatar.AvatarRig.Spine.localRotation *= Quaternion.AngleAxis(_spineTorsionAngle, _spineUniversalLocalAxes.LocalUp);
|
||||
}
|
||||
}
|
||||
|
||||
// Pop independent transforms (usually hands and other transforms with their own sensors)
|
||||
|
||||
foreach (IndependentBoneInfo boneInfo in _independentBones)
|
||||
{
|
||||
boneInfo.Transform.SetPositionAndRotation(boneInfo.Position, boneInfo.Rotation);
|
||||
}
|
||||
|
||||
_avatarNeck.SetPositionAndRotation(neckPosition, neckRotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies whenever the avatar was moved in order to update the internal forward looking vector.
|
||||
/// </summary>
|
||||
/// <param name="e">Move event parameters</param>
|
||||
public void NotifyAvatarMoved(UxrAvatarMoveEventArgs e)
|
||||
{
|
||||
if (!Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float angle = Vector3.SignedAngle(e.OldForward, e.NewForward, _avatar.transform.up);
|
||||
_avatarForwardTarget = Quaternion.AngleAxis(angle, _avatar.transform.up) * _avatarForwardTarget;
|
||||
_straightSpineForward = Quaternion.AngleAxis(angle, _avatar.transform.up) * _straightSpineForward;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Computes an world position based on an offset from an object.
|
||||
/// </summary>
|
||||
/// <param name="axes">The axes <paramref name="offset" /> refer to</param>
|
||||
/// <param name="transform">The object origin</param>
|
||||
/// <param name="offset">The offset components</param>
|
||||
/// <returns>Offset vector</returns>
|
||||
private Vector3 GetWorldPosFromOffset(Transform axes, Transform transform, Vector3 offset)
|
||||
{
|
||||
return transform.position + axes.right * offset.x + axes.up * offset.y + axes.forward * offset.z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes how much of an influence an arm has on the upper body to make it rotate left or right.
|
||||
/// </summary>
|
||||
/// <param name="upperBodyTransform">Any upper body node (spine, chest...)</param>
|
||||
/// <param name="upperBodyUniversalLocalAxes">
|
||||
/// The upper body nodes's universal local axes.
|
||||
/// These will be the upper body transform local axes that map to the avatar right, up and
|
||||
/// forward vectors.
|
||||
/// </param>
|
||||
/// <param name="arm">The arm</param>
|
||||
/// <param name="handSide">Is it a left arm or right arm?</param>
|
||||
/// <returns>
|
||||
/// Value [-1.0, 1.0] that tells how much the upper body should rotate to the left or
|
||||
/// right due to the arm position.
|
||||
/// </returns>
|
||||
private float GetArmTorsoRotationWeight(Transform upperBodyTransform, UxrUniversalLocalAxes upperBodyUniversalLocalAxes, UxrAvatarArm arm, UxrHandSide handSide)
|
||||
{
|
||||
if (arm.UpperArm && arm.Forearm)
|
||||
{
|
||||
Vector3 shoulderToElbowVector = arm.Forearm.position - arm.UpperArm.position;
|
||||
Vector3 projectedShoulderToElbowVector = Vector3.ProjectOnPlane(shoulderToElbowVector, upperBodyUniversalLocalAxes.WorldUp);
|
||||
|
||||
float straightFactor = projectedShoulderToElbowVector.magnitude / shoulderToElbowVector.magnitude;
|
||||
float armAngle = Vector3.SignedAngle(upperBodyUniversalLocalAxes.WorldRight * (handSide == UxrHandSide.Left ? -1.0f : 1.0f), projectedShoulderToElbowVector, upperBodyUniversalLocalAxes.WorldUp);
|
||||
|
||||
return armAngle / 180.0f * straightFactor;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private const float CameraUpsideDownAngleThreshold = 15.0f; // To avoid gimbal errors we will not update the avatar rotation if the avatar is looking up or down with this angle respect to the vertical axis.
|
||||
private const float AvatarRotationDegreesPerSecond = 1080.0f; // Degrees per second we will move the avatar to compensate for the head torsion
|
||||
private const float DegreesStraightenedPerMeterMoved = 120.0f; // Amount of degrees to straighten the avatar direction with respect to the head direction when the avatar moves.
|
||||
private const float AvatarStraighteningMinSpeed = 0.3f; // To straighten the avatar direction it will need to be moving at least this amount of units per second
|
||||
private const float BodyTorsionSmoothTime = 0.1f; // Body torsion smoothening time
|
||||
|
||||
private UxrAvatar _avatar;
|
||||
private Transform _avatarTransform;
|
||||
private UxrBodyIKSettings _settings;
|
||||
private List<IndependentBoneInfo> _independentBones;
|
||||
|
||||
private Transform _avatarBodyRoot;
|
||||
private Transform _avatarForward;
|
||||
private Transform _avatarNeck;
|
||||
private Transform _avatarHead;
|
||||
private Transform _avatarEyes;
|
||||
|
||||
private Vector3 _neckPosRelativeToEyes;
|
||||
private Quaternion _neckRotRelativeToEyes;
|
||||
private Vector3 _avatarForwardPosRelativeToNeck;
|
||||
private Vector3 _avatarForwardTarget;
|
||||
private Vector3 _straightSpineForward;
|
||||
|
||||
private UxrUniversalLocalAxes _spineUniversalLocalAxes;
|
||||
private UxrUniversalLocalAxes _chestUniversalLocalAxes;
|
||||
private UxrUniversalLocalAxes _upperChestUniversalLocalAxes;
|
||||
private UxrUniversalLocalAxes _neckUniversalLocalAxes;
|
||||
private UxrUniversalLocalAxes _headUniversalLocalAxes;
|
||||
private float _spineTorsionAngle;
|
||||
private float _chestTorsionAngle;
|
||||
private float _upperChestTorsionAngle;
|
||||
private float _spineTorsionSpeed;
|
||||
private float _chestTorsionSpeed;
|
||||
private float _upperChestTorsionSpeed;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrBodyIK.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrBodyIK.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 618804a1df753f84194da553f01a2025
|
||||
timeCreated: 1519294533
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
124
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrBodyIKSettings.cs
vendored
Normal file
124
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrBodyIKSettings.cs
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrBodyIKSettings.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores parameters that drive Inverse Kinematics for full-body avatars.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For now only half-body Inverse Kinematics is supported. Full-body will be implemented at some point.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
public class UxrBodyIKSettings
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _lockBodyPivot;
|
||||
[SerializeField] private float _bodyPivotRotationSpeed = 0.2f;
|
||||
[SerializeField] private float _headFreeRangeBend = 20.0f;
|
||||
[SerializeField] private float _headFreeRangeTorsion = 30.0f;
|
||||
[SerializeField] private float _neckHeadBalance = 0.5f;
|
||||
[SerializeField] private float _spineBend = 0.05f;
|
||||
[SerializeField] private float _spineTorsion = 0.4f;
|
||||
[SerializeField] private float _chestBend = 0.3f;
|
||||
[SerializeField] private float _chestTorsion = 0.8f;
|
||||
[SerializeField] private float _upperChestBend = 0.4f;
|
||||
[SerializeField] private float _upperChestTorsion = 0.2f;
|
||||
[SerializeField] private float _neckBaseHeight = 1.6f;
|
||||
[SerializeField] private float _neckForwardOffset;
|
||||
[SerializeField] private float _eyesBaseHeight = 1.75f;
|
||||
[SerializeField] private float _eyesForwardOffset = 0.1f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the avatar pivot will be kept in place so that it will only rotate around.
|
||||
/// </summary>
|
||||
public bool LockBodyPivot => _lockBodyPivot;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the speed the body will turn around with. This is used to smooth out rotation.
|
||||
/// </summary>
|
||||
public float BodyPivotRotationSpeed => _bodyPivotRotationSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of degrees the head can bend before requiring rotation of other bones down the spine.
|
||||
/// </summary>
|
||||
public float HeadFreeRangeBend => _headFreeRangeBend;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of degrees the head can turn before requiring rotation of other bones down the spine.
|
||||
/// </summary>
|
||||
public float HeadFreeRangeTorsion => _headFreeRangeTorsion;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value in [0.0, 1.0] range that tells how rotation will be distributed between the head and the neck. 0.0
|
||||
/// will fully use the neck and 1.0 will fully use the head. Values in between will distribute it among the two.
|
||||
/// </summary>
|
||||
public float NeckHeadBalance => _neckHeadBalance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount the spine will bend when the head bends.
|
||||
/// </summary>
|
||||
public float SpineBend => _spineBend;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount the spine will turn when the head turns.
|
||||
/// </summary>
|
||||
public float SpineTorsion => _spineTorsion;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount the chest will bend when the head bends.
|
||||
/// </summary>
|
||||
public float ChestBend => _chestBend;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount the chest will turn when the head turns.
|
||||
/// </summary>
|
||||
public float ChestTorsion => _chestTorsion;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount the upper chest will bend when the head bends.
|
||||
/// </summary>
|
||||
public float UpperChestBend => _upperChestBend;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount the upper chest will turn when the head turns.
|
||||
/// </summary>
|
||||
public float UpperChestTorsion => _upperChestTorsion;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of the base of the neck starting from the avatar root Y. This is used to create a dummy neck when
|
||||
/// the avatar is lacking a neck bone.
|
||||
/// </summary>
|
||||
public float NeckBaseHeight => _neckBaseHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the forward offset of the neck starting from the avatar root Z. This is used to create a dummy neck when the
|
||||
/// avatar is lacking a neck bone.
|
||||
/// </summary>
|
||||
public float NeckForwardOffset => _neckForwardOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of the eyes starting from the avatar root Y. This is used to know where to place the avatar head
|
||||
/// knowing the camera will be positioned on the eyes.
|
||||
/// </summary>
|
||||
public float EyesBaseHeight => _eyesBaseHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the forward offset of the eyes starting from the avatar root Z. This is used to know where to place the avatar
|
||||
/// head knowing the camera will be positioned on the eyes.
|
||||
/// </summary>
|
||||
public float EyesForwardOffset => _eyesForwardOffset;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrBodyIKSettings.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrBodyIKSettings.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8d0b000596d4a8c9294cb3450d0fdfe
|
||||
timeCreated: 1642860814
|
||||
23
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdConstraintType.cs
vendored
Normal file
23
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdConstraintType.cs
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrCcdConstraintType.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different constraints of a CCD link.
|
||||
/// </summary>
|
||||
public enum UxrCcdConstraintType
|
||||
{
|
||||
/// <summary>
|
||||
/// Constrained to one axis.
|
||||
/// </summary>
|
||||
SingleAxis,
|
||||
|
||||
/// <summary>
|
||||
/// Constrained to two axes.
|
||||
/// </summary>
|
||||
TwoAxes
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdConstraintType.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdConstraintType.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46ce404cf07f4b73a03b51e6675b8766
|
||||
timeCreated: 1643125910
|
||||
35
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdIKSolver.IterationResult.cs
vendored
Normal file
35
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdIKSolver.IterationResult.cs
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrCcdIKSolver.IterationResult.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
public partial class UxrCcdIKSolver
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Result of the CCD algorithm iteration
|
||||
/// </summary>
|
||||
private enum IterationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The effector has reached the goal.
|
||||
/// </summary>
|
||||
GoalReached,
|
||||
|
||||
/// <summary>
|
||||
/// The effector is still trying to reach the goal.
|
||||
/// </summary>
|
||||
ReachingGoal,
|
||||
|
||||
/// <summary>
|
||||
/// There was an error and no links were rotated in order to reach the goal.
|
||||
/// </summary>
|
||||
Error,
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffa1891b73bb4713833bc72dc78e06c6
|
||||
timeCreated: 1643742218
|
||||
421
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdIKSolver.cs
vendored
Normal file
421
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdIKSolver.cs
vendored
Normal file
@@ -0,0 +1,421 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrCcdIKSolver.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that we use to solve IK chains using CCD (Cyclic Coordinate Descent). A chain is defined
|
||||
/// by a set of links, an effector and a goal.
|
||||
/// The links are bones that will try to make the effector reach the same exact point, or the closest to, the goal.
|
||||
/// Usually the effector is on the tip of the last bone.
|
||||
/// Each link can have different rotation constraints to simulate different behaviours and systems.
|
||||
/// </summary>
|
||||
public partial class UxrCcdIKSolver : UxrIKSolver
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private int _maxIterations = 10;
|
||||
[SerializeField] private float _minDistanceToGoal = 0.001f;
|
||||
[SerializeField] private List<UxrCcdLink> _links = new List<UxrCcdLink>();
|
||||
[SerializeField] private Transform _endEffector;
|
||||
[SerializeField] private Transform _goal;
|
||||
[SerializeField] private bool _constrainGoalToEffector;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of links in the CCD.
|
||||
/// </summary>
|
||||
public IReadOnlyList<UxrCcdLink> Links => _links.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the end effector, which is the point that is part of the chain that will try to match the goal position.
|
||||
/// </summary>
|
||||
public Transform EndEffector => _endEffector;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the goal, which is the goal that the chain will try to match with the <see cref="EndEffector" />.
|
||||
/// </summary>
|
||||
public Transform Goal => _goal;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides UxrIKSolver
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Initialized => _initialized;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the internal data for the IK chain. This will only need to be called once during Awake(), but inside
|
||||
/// the Unity editor we can call it also for drawing some gizmos that need it.
|
||||
/// </summary>
|
||||
public void ComputeLinkData()
|
||||
{
|
||||
if (_links != null && _endEffector != null)
|
||||
{
|
||||
for (int i = 0; i < _links.Count; ++i)
|
||||
{
|
||||
if (_links[i].Bone != null && !(i < _links.Count - 1 && _links[i + 1].Bone == null))
|
||||
{
|
||||
_links[i].MtxToLocalParent = Matrix4x4.identity;
|
||||
|
||||
if (_links[i].Bone.parent != null)
|
||||
{
|
||||
_links[i].MtxToLocalParent = _links[i].Bone.parent.worldToLocalMatrix;
|
||||
}
|
||||
|
||||
_links[i].Initialized = true;
|
||||
_links[i].InitialLocalRotation = _links[i].Bone.localRotation;
|
||||
_links[i].LocalSpaceAxis1ZeroAngleVector = _links[i].RotationAxis1.GetPerpendicularVector();
|
||||
_links[i].LocalSpaceAxis2ZeroAngleVector = _links[i].RotationAxis2.GetPerpendicularVector();
|
||||
_links[i].ParentSpaceAxis1 = _links[i].MtxToLocalParent.MultiplyVector(_links[i].Bone.TransformDirection(_links[i].RotationAxis1));
|
||||
_links[i].ParentSpaceAxis2 = _links[i].MtxToLocalParent.MultiplyVector(_links[i].Bone.TransformDirection(_links[i].RotationAxis2));
|
||||
_links[i].ParentSpaceAxis1ZeroAngleVector = _links[i].MtxToLocalParent.MultiplyVector(_links[i].Bone.TransformDirection(_links[i].LocalSpaceAxis1ZeroAngleVector));
|
||||
_links[i].ParentSpaceAxis2ZeroAngleVector = _links[i].MtxToLocalParent.MultiplyVector(_links[i].Bone.TransformDirection(_links[i].LocalSpaceAxis2ZeroAngleVector));
|
||||
_links[i].LinkLength = i == _links.Count - 1 ? Vector3.Distance(_links[i].Bone.position, _endEffector.position) : Vector3.Distance(_links[i].Bone.position, _links[i + 1].Bone.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the initial link rotations.
|
||||
/// </summary>
|
||||
public void RestoreInitialRotations()
|
||||
{
|
||||
if (_links != null)
|
||||
{
|
||||
foreach (UxrCcdLink link in _links)
|
||||
{
|
||||
if (link.Bone != null && link.Initialized)
|
||||
{
|
||||
link.Bone.transform.localRotation = link.InitialLocalRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the weight of the given link.
|
||||
/// </summary>
|
||||
/// <param name="link">Link index</param>
|
||||
/// <param name="weight">Link weight [0.0f, 1.0f]</param>
|
||||
public void SetLinkWeight(int link, float weight)
|
||||
{
|
||||
if (link >= 0 && link < _links.Count)
|
||||
{
|
||||
_links[link].Weight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default values for the given link.
|
||||
/// </summary>
|
||||
/// <param name="link">Link index</param>
|
||||
public void SetLinkDefaultValues(int link)
|
||||
{
|
||||
if (link >= 0 && link < _links.Count)
|
||||
{
|
||||
_links[link] = new UxrCcdLink();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the link data.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
ComputeLinkData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the goal needs to be parented so that the IK computation doesn't affect the goal itself.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (_goal.HasParent(_endEffector) || _links.Any(l => _goal.HasParent(l.Bone)))
|
||||
{
|
||||
_goal.SetParent(transform);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrIKSolver
|
||||
|
||||
/// <summary>
|
||||
/// IK solver implementation. Will try to make the end effector in the link chain to match the goal.
|
||||
/// </summary>
|
||||
protected override void InternalSolveIK()
|
||||
{
|
||||
Vector3 goalPosition = _goal.position;
|
||||
Vector3 goalForward = _goal.forward;
|
||||
|
||||
for (int i = 0; i < _maxIterations; ++i)
|
||||
{
|
||||
IterationResult result = ComputeSingleIterationCcd(_links, _endEffector, goalPosition, goalForward, _minDistanceToGoal);
|
||||
|
||||
if (result != IterationResult.ReachingGoal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_constrainGoalToEffector && Vector3.Distance(goalPosition, _endEffector.position) > _minDistanceToGoal)
|
||||
{
|
||||
_goal.position = _endEffector.position;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Fixes an angle so that it is always in the -180, 180 degrees range.
|
||||
/// </summary>
|
||||
/// <param name="angle">Angle in degrees</param>
|
||||
/// <returns>Angle in the -180, 180 degrees range</returns>
|
||||
private static float FixAngle(float angle)
|
||||
{
|
||||
angle = angle % 360.0f;
|
||||
|
||||
if (angle > 180.0f)
|
||||
{
|
||||
angle -= 360.0f;
|
||||
}
|
||||
else if (angle < -180.0f)
|
||||
{
|
||||
angle += 360.0f;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes a single iteration of the CCD algorithm on our link chain.
|
||||
/// </summary>
|
||||
/// <param name="links">List of links (bones) of the chain</param>
|
||||
/// <param name="endEffector">The point on the chain that will try to reach the goal</param>
|
||||
/// <param name="goalPosition">The goal that the end effector will try to reach</param>
|
||||
/// <param name="goalForward">The goal forward vector that the end effector will try to reach if alignment is enabled</param>
|
||||
/// <param name="minDistanceToGoal">Minimum distance to the goal that is considered success</param>
|
||||
/// <returns>Result of the iteration</returns>
|
||||
private static IterationResult ComputeSingleIterationCcd(List<UxrCcdLink> links, Transform endEffector, Vector3 goalPosition, Vector3 goalForward, float minDistanceToGoal)
|
||||
{
|
||||
if (Vector3.Distance(goalPosition, endEffector.position) <= minDistanceToGoal)
|
||||
{
|
||||
return IterationResult.GoalReached;
|
||||
}
|
||||
|
||||
// Iterate from tip to base
|
||||
|
||||
bool linksRotated = false;
|
||||
|
||||
for (var i = links.Count - 1; i >= 0; i--)
|
||||
{
|
||||
UxrCcdLink link = links[i];
|
||||
|
||||
if (Vector3.Distance(goalPosition, endEffector.position) <= minDistanceToGoal)
|
||||
{
|
||||
return IterationResult.GoalReached;
|
||||
}
|
||||
|
||||
// Compute the matrix that transforms from world space to the parent bone's local space
|
||||
|
||||
link.MtxToLocalParent = Matrix4x4.identity;
|
||||
|
||||
if (link.Bone.parent != null)
|
||||
{
|
||||
link.MtxToLocalParent = link.Bone.parent.worldToLocalMatrix;
|
||||
}
|
||||
|
||||
// Compute the vector that rotates around axis1 corresponding to 0 degrees. It will be computed in local space of the parent link.
|
||||
|
||||
Vector3 parentSpaceAngle1Vector = link.MtxToLocalParent.MultiplyVector(link.Bone.TransformDirection(link.LocalSpaceAxis1ZeroAngleVector));
|
||||
|
||||
if (link.Constraint == UxrCcdConstraintType.TwoAxes)
|
||||
{
|
||||
// When dealing with 2 axis constraint mode we need to recompute the rotation axis in parent space
|
||||
link.ParentSpaceAxis1 = link.MtxToLocalParent.MultiplyVector(link.Bone.TransformDirection(link.RotationAxis1));
|
||||
}
|
||||
|
||||
// Using the computations above, calculate the angle1 value. This is the value of rotation in degrees corresponding to the first constraint axis
|
||||
|
||||
link.Angle1 = Vector3.SignedAngle(Vector3.ProjectOnPlane(link.ParentSpaceAxis1ZeroAngleVector, link.ParentSpaceAxis1),
|
||||
Vector3.ProjectOnPlane(parentSpaceAngle1Vector, link.ParentSpaceAxis1),
|
||||
link.ParentSpaceAxis1);
|
||||
|
||||
// Now let's rotate around axis1 if needed. We will compute the current vector from this node to the effector and also the current vector from this node
|
||||
// to the target. Our goal is to make the first vector match the second vector but we may only rotate around axis1. So what we do is project the goal vector
|
||||
// onto the plane with axis1 as its normal and this will be the result of our "valid" rotation due to the constraint.
|
||||
|
||||
Vector3 currentDirection = endEffector.position - link.Bone.position;
|
||||
Vector3 desiredDirection = goalPosition - link.Bone.position;
|
||||
|
||||
if (link.AlignToGoal)
|
||||
{
|
||||
currentDirection = endEffector.forward;
|
||||
desiredDirection = goalForward;
|
||||
}
|
||||
|
||||
Vector3 worldAxis1 = link.Bone.TransformDirection(link.RotationAxis1);
|
||||
Vector3 closestVectorAxis1Rotation = Vector3.ProjectOnPlane(desiredDirection, worldAxis1);
|
||||
|
||||
float newAxis1AngleIncrement = link.Weight * Vector3.SignedAngle(Vector3.ProjectOnPlane(currentDirection, worldAxis1), closestVectorAxis1Rotation, worldAxis1);
|
||||
float totalAngleAxis1 = FixAngle(link.Angle1 + newAxis1AngleIncrement);
|
||||
|
||||
// Now that we have computed our increment, let's see if we need to clamp it between the limits
|
||||
|
||||
if (link.Axis1HasLimits)
|
||||
{
|
||||
if (totalAngleAxis1 > link.Axis1AngleMax)
|
||||
{
|
||||
newAxis1AngleIncrement -= totalAngleAxis1 - link.Axis1AngleMax;
|
||||
}
|
||||
else if (totalAngleAxis1 < link.Axis1AngleMin)
|
||||
{
|
||||
newAxis1AngleIncrement += link.Axis1AngleMin - totalAngleAxis1;
|
||||
}
|
||||
|
||||
totalAngleAxis1 = FixAngle(link.Angle1 + newAxis1AngleIncrement);
|
||||
}
|
||||
|
||||
// Do we need to rotate?
|
||||
|
||||
if (Mathf.Approximately(newAxis1AngleIncrement, 0.0f) == false)
|
||||
{
|
||||
link.Angle1 = totalAngleAxis1;
|
||||
link.Bone.localRotation = link.InitialLocalRotation * Quaternion.AngleAxis(link.Angle1, link.RotationAxis1);
|
||||
|
||||
if (link.Constraint == UxrCcdConstraintType.TwoAxes)
|
||||
{
|
||||
link.Bone.localRotation = link.Bone.localRotation * Quaternion.AngleAxis(link.Angle2, link.RotationAxis2);
|
||||
}
|
||||
|
||||
linksRotated = true;
|
||||
}
|
||||
|
||||
if (link.Constraint == UxrCcdConstraintType.TwoAxes)
|
||||
{
|
||||
// Axis 2. Axis 2 works exactly like axis 1 but we operate on another plane
|
||||
|
||||
Vector3 parentSpaceAngle2Vector = link.MtxToLocalParent.MultiplyVector(link.Bone.TransformDirection(link.LocalSpaceAxis2ZeroAngleVector));
|
||||
|
||||
link.ParentSpaceAxis2 = link.MtxToLocalParent.MultiplyVector(link.Bone.TransformDirection(link.RotationAxis2));
|
||||
link.Angle2 = Vector3.SignedAngle(Vector3.ProjectOnPlane(link.ParentSpaceAxis2ZeroAngleVector, link.ParentSpaceAxis2),
|
||||
Vector3.ProjectOnPlane(parentSpaceAngle2Vector, link.ParentSpaceAxis2),
|
||||
link.ParentSpaceAxis2);
|
||||
|
||||
currentDirection = endEffector.position - link.Bone.position;
|
||||
desiredDirection = goalPosition - link.Bone.position;
|
||||
|
||||
if (link.AlignToGoal)
|
||||
{
|
||||
currentDirection = endEffector.forward;
|
||||
desiredDirection = goalForward;
|
||||
}
|
||||
|
||||
Vector3 worldAxis2 = link.Bone.TransformDirection(link.RotationAxis2);
|
||||
Vector3 closestVectorAxis2Rotation = Vector3.ProjectOnPlane(desiredDirection, worldAxis2);
|
||||
|
||||
float newAxis2AngleIncrement = link.Weight * Vector3.SignedAngle(Vector3.ProjectOnPlane(currentDirection, worldAxis2), closestVectorAxis2Rotation, worldAxis2);
|
||||
float totalAngleAxis2 = FixAngle(link.Angle2 + newAxis2AngleIncrement);
|
||||
|
||||
if (link.Axis2HasLimits)
|
||||
{
|
||||
if (totalAngleAxis2 > link.Axis2AngleMax)
|
||||
{
|
||||
newAxis2AngleIncrement -= totalAngleAxis2 - link.Axis2AngleMax;
|
||||
}
|
||||
else if (totalAngleAxis2 < link.Axis2AngleMin)
|
||||
{
|
||||
newAxis2AngleIncrement += link.Axis2AngleMin - totalAngleAxis2;
|
||||
}
|
||||
|
||||
totalAngleAxis2 = FixAngle(link.Angle2 + newAxis2AngleIncrement);
|
||||
}
|
||||
|
||||
if (Mathf.Approximately(newAxis2AngleIncrement, 0.0f) == false)
|
||||
{
|
||||
// Rotation order is first angle2 then angle1 because previously we have rotated in this order already
|
||||
link.Angle2 = totalAngleAxis2;
|
||||
link.Bone.localRotation = link.InitialLocalRotation * Quaternion.AngleAxis(link.Angle1, link.RotationAxis1) * Quaternion.AngleAxis(link.Angle2, link.RotationAxis2);
|
||||
|
||||
linksRotated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return linksRotated ? Vector3.Distance(goalPosition, endEffector.position) <= minDistanceToGoal ? IterationResult.GoalReached : IterationResult.ReachingGoal : IterationResult.Error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform that should be used to restore the goal position every time an IK link
|
||||
/// is reoriented.
|
||||
/// We use this in cases where we manipulate an object that the goal is part of, and the IK chain
|
||||
/// is in a hierarchy above the object/goal. This is needed because when computing the different
|
||||
/// IK steps, the goal and the object may be repositioned as a consequence, being below in the chain.
|
||||
/// As a double measure, what we try to reposition is the topmost parent that is below the IK chain,
|
||||
/// since the goal may be a dummy at the end of the chain and repositioning the goal alone would
|
||||
/// not be enough.
|
||||
/// </summary>
|
||||
/// <param name="links">List of links (bones) of the chain</param>
|
||||
/// <param name="goal">The goal that the end effector will try to reach</param>
|
||||
/// <returns>Transform that should be stored</returns>
|
||||
private static Transform GetGoalSafeRestoreTransform(List<UxrCcdLink> links, Transform goal)
|
||||
{
|
||||
Transform current = goal;
|
||||
Transform previous = goal;
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
for (int i = links.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (current == links[i].Bone && current != previous)
|
||||
{
|
||||
// Found a bone. previous here is the child that we should move/rotate in order to
|
||||
// preserve the original goal position/orientation.
|
||||
return previous;
|
||||
}
|
||||
}
|
||||
|
||||
previous = current;
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
return goal;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private bool _initialized;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
12
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdIKSolver.cs.meta
vendored
Normal file
12
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdIKSolver.cs.meta
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fba00edd9cbdb6b43915c325836d87c8
|
||||
timeCreated: 1510133937
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
185
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdLink.cs
vendored
Normal file
185
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdLink.cs
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrCcdLink.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a link -bone- in an IK chain solved using CCD.
|
||||
/// </summary>
|
||||
/// <seealso cref="UxrCcdIKSolver" />
|
||||
[Serializable]
|
||||
public class UxrCcdLink
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
// Setup in the editor
|
||||
[SerializeField] private Transform _bone;
|
||||
[SerializeField] private float _weight;
|
||||
[SerializeField] private UxrCcdConstraintType _constraint;
|
||||
[SerializeField] private Vector3 _rotationAxis1;
|
||||
[SerializeField] private Vector3 _rotationAxis2;
|
||||
[SerializeField] private bool _axis1HasLimits;
|
||||
[SerializeField] private float _axis1AngleMin;
|
||||
[SerializeField] private float _axis1AngleMax;
|
||||
[SerializeField] private bool _axis2HasLimits;
|
||||
[SerializeField] private float _axis2AngleMin;
|
||||
[SerializeField] private float _axis2AngleMax;
|
||||
[SerializeField] private bool _alignToGoal;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the link transform.
|
||||
/// </summary>
|
||||
public Transform Bone => _bone;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the link constraint type.
|
||||
/// </summary>
|
||||
public UxrCcdConstraintType Constraint => _constraint;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first rotation axis.
|
||||
/// </summary>
|
||||
public Vector3 RotationAxis1 => _rotationAxis1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the second rotation axis when there are two constraints.
|
||||
/// </summary>
|
||||
public Vector3 RotationAxis2 => _rotationAxis2;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the first axis has rotational limits.
|
||||
/// </summary>
|
||||
public bool Axis1HasLimits => _axis1HasLimits;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lower angle limits of the first axis.
|
||||
/// </summary>
|
||||
public float Axis1AngleMin => _axis1AngleMin;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the higher angle limits of the first axis.
|
||||
/// </summary>
|
||||
public float Axis1AngleMax => _axis1AngleMax;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the second axis has rotational limits when there are two constraints.
|
||||
/// </summary>
|
||||
public bool Axis2HasLimits => _axis2HasLimits;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lower angle limits of the second axis when there are two constraints.
|
||||
/// </summary>
|
||||
public float Axis2AngleMin => _axis2AngleMin;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the higher angle limits of the second axis when there are two constraints.
|
||||
/// </summary>
|
||||
public float Axis2AngleMax => _axis2AngleMax;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the effector should not only try to position itself on the goal but also use the same orientation.
|
||||
/// </summary>
|
||||
public bool AlignToGoal => _alignToGoal;
|
||||
|
||||
/// <summary>
|
||||
/// The weight among all the CCD links in the chain.
|
||||
/// </summary>
|
||||
public float Weight
|
||||
{
|
||||
get => _weight;
|
||||
set => _weight = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the data has been initialized.
|
||||
/// </summary>
|
||||
public bool Initialized { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local rotation at the beginning.
|
||||
/// </summary>
|
||||
public Quaternion InitialLocalRotation { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference perpendicular to axis1 that is considered as the reference of having 0 degrees around axis1.
|
||||
/// </summary>
|
||||
public Vector3 LocalSpaceAxis1ZeroAngleVector { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference perpendicular to axis2 that is considered as the reference of having 0 degrees around axis2.
|
||||
/// </summary>
|
||||
public Vector3 LocalSpaceAxis2ZeroAngleVector { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the link.
|
||||
/// </summary>
|
||||
public float LinkLength { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="RotationAxis1" /> in local space of the parent object.
|
||||
/// </summary>
|
||||
public Vector3 ParentSpaceAxis1 { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="RotationAxis2" /> in local space of the parent object.
|
||||
/// </summary>
|
||||
public Vector3 ParentSpaceAxis2 { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="LocalSpaceAxis1ZeroAngleVector" /> in local space of the parent object.
|
||||
/// </summary>
|
||||
public Vector3 ParentSpaceAxis1ZeroAngleVector { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="LocalSpaceAxis2ZeroAngleVector" /> in local space of the parent object.
|
||||
/// </summary>
|
||||
public Vector3 ParentSpaceAxis2ZeroAngleVector { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transformation matrix that gets from world-space to local space in the parent transform.
|
||||
/// </summary>
|
||||
public Matrix4x4 MtxToLocalParent { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets rotation degrees around <see cref="RotationAxis1" />.
|
||||
/// </summary>
|
||||
public float Angle1 { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets rotation degrees around <see cref="RotationAxis2" />.
|
||||
/// </summary>
|
||||
public float Angle2 { get; internal set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor.
|
||||
/// </summary>
|
||||
public UxrCcdLink()
|
||||
{
|
||||
_weight = 1.0f;
|
||||
_constraint = UxrCcdConstraintType.SingleAxis;
|
||||
_rotationAxis1 = Vector3.right;
|
||||
_rotationAxis2 = Vector3.up;
|
||||
_axis1HasLimits = true;
|
||||
_axis1AngleMin = -45.0f;
|
||||
_axis1AngleMax = 45.0f;
|
||||
_axis2HasLimits = false;
|
||||
_axis2AngleMin = -45.0f;
|
||||
_axis2AngleMax = 45.0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdLink.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrCcdLink.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34bdb75b6a3c4a7cb567e7f471b643a1
|
||||
timeCreated: 1643125714
|
||||
131
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrIKSolver.cs
vendored
Normal file
131
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrIKSolver.cs
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrIKSolver.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
/// <summary>
|
||||
/// Base IK Solver class. IK solvers should inherit from it and override the <see cref="InternalSolveIK" /> method.
|
||||
/// Not all solvers need to be part of an avatar, but the <see cref="UxrAvatarComponent{T}" /> inheritance is used to
|
||||
/// be able to enumerate all the solvers that are part of an avatar.
|
||||
/// </summary>
|
||||
public abstract class UxrIKSolver : UxrAvatarComponent<UxrIKSolver>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _enabled = true;
|
||||
[SerializeField] private bool _manualUpdate;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Called right before the IK is about to be solved during the current frame
|
||||
/// </summary>
|
||||
public event Action Solving;
|
||||
|
||||
/// <summary>
|
||||
/// Called right after the IK was solved during the current frame
|
||||
/// </summary>
|
||||
public event Action Solved;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the component is initialized.
|
||||
/// </summary>
|
||||
public abstract bool Initialized { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the solver needs to be updated automatically.
|
||||
/// </summary>
|
||||
public bool NeedsAutoUpdate => gameObject.activeInHierarchy && enabled && SolverEnabled && !ManualUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the IK solver enabled state?
|
||||
/// </summary>
|
||||
public bool SolverEnabled
|
||||
{
|
||||
get => _enabled;
|
||||
set => _enabled = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the IK solver will update itself. Otherwise the user will be responsible of calling <see cref="SolveIK" />.
|
||||
/// </summary>
|
||||
public bool ManualUpdate
|
||||
{
|
||||
get => _manualUpdate;
|
||||
set => _manualUpdate = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Solves the IK. Calls <see cref="InternalSolveIK" />,which is implemented in child classes, but calls the
|
||||
/// appropriate <see cref="Solving" /> and <see cref="Solved" /> events.
|
||||
/// </summary>
|
||||
public void SolveIK()
|
||||
{
|
||||
Solving?.Invoke();
|
||||
InternalSolveIK();
|
||||
Solved?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to events
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
UxrManager.StageUpdating += UxrManager_StageUpdating;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
UxrManager.StageUpdating -= UxrManager_StageUpdating;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Will solve the IK chain in case it is not part of an avatar. If it is part of a VR avatar, the VR avatar will take
|
||||
/// care of calling the SolveIK method so that it is processed in the correct order, after the hands are updated.
|
||||
/// </summary>
|
||||
private void UxrManager_StageUpdating(UxrUpdateStage stage)
|
||||
{
|
||||
if (stage == UxrUpdateStage.PostProcess && Avatar == null && NeedsAutoUpdate)
|
||||
{
|
||||
SolveIK();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// To be implemented in child classes to execute the actual IK solving algorithm for the current frame
|
||||
/// </summary>
|
||||
protected abstract void InternalSolveIK();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrIKSolver.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrIKSolver.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c24e6ba752cd9d47b3c68e991363e16
|
||||
timeCreated: 1519111756
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
88
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrWristTorsionIKSolver.cs
vendored
Normal file
88
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrWristTorsionIKSolver.cs
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrWristTorsionIKSolver.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Math;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.IK
|
||||
{
|
||||
/// <summary>
|
||||
/// IK solver that distributes a wrist torsion among different bones in a forearm in order to smooth it out.
|
||||
/// </summary>
|
||||
public class UxrWristTorsionIKSolver : UxrIKSolver
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] [Range(0.0f, 1.0f)] private float _amount = 1.0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the amount of torsion to apply on this Transform from the source. 0 = no torsion, 1 = full torsion,
|
||||
/// etc.
|
||||
/// </summary>
|
||||
public float Amount
|
||||
{
|
||||
get => _amount;
|
||||
set => _amount = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides UxrIKSolver
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Initialized => _initialized;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_handSide = transform.HasParent(Avatar.AvatarRig.LeftArm.UpperArm) ? UxrHandSide.Left : UxrHandSide.Right;
|
||||
_startLocalRotation = transform.localRotation;
|
||||
|
||||
UxrUniversalLocalAxes handUniversalLocalAxes = Avatar.AvatarRigInfo.GetArmInfo(_handSide).HandUniversalLocalAxes;
|
||||
_torsionLocalAxis = transform.InverseTransformDirection(handUniversalLocalAxes.WorldForward).GetClosestAxis();
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrIKSolver
|
||||
|
||||
/// <summary>
|
||||
/// Solves the Inverse Kinematics.
|
||||
/// </summary>
|
||||
protected override void InternalSolveIK()
|
||||
{
|
||||
float angle = Avatar.AvatarRigInfo.GetArmInfo(_handSide).WristTorsionInfo.WristTorsionAngle;
|
||||
transform.localRotation = _startLocalRotation * Quaternion.AngleAxis(angle * Amount, _torsionLocalAxis);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private bool _initialized;
|
||||
private UxrHandSide _handSide;
|
||||
private Quaternion _startLocalRotation;
|
||||
private Vector3 _torsionLocalAxis;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
12
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrWristTorsionIKSolver.cs.meta
vendored
Normal file
12
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/IK/UxrWristTorsionIKSolver.cs.meta
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a41528363b03e1c4a8b730dee5b434ab
|
||||
timeCreated: 1510043257
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55c027c769a848d3bcca74385df23e35
|
||||
timeCreated: 1643801906
|
||||
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrColor32Interpolator.cs
vendored
Normal file
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrColor32Interpolator.cs
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrColor32Interpolator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interpolator for <see cref="Color32" />.
|
||||
/// </summary>
|
||||
public class UxrColor32Interpolator : UxrVarInterpolator<Color32>
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="smoothDamp">Smooth damp for interpolation [0.0, 1.0] where 0.0 means no smoothing</param>
|
||||
/// <param name="useStep">Whether to use step interpolation, where the interpolation will always return the start value</param>
|
||||
public UxrColor32Interpolator(float smoothDamp = 0.0f, bool useStep = false) : base(smoothDamp, useStep)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrVarInterpolator<Color32>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Color32 GetInterpolatedValue(Color32 a, Color32 b, float t)
|
||||
{
|
||||
return Color32.Lerp(a, b, t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Default interpolator with smoothing.
|
||||
/// </summary>
|
||||
public static readonly UxrColor32Interpolator DefaultInterpolator = new UxrColor32Interpolator(0.0f, false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c25c810b663cfb9479248e749fcd8f68
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrColorInterpolator.cs
vendored
Normal file
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrColorInterpolator.cs
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrColorInterpolator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interpolator for <see cref="Color" />.
|
||||
/// </summary>
|
||||
public class UxrColorInterpolator : UxrVarInterpolator<Color>
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="smoothDamp">Smooth damp for interpolation [0.0, 1.0] where 0.0 means no smoothing</param>
|
||||
/// <param name="useStep">Whether to use step interpolation, where the interpolation will always return the start value</param>
|
||||
public UxrColorInterpolator(float smoothDamp = 0.0f, bool useStep = false) : base(smoothDamp, useStep)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrVarInterpolator<Color>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Color GetInterpolatedValue(Color a, Color b, float t)
|
||||
{
|
||||
return Color.Lerp(a, b, t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Default interpolator with smoothing.
|
||||
/// </summary>
|
||||
public static readonly UxrColorInterpolator DefaultInterpolator = new UxrColorInterpolator(0.0f, false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee74b3fb73e96684e950de6e4b348181
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
80
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrEasing.cs
vendored
Normal file
80
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrEasing.cs
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrEasing.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Animation.Lights;
|
||||
using UltimateXR.Animation.Materials;
|
||||
using UltimateXR.Animation.Transforms;
|
||||
using UltimateXR.Animation.UI;
|
||||
using UltimateXR.Manipulation.Helpers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Type of interpolation curves.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// References:
|
||||
/// </para>
|
||||
/// <list type="bullet">
|
||||
/// <item>http://easings.net</item>
|
||||
/// <item>http://gillcup.readthedocs.org/en/latest/_images/easings.png</item>
|
||||
/// </list>
|
||||
/// Examples of some classes that use interpolation:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="UxrInterpolator" />: Access to interpolation calculations</item>
|
||||
/// <item><see cref="UxrTween" /> and all derived classes (UI tweening)</item>
|
||||
/// <item><see cref="UxrAnimatedTransform" /> (<see cref="Transform" /> animation)</item>
|
||||
/// <item><see cref="UxrAnimatedLightIntensity" /> (<see cref="Light" /> intensity parameter animation)</item>
|
||||
/// <item><see cref="UxrAnimatedMaterial" /> (<see cref="Material" /> parameter animation)</item>
|
||||
/// <item><see cref="UxrRestoreOnRelease" /> (Restores original grabbable object position/rotation when released)</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public enum UxrEasing
|
||||
{
|
||||
Linear,
|
||||
EaseInSine,
|
||||
EaseOutSine,
|
||||
EaseInOutSine,
|
||||
EaseOutInSine,
|
||||
EaseInQuad,
|
||||
EaseOutQuad,
|
||||
EaseInOutQuad,
|
||||
EaseOutInQuad,
|
||||
EaseInCubic,
|
||||
EaseOutCubic,
|
||||
EaseInOutCubic,
|
||||
EaseOutInCubic,
|
||||
EaseInQuart,
|
||||
EaseOutQuart,
|
||||
EaseInOutQuart,
|
||||
EaseOutInQuart,
|
||||
EaseInQuint,
|
||||
EaseOutQuint,
|
||||
EaseInOutQuint,
|
||||
EaseOutInQuint,
|
||||
EaseInExpo,
|
||||
EaseOutExpo,
|
||||
EaseInOutExpo,
|
||||
EaseOutInExpo,
|
||||
EaseInCirc,
|
||||
EaseOutCirc,
|
||||
EaseInOutCirc,
|
||||
EaseOutInCirc,
|
||||
EaseInBack,
|
||||
EaseOutBack,
|
||||
EaseInOutBack,
|
||||
EaseOutInBack,
|
||||
EaseInElastic,
|
||||
EaseOutElastic,
|
||||
EaseInOutElastic,
|
||||
EaseOutInElastic,
|
||||
EaseInBounce,
|
||||
EaseOutBounce,
|
||||
EaseInOutBounce,
|
||||
EaseOutInBounce
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrEasing.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrEasing.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b117473a846147e7a03b9ae6af20c21a
|
||||
timeCreated: 1642849506
|
||||
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrFloatIInterpolator.cs
vendored
Normal file
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrFloatIInterpolator.cs
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFloatInterpolator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interpolator for float values.
|
||||
/// </summary>
|
||||
public class UxrFloatInterpolator : UxrVarInterpolator<float>
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="smoothDamp">Smooth damp for interpolation [0.0, 1.0] where 0.0 means no smoothing</param>
|
||||
/// <param name="useStep">Whether to use step interpolation, where the interpolation will always return the start value</param>
|
||||
public UxrFloatInterpolator(float smoothDamp = 0.0f, bool useStep = false) : base(smoothDamp, useStep)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrVarInterpolator<float>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override float GetInterpolatedValue(float a, float b, float t)
|
||||
{
|
||||
return Mathf.Lerp(a, b, t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Default interpolator with smoothing.
|
||||
/// </summary>
|
||||
public static readonly UxrFloatInterpolator DefaultInterpolator = new UxrFloatInterpolator(0.0f, false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0fb9477ff396cd3409bf24e9e9466ce7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrIntInterpolator.cs
vendored
Normal file
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrIntInterpolator.cs
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrIntInterpolator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interpolator for int values.
|
||||
/// </summary>
|
||||
public class UxrIntInterpolator : UxrVarInterpolator<int>
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="smoothDamp">Smooth damp for interpolation [0.0, 1.0] where 0.0 means no smoothing</param>
|
||||
/// <param name="useStep">Whether to use step interpolation, where the interpolation will always return the start value</param>
|
||||
public UxrIntInterpolator(float smoothDamp = 0.0f, bool useStep = false) : base(smoothDamp, useStep)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrVarInterpolator<int>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override int GetInterpolatedValue(int a, int b, float t)
|
||||
{
|
||||
return Mathf.RoundToInt(Mathf.Lerp(a, b, t));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Default interpolator with smoothing.
|
||||
/// </summary>
|
||||
public static readonly UxrIntInterpolator DefaultInterpolator = new UxrIntInterpolator();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
11
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrIntInterpolator.cs.meta
vendored
Normal file
11
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrIntInterpolator.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a825634ab74f53042ab27d06218b3c68
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
197
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrInterpolationSettings.cs
vendored
Normal file
197
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrInterpolationSettings.cs
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrInterpolationSettings.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the different parameters of an interpolation.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrInterpolationSettings
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private float _durationSeconds;
|
||||
[SerializeField] private float _delaySeconds;
|
||||
[SerializeField] private UxrEasing _easing;
|
||||
[SerializeField] private UxrLoopMode _loopMode;
|
||||
[SerializeField] private float _loopedDurationSeconds;
|
||||
[SerializeField] private bool _useUnscaledTime;
|
||||
[SerializeField] private bool _delayUsingEndValue;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interpolation duration in seconds. In looped interpolations it tells the duration of a single
|
||||
/// loop.
|
||||
/// </summary>
|
||||
public float DurationSeconds
|
||||
{
|
||||
get => _durationSeconds;
|
||||
set => _durationSeconds = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interpolation delay in seconds. The delay is usually relative to the time the object that it uses
|
||||
/// was enabled and specifies an initial waiting time before the actual interpolation will start.
|
||||
/// </summary>
|
||||
public float DelaySeconds
|
||||
{
|
||||
get => _delaySeconds;
|
||||
set => _delaySeconds = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the easing function to use by the interpolation.
|
||||
/// </summary>
|
||||
public UxrEasing Easing
|
||||
{
|
||||
get => _easing;
|
||||
set => _easing = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if and how to loop the interpolation.
|
||||
/// </summary>
|
||||
public UxrLoopMode LoopMode
|
||||
{
|
||||
get => _loopMode;
|
||||
set => _loopMode = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total animation duration in interpolations that use looping. The duration of a single loop is
|
||||
/// described by <see cref="DurationSeconds" />. A negative value tells to loop indefinitely.
|
||||
/// </summary>
|
||||
public float LoopedDurationSeconds
|
||||
{
|
||||
get => _loopedDurationSeconds;
|
||||
set => _loopedDurationSeconds = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to use unscaled time (<see cref="Time.unscaledTime" />) or regular time
|
||||
/// <see cref="Time.time" /> when interpolating.
|
||||
/// Regular time is affected by <see cref="Time.timeScale" />, which is normally used to pause the application or
|
||||
/// simulate slow motion effects.
|
||||
/// </summary>
|
||||
public bool UseUnscaledTime
|
||||
{
|
||||
get => _useUnscaledTime;
|
||||
set => _useUnscaledTime = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to use the interpolation end value during the initial delay, if there is a
|
||||
/// <see cref="DelaySeconds" /> value specified.
|
||||
/// By default the interpolation uses the start value during the initial delay.
|
||||
/// </summary>
|
||||
public bool DelayUsingEndValue
|
||||
{
|
||||
get => _delayUsingEndValue;
|
||||
set => _delayUsingEndValue = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public UxrInterpolationSettings()
|
||||
{
|
||||
_durationSeconds = 0.0f;
|
||||
_delaySeconds = 0.0f;
|
||||
_easing = UxrEasing.Linear;
|
||||
_loopMode = UxrLoopMode.None;
|
||||
_loopedDurationSeconds = -1.0f;
|
||||
_useUnscaledTime = false;
|
||||
_delayUsingEndValue = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UxrInterpolationSettings constructor.
|
||||
/// </summary>
|
||||
/// <param name="durationSeconds">
|
||||
/// The duration in seconds the interpolation will be applied. If a loopMode was specified, it tells the duration of a
|
||||
/// single loop.
|
||||
/// </param>
|
||||
/// <param name="delaySeconds">The delay in seconds before the interpolation</param>
|
||||
/// <param name="easing">The type of interpolation used.</param>
|
||||
/// <param name="loopMode">The type of looping used.</param>
|
||||
/// <param name="loopedDurationSeconds">
|
||||
/// If loopMode is not LoopMode.None this parameter will tell how many seconds the total duration of
|
||||
/// the interpolation will last and durationSeconds will tell the duration of each loop. A negative value means it will
|
||||
/// loop forever.
|
||||
/// </param>
|
||||
/// <param name="useUnscaledTime">
|
||||
/// Tells whether to use the real timer value <see cref="Time.unscaledTime" /> (true) or the scaled
|
||||
/// <see cref="Time.time" /> value (false) which is affected by <see cref="Time.timeScale" />.
|
||||
/// </param>
|
||||
/// <param name="delayUsingEndValue">
|
||||
/// Tells whether to use the interpolation end value during the delay, if there is a
|
||||
/// <paramref name="delaySeconds" /> specified. By default it's false, which means the interpolation start value is
|
||||
/// used during the delay.
|
||||
/// </param>
|
||||
public UxrInterpolationSettings(float durationSeconds,
|
||||
float delaySeconds = 0.0f,
|
||||
UxrEasing easing = UxrEasing.Linear,
|
||||
UxrLoopMode loopMode = UxrLoopMode.None,
|
||||
float loopedDurationSeconds = -1.0f,
|
||||
bool useUnscaledTime = false,
|
||||
bool delayUsingEndValue = false)
|
||||
{
|
||||
_durationSeconds = durationSeconds;
|
||||
_delaySeconds = delaySeconds;
|
||||
_easing = easing;
|
||||
_loopMode = loopMode;
|
||||
_loopedDurationSeconds = loopedDurationSeconds;
|
||||
_useUnscaledTime = useUnscaledTime;
|
||||
_delayUsingEndValue = delayUsingEndValue;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the T value used for linear interpolations given a time value.
|
||||
/// </summary>
|
||||
/// <param name="time">Time value</param>
|
||||
/// <returns>The interpolation t value required to linearly interpolate using the current parameters</returns>
|
||||
public float GetInterpolationFactor(float time)
|
||||
{
|
||||
return UxrInterpolator.Interpolate(0.0f, 1.0f, _durationSeconds, _delaySeconds, time, _easing, _loopMode, _loopedDurationSeconds, _delayUsingEndValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given time has surpassed the interpolation duration.
|
||||
/// </summary>
|
||||
/// <param name="time">Time value</param>
|
||||
/// <returns>Boolean telling whether the interpolation has finished</returns>
|
||||
public bool CheckInterpolationHasFinished(float time)
|
||||
{
|
||||
if (LoopMode == UxrLoopMode.None && time > DelaySeconds + DurationSeconds)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LoopMode != UxrLoopMode.None && time > DelaySeconds + LoopedDurationSeconds && LoopedDurationSeconds >= 0.0f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 712731616a3841a4c8822c33b6a6aa87
|
||||
timeCreated: 1521533433
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
798
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrInterpolator.cs
vendored
Normal file
798
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrInterpolator.cs
vendored
Normal file
@@ -0,0 +1,798 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrInterpolator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Extensions.System;
|
||||
using UltimateXR.UI.UnityInputModule.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality to interpolate between values using a wide range of interpolation modes.
|
||||
/// This class also provides functionality to interpolate between 2 strings using a typewriter effect.
|
||||
/// </summary>
|
||||
public static class UxrInterpolator
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Smooths a float value using the previous value, new value and a smooth value between [0.0, 1.0].
|
||||
/// </summary>
|
||||
/// <param name="oldValue">Old value</param>
|
||||
/// <param name="newValue">New value</param>
|
||||
/// <param name="smooth">Smooth value [0.0, 1.0] where 0.0 is no smoothing and 1.0 is maximum smoothing</param>
|
||||
/// <returns>Smoothed value</returns>
|
||||
public static float SmoothDamp(float oldValue, float newValue, float smooth)
|
||||
{
|
||||
return Mathf.Lerp(oldValue, newValue, GetSmoothInterpolationValue(smooth, Time.deltaTime));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smooths a position value using the last position, new position and a smooth value between [0.0, 1.0].
|
||||
/// </summary>
|
||||
/// <param name="oldPos">Old position</param>
|
||||
/// <param name="newPos">New position</param>
|
||||
/// <param name="smooth">Smooth value [0.0, 1.0] where 0.0 is no smoothing and 1.0 is maximum smoothing</param>
|
||||
/// <returns>Smoothed position value</returns>
|
||||
public static Vector3 SmoothDampPosition(Vector3 oldPos, Vector3 newPos, float smooth)
|
||||
{
|
||||
return Vector3.Lerp(oldPos, newPos, GetSmoothInterpolationValue(smooth, Time.deltaTime));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smooths a rotation value using the last rotation, new rotation and a smooth value between [0.0, 1.0].
|
||||
/// This tries to do something similar to <see cref="Vector3.SmoothDamp" /> but for rotations.
|
||||
/// </summary>
|
||||
/// <param name="oldRot">Old rotation</param>
|
||||
/// <param name="newRot">New rotation</param>
|
||||
/// <param name="smooth">Smooth value [0.0, 1.0] where 0.0 is no smoothing and 1.0 is maximum smoothing</param>
|
||||
/// <returns>Smoothed rotation value</returns>
|
||||
public static Quaternion SmoothDampRotation(Quaternion oldRot, Quaternion newRot, float smooth)
|
||||
{
|
||||
return Quaternion.Slerp(oldRot, newRot, GetSmoothInterpolationValue(smooth, Time.deltaTime));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two floating point values using a t between range [0.0, 1.0] and a given easing.
|
||||
/// </summary>
|
||||
/// <param name="a">Start value</param>
|
||||
/// <param name="b">End value</param>
|
||||
/// <param name="t">Interpolation factor</param>
|
||||
/// <param name="easing">Easing</param>
|
||||
/// <returns>Interpolated value</returns>
|
||||
public static float Interpolate(float a, float b, float t, UxrEasing easing)
|
||||
{
|
||||
return Interpolate(a, b, 1.0f, 0.0f, Mathf.Clamp01(t), easing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two points using a t between range [0.0, 1.0] and a given easing.
|
||||
/// </summary>
|
||||
/// <param name="a">Start value</param>
|
||||
/// <param name="b">End value</param>
|
||||
/// <param name="t">Interpolation factor</param>
|
||||
/// <param name="easing">Easing</param>
|
||||
/// <returns>Interpolated value</returns>
|
||||
public static Vector3 Interpolate(Vector3 a, Vector3 b, float t, UxrEasing easing)
|
||||
{
|
||||
return Interpolate(a, b, 1.0f, 0.0f, Mathf.Clamp01(t), easing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spherically interpolates (SLERP) between two quaternions using a t between range [0.0, 1.0] and a given easing.
|
||||
/// </summary>
|
||||
/// <param name="a">Start value</param>
|
||||
/// <param name="b">End value</param>
|
||||
/// <param name="t">Interpolation factor</param>
|
||||
/// <param name="easing">Easing</param>
|
||||
/// <returns>Interpolated value</returns>
|
||||
public static Quaternion Interpolate(Quaternion a, Quaternion b, float t, UxrEasing easing)
|
||||
{
|
||||
return Interpolate(a, b, 1.0f, 0.0f, Mathf.Clamp01(t), easing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two floating point values.
|
||||
/// </summary>
|
||||
/// <param name="startValue">The start value</param>
|
||||
/// <param name="endValue">The end value</param>
|
||||
/// <param name="duration">
|
||||
/// The duration of the interpolation. If there is looping (loopMode != LoopMode.None) then it will
|
||||
/// specify the duration of a single loop
|
||||
/// </param>
|
||||
/// <param name="delay">The delay duration before the interpolation starts</param>
|
||||
/// <param name="time">
|
||||
/// The time value. This value will be clamped between [delay, delay + duration] or if there is looping
|
||||
/// (loopMode != LoopMode.None) then it will be clamped between [delay, delay + loopedDuration]. In this case
|
||||
/// duration will specify the duration of the loop
|
||||
/// </param>
|
||||
/// <param name="easing">The interpolation method to use. See @Easing</param>
|
||||
/// <param name="loopMode">Which looping mode to use. See @LoopMode</param>
|
||||
/// <param name="loopedDuration">
|
||||
/// If loopMode is not LoopMode.None then loopedDuration will specify the duration of the
|
||||
/// interpolation including all the loops. A negative value will make it loop forever.
|
||||
/// </param>
|
||||
/// <param name="delayUsingEndValue">
|
||||
/// Tells whether to use the interpolation end value during the delay, if there is a <paramref name="delay" />
|
||||
/// specified. By default it's false, which means the interpolation start value is used during the delay.
|
||||
/// </param>
|
||||
/// <returns>Interpolated floating point value</returns>
|
||||
public static float Interpolate(float startValue,
|
||||
float endValue,
|
||||
float duration,
|
||||
float delay,
|
||||
float time,
|
||||
UxrEasing easing,
|
||||
UxrLoopMode loopMode = UxrLoopMode.None,
|
||||
float loopedDuration = -1.0f,
|
||||
bool delayUsingEndValue = false)
|
||||
{
|
||||
return Interpolate(new Vector4(startValue, 0, 0, 0), new Vector4(endValue, 0, 0, 0), duration, delay, time, easing, loopMode, loopedDuration, delayUsingEndValue).x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two floating point values.
|
||||
/// </summary>
|
||||
/// <param name="startValue">The start value</param>
|
||||
/// <param name="endValue">The end value</param>
|
||||
/// <param name="time">The time value</param>
|
||||
/// <param name="settings">The interpolation settings to use</param>
|
||||
/// <returns>Interpolated floating point value</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// When <paramref name="settings" /> is null.
|
||||
/// </exception>
|
||||
public static float Interpolate(float startValue, float endValue, float time, UxrInterpolationSettings settings)
|
||||
{
|
||||
settings.ThrowIfNull(nameof(settings));
|
||||
return Interpolate(new Vector4(startValue, 0, 0, 0),
|
||||
new Vector4(endValue, 0, 0, 0),
|
||||
settings.DurationSeconds,
|
||||
settings.DelaySeconds,
|
||||
time,
|
||||
settings.Easing,
|
||||
settings.LoopMode,
|
||||
settings.LoopedDurationSeconds,
|
||||
settings.DelayUsingEndValue).x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the T value used for linear interpolations like Vector3.Lerp or Quaternion.Slerp using easing and loop.
|
||||
/// </summary>
|
||||
/// <param name="t">Value between range [0.0f, 1.0f]</param>
|
||||
/// <param name="easing">The interpolation method to use.</param>
|
||||
/// <param name="loopMode">Which looping mode to use.</param>
|
||||
/// <param name="loopedDuration">
|
||||
/// If loopMode is not LoopMode.None then loopedDuration will specify the duration of the
|
||||
/// interpolation including all the loops. A negative value will make it loop forever.
|
||||
/// </param>
|
||||
/// <returns>The t value used to linearly interpolate using the specified parameters</returns>
|
||||
public static float GetInterpolationFactor(float t, UxrEasing easing, UxrLoopMode loopMode = UxrLoopMode.None, float loopedDuration = -1.0f)
|
||||
{
|
||||
return Interpolate(0.0f, 1.0f, 1.0f, 0.0f, t, easing, loopMode, loopedDuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two <see cref="Vector4" /> values
|
||||
/// </summary>
|
||||
/// <param name="startValue">The start value</param>
|
||||
/// <param name="endValue">The end value</param>
|
||||
/// <param name="duration">
|
||||
/// The duration of the interpolation. If there is looping (loopMode != LoopMode.None) then it will
|
||||
/// specify the duration of a single loop
|
||||
/// </param>
|
||||
/// <param name="delay">The delay duration before the interpolation starts</param>
|
||||
/// <param name="time">
|
||||
/// The time value. This value will be clamped between [delay, delay + duration] or if there is looping
|
||||
/// (loopMode != LoopMode.None) then it will be clamped between [delay, delay + loopedDuration]. In this case
|
||||
/// duration will specify the duration of the loop
|
||||
/// </param>
|
||||
/// <param name="easing">The interpolation method to use. See @Easing</param>
|
||||
/// <param name="loopMode">Which looping mode to use. See @LoopMode</param>
|
||||
/// <param name="loopedDuration">
|
||||
/// If loopMode is not LoopMode.None then loopedDuration will specify the duration of the
|
||||
/// interpolation including all the loops. A negative value will make it loop forever.
|
||||
/// </param>
|
||||
/// <param name="delayUsingEndValue">
|
||||
/// Tells whether to use the interpolation end value during the delay, if there is a <paramref name="delay" />
|
||||
/// specified. By default it's false, which means the interpolation start value is used during the delay.
|
||||
/// </param>
|
||||
/// <returns>Interpolated <see cref="Vector4" /> value</returns>
|
||||
public static Vector4 Interpolate(Vector4 startValue,
|
||||
Vector4 endValue,
|
||||
float duration,
|
||||
float delay,
|
||||
float time,
|
||||
UxrEasing easing,
|
||||
UxrLoopMode loopMode = UxrLoopMode.None,
|
||||
float loopedDuration = -1.0f,
|
||||
bool delayUsingEndValue = false)
|
||||
{
|
||||
if (time < delay)
|
||||
{
|
||||
return delayUsingEndValue ? endValue : startValue;
|
||||
}
|
||||
|
||||
// Compute interpolation t
|
||||
time = Mathf.Max(delay, time);
|
||||
|
||||
if (!(loopMode != UxrLoopMode.None && loopedDuration < 0.0f))
|
||||
{
|
||||
Mathf.Min(time, delay + (loopMode == UxrLoopMode.None ? duration : loopedDuration));
|
||||
}
|
||||
|
||||
float t = duration == 0.0f ? 0.0f : (time - delay) / duration;
|
||||
|
||||
if (loopMode == UxrLoopMode.Loop)
|
||||
{
|
||||
t = t - (int)t;
|
||||
}
|
||||
else if (loopMode == UxrLoopMode.PingPong)
|
||||
{
|
||||
int loopCount = (int)t;
|
||||
|
||||
t = t - loopCount;
|
||||
|
||||
if ((loopCount & 1) == 1)
|
||||
{
|
||||
// It's going back
|
||||
t = 1.0f - t;
|
||||
}
|
||||
}
|
||||
|
||||
return InterpolateVector4(startValue, endValue, t, easing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two <see cref="Vector4" /> values
|
||||
/// </summary>
|
||||
/// <param name="startValue">The start value</param>
|
||||
/// <param name="endValue">The end value</param>
|
||||
/// <param name="time">The time value</param>
|
||||
/// <param name="settings">Interpolation settings to use</param>
|
||||
/// <returns>The interpolated <see cref="Vector4" /> value</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// When <paramref name="settings" /> is null.
|
||||
/// </exception>
|
||||
public static Vector4 Interpolate(Vector4 startValue, Vector4 endValue, float time, UxrInterpolationSettings settings)
|
||||
{
|
||||
settings.ThrowIfNull(nameof(settings));
|
||||
return Interpolate(startValue, endValue, settings.DurationSeconds, settings.DelaySeconds, time, settings.Easing, settings.LoopMode, settings.LoopedDurationSeconds, settings.DelayUsingEndValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two <see cref="Quaternion" /> values. The interpolation uses SLERP.
|
||||
/// </summary>
|
||||
/// <param name="startValue">The start value</param>
|
||||
/// <param name="endValue">The end value</param>
|
||||
/// <param name="duration">
|
||||
/// The duration of the interpolation. If there is looping (loopMode != LoopMode.None) then it will
|
||||
/// specify the duration of a single loop
|
||||
/// </param>
|
||||
/// <param name="delay">The delay duration before the interpolation starts</param>
|
||||
/// <param name="time">
|
||||
/// The time value. This value will be clamped between [delay, delay + duration] or if there is looping
|
||||
/// (loopMode != LoopMode.None) then it will be clamped between [delay, delay + loopedDuration]. In this case
|
||||
/// duration will specify the duration of the loop
|
||||
/// </param>
|
||||
/// <param name="easing">The interpolation method to use. See @Easing</param>
|
||||
/// <param name="loopMode">Which looping mode to use. See @LoopMode</param>
|
||||
/// <param name="loopedDuration">
|
||||
/// If loopMode is not LoopMode.None then loopedDuration will specify the duration of the
|
||||
/// interpolation including all the loops. A negative value will make it loop forever.
|
||||
/// </param>
|
||||
/// <param name="delayUsingEndValue">
|
||||
/// Tells whether to use the interpolation end value during the delay, if there is a <paramref name="delay" />
|
||||
/// specified. By default it's false, which means the interpolation start value is used during the delay.
|
||||
/// </param>
|
||||
/// <returns>Interpolated <see cref="Quaternion" /> value</returns>
|
||||
public static Quaternion Interpolate(Quaternion startValue,
|
||||
Quaternion endValue,
|
||||
float duration,
|
||||
float delay,
|
||||
float time,
|
||||
UxrEasing easing,
|
||||
UxrLoopMode loopMode = UxrLoopMode.None,
|
||||
float loopedDuration = -1.0f,
|
||||
bool delayUsingEndValue = false)
|
||||
{
|
||||
float t = Interpolate(0.0f, 1.0f, duration, delay, time, easing, loopMode, loopedDuration);
|
||||
return Quaternion.Slerp(startValue, endValue, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two <see cref="Quaternion" /> values. The interpolation uses SLERP.
|
||||
/// </summary>
|
||||
/// <param name="startValue">The start value</param>
|
||||
/// <param name="endValue">The end value</param>
|
||||
/// <param name="time">The time value</param>
|
||||
/// <param name="settings">Interpolation settings to use</param>
|
||||
/// <returns>Interpolated <see cref="Quaternion" /> value</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// When <paramref name="settings" /> is null.
|
||||
/// </exception>
|
||||
public static Quaternion Interpolate(Quaternion startValue,
|
||||
Quaternion endValue,
|
||||
float time,
|
||||
UxrInterpolationSettings settings)
|
||||
{
|
||||
settings.ThrowIfNull(nameof(settings));
|
||||
return Interpolate(startValue, endValue, settings.DurationSeconds, settings.DelaySeconds, time, settings.Easing, settings.LoopMode, settings.LoopedDurationSeconds, settings.DelayUsingEndValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates text using a typewriter effect.
|
||||
/// </summary>
|
||||
/// <param name="startText">Start text</param>
|
||||
/// <param name="endText">End text</param>
|
||||
/// <param name="t">Interpolation t between range [0.0, 1.0]</param>
|
||||
/// <param name="isForUnityTextUI">
|
||||
/// If true, uses the rich text properties of the Unity UI text component to add invisible characters during
|
||||
/// interpolation.
|
||||
/// These invisible characters will help keeping the final text layout so that there are no line wraps or line jumps
|
||||
/// during the interpolation.
|
||||
/// </param>
|
||||
/// <returns>Interpolated text</returns>
|
||||
/// <remarks>
|
||||
/// See <see cref="InterpolateText(float,bool,string,object[])" /> to use a format string and arguments for more
|
||||
/// advanced interpolation and numerical string interpolation.
|
||||
/// </remarks>
|
||||
public static string InterpolateText(string startText, string endText, float t, bool isForUnityTextUI)
|
||||
{
|
||||
return InterpolateText(t, isForUnityTextUI, "{0}", startText, endText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates text using a typewriter effect
|
||||
/// </summary>
|
||||
/// <param name="t">Interpolation t between range [0.0, 1.0]</param>
|
||||
/// <param name="isForUnityTextUI">
|
||||
/// If true, uses the rich text properties of the Unity UI text component to add invisible characters during
|
||||
/// interpolation.
|
||||
/// These invisible characters will help keeping the final text layout so that there are no line wraps or line jumps
|
||||
/// during the interpolation.
|
||||
/// </param>
|
||||
/// <param name="formatString">
|
||||
/// The format string (what would be the first parameter of <see cref="string.Format(string,object[])" />)
|
||||
/// </param>
|
||||
/// <param name="formatStringArgs">
|
||||
/// <para>
|
||||
/// Start/end pairs that will be interpolated and fed into <see cref="string.Format(string,object[])" />.
|
||||
/// These should be sequential pairs of values of the same type that represent the start value and the end value.
|
||||
/// For instance format could be "{0}:{1}" and args could be startArg0, endArg0, startArg1, endArg1.
|
||||
/// This will print 2 interpolated values (Arg0 and Arg1) whose start and end values are defined by the other 4
|
||||
/// parameters.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The interpolation can detect numerical values (int/float) and use numerical interpolation instead of raw string
|
||||
/// interpolation. This can be useful for effects as seen in the examples.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <example>
|
||||
/// Simple typewriter effect to write a sentence starting from empty: (t goes from 0.0 to 1.0)
|
||||
/// <code>
|
||||
/// InterpolateText(t, true, "{0}", string.Empty, "Welcome to the Matrix!");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <example>
|
||||
/// Using format string args to create an increasing score animation. The numerical values are interpolated instead of
|
||||
/// using a typewriter effect. (t goes from 0.0 to 1.0).
|
||||
/// <code>
|
||||
/// int finalScore = 999999;
|
||||
/// InterpolateText(t, true, "Final score: {0:000000}", 0, finalScore);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>Interpolated string</returns>
|
||||
public static string InterpolateText(float t, bool isForUnityTextUI, string formatString, params object[] formatStringArgs)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!(formatStringArgs.Length > 0 && formatStringArgs.Length % 2 == 0))
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.AnimationModule} {nameof(InterpolateText)}: The text has no arguments or the number of arguments is not even");
|
||||
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
#endif
|
||||
int numArgs = formatStringArgs.Length / 2;
|
||||
|
||||
object[] finalArgs = new object[numArgs];
|
||||
|
||||
for (int i = 0; i < numArgs; i++)
|
||||
{
|
||||
if (formatStringArgs[i] == null)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.AnimationModule} {nameof(InterpolateText)}: Argument " + i + " is null");
|
||||
}
|
||||
|
||||
return formatStringArgs[i + numArgs] != null ? formatStringArgs[i + numArgs].ToString() : string.Empty;
|
||||
}
|
||||
|
||||
if (formatStringArgs[i + numArgs] == null)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.AnimationModule} {nameof(InterpolateText)}: Argument " + (i + numArgs) + " is null");
|
||||
}
|
||||
|
||||
return formatStringArgs[i] != null ? formatStringArgs[i].ToString() : string.Empty;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!(formatStringArgs[i].GetType() == formatStringArgs[i + numArgs].GetType()))
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.AnimationModule} {nameof(InterpolateText)}: Type of argument " + i + " is not the same as argument " + (i + numArgs));
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
#endif
|
||||
if (formatStringArgs[i] is int)
|
||||
{
|
||||
finalArgs[i] = Mathf.RoundToInt(Mathf.Lerp((int)formatStringArgs[i], (int)formatStringArgs[i + numArgs], Mathf.Clamp01(t)));
|
||||
}
|
||||
else if (formatStringArgs[i] is float)
|
||||
{
|
||||
finalArgs[i] = Mathf.Lerp((float)formatStringArgs[i], (float)formatStringArgs[i + numArgs], Mathf.Clamp01(t));
|
||||
}
|
||||
else if (formatStringArgs[i] is string)
|
||||
{
|
||||
string a = (string)formatStringArgs[i];
|
||||
string b = (string)formatStringArgs[i + 1];
|
||||
int startChars = a.Length;
|
||||
int endChars = b.Length;
|
||||
|
||||
int numChars = Mathf.Clamp(Mathf.RoundToInt(Mathf.Lerp(startChars, endChars, t)), 0, b.Length);
|
||||
|
||||
if (Mathf.Approximately(t, 1.0f))
|
||||
{
|
||||
finalArgs[i] = b;
|
||||
}
|
||||
else if (numChars > 0)
|
||||
{
|
||||
float letterT = Mathf.Clamp01(Mathf.Repeat(t, 1.0f / endChars) * endChars);
|
||||
int changingIndex = Mathf.Max(0, numChars - 1);
|
||||
char startCar = char.IsUpper(b[changingIndex]) ? 'A' : 'a';
|
||||
char endCar = char.IsUpper(b[changingIndex]) ? 'Z' : 'z';
|
||||
|
||||
finalArgs[i] = b.Substring(0, Mathf.Max(0, numChars - 1)) + (char)(startCar + letterT * (endCar - startCar));
|
||||
|
||||
if (isForUnityTextUI)
|
||||
{
|
||||
// Add the remaining characters as "invisible" to avoid word wrapping effects during interpolation.
|
||||
|
||||
string remaining = @"<color=#00000000>" + (endChars > startChars ? b.Substring(numChars, endChars - numChars) : string.Empty) + @"</color>";
|
||||
|
||||
if (!UxrRightToLeftSupport.UseRightToLeft)
|
||||
{
|
||||
finalArgs[i] += remaining;
|
||||
}
|
||||
else
|
||||
{
|
||||
finalArgs[i] = remaining + finalArgs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
finalArgs[i] = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string.Format(formatString, finalArgs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets a framerate-independent smoothed interpolation value.
|
||||
/// </summary>
|
||||
/// <param name="smooth">Smooth value [0.0, 1.0] with 0 meaning no smoothing and 1 maximum smoothing</param>
|
||||
/// <param name="deltaTime">Elapsed time in seconds</param>
|
||||
/// <returns>Interpolation value [0.0, 1.0]</returns>
|
||||
internal static float GetSmoothInterpolationValue(float smooth, float deltaTime)
|
||||
{
|
||||
return smooth > 0.0f ? (1.0f - Mathf.Clamp01(smooth)) * deltaTime * MaxSmoothSpeed : 1.0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates a curve using interpolation. This is the core math code that does the actual interpolation.
|
||||
/// </summary>
|
||||
/// <param name="start">Initial value</param>
|
||||
/// <param name="end">End value</param>
|
||||
/// <param name="t">Interpolation t value (range [0.0f, 1.0f])</param>
|
||||
/// <param name="easing">Interpolation type</param>
|
||||
/// <returns>Interpolated value</returns>
|
||||
private static Vector4 InterpolateVector4(Vector4 start, Vector4 end, float t, UxrEasing easing)
|
||||
{
|
||||
Vector4 change = end - start;
|
||||
|
||||
switch (easing)
|
||||
{
|
||||
///////////////////////////////////////// LINEAR ////////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.Linear: return start + change * t;
|
||||
|
||||
///////////////////////////////////////// SINE //////////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.EaseInSine: return -change * Mathf.Cos(t * (Mathf.PI / 2.0f)) + change + start;
|
||||
case UxrEasing.EaseOutSine: return change * Mathf.Sin(t * (Mathf.PI / 2.0f)) + start;
|
||||
case UxrEasing.EaseInOutSine: return -change / 2.0f * (Mathf.Cos(Mathf.PI * t) - 1.0f) + start;
|
||||
case UxrEasing.EaseOutInSine when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutSine);
|
||||
case UxrEasing.EaseOutInSine: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInSine);
|
||||
|
||||
///////////////////////////////////////// QUAD //////////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.EaseInQuad: return start + t * t * change;
|
||||
case UxrEasing.EaseOutQuad: return (t - 2.0f) * t * -change + start;
|
||||
|
||||
case UxrEasing.EaseInOutQuad:
|
||||
{
|
||||
t *= 2.0f;
|
||||
|
||||
if (t < 1)
|
||||
{
|
||||
return change / 2.0f * t * t + start;
|
||||
}
|
||||
|
||||
t -= 1.0f;
|
||||
return -change / 2.0f * (t * (t - 2.0f) - 1.0f) + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseOutInQuad when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutQuad);
|
||||
case UxrEasing.EaseOutInQuad: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInQuad);
|
||||
|
||||
///////////////////////////////////////// CUBIC /////////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.EaseInCubic: return start + t * t * t * change;
|
||||
|
||||
case UxrEasing.EaseOutCubic:
|
||||
t -= 1.0f;
|
||||
return change * (t * t * t + 1.0f) + start;
|
||||
|
||||
case UxrEasing.EaseInOutCubic:
|
||||
{
|
||||
t *= 2.0f;
|
||||
|
||||
if (t < 1.0f)
|
||||
{
|
||||
return change / 2.0f * t * t * t + start;
|
||||
}
|
||||
|
||||
t -= 2.0f;
|
||||
return change / 2.0f * (t * t * t + 2.0f) + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseOutInCubic when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutCubic);
|
||||
case UxrEasing.EaseOutInCubic: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInCubic);
|
||||
|
||||
///////////////////////////////////////// QUART /////////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.EaseInQuart: return start + t * t * t * t * change;
|
||||
|
||||
case UxrEasing.EaseOutQuart:
|
||||
t -= 1.0f;
|
||||
return -change * (t * t * t * t - 1.0f) + start;
|
||||
|
||||
case UxrEasing.EaseInOutQuart:
|
||||
{
|
||||
t *= 2.0f;
|
||||
|
||||
if (t < 1.0f)
|
||||
{
|
||||
return change / 2.0f * t * t * t * t + start;
|
||||
}
|
||||
|
||||
t -= 2.0f;
|
||||
return -change / 2.0f * (t * t * t * t - 2.0f) + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseOutInQuart when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutQuart);
|
||||
case UxrEasing.EaseOutInQuart: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInQuart);
|
||||
|
||||
///////////////////////////////////////// QUINT /////////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.EaseInQuint: return start + t * t * t * t * t * change;
|
||||
|
||||
case UxrEasing.EaseOutQuint:
|
||||
t -= 1.0f;
|
||||
return change * (t * t * t * t * t + 1.0f) + start;
|
||||
|
||||
case UxrEasing.EaseInOutQuint:
|
||||
{
|
||||
t *= 2.0f;
|
||||
|
||||
if (t < 1.0f)
|
||||
{
|
||||
return change / 2.0f * t * t * t * t * t + start;
|
||||
}
|
||||
|
||||
t -= 2.0f;
|
||||
return change / 2.0f * (t * t * t * t * t + 2.0f) + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseOutInQuint when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutQuint);
|
||||
case UxrEasing.EaseOutInQuint: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInQuint);
|
||||
|
||||
///////////////////////////////////////// EXPO //////////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.EaseInExpo: return change * Mathf.Pow(2.0f, 10.0f * (t - 1.0f)) + start;
|
||||
case UxrEasing.EaseOutExpo: return change * (-Mathf.Pow(2.0f, -10.0f * t) + 1.0f) + start;
|
||||
|
||||
case UxrEasing.EaseInOutExpo:
|
||||
{
|
||||
t *= 2.0f;
|
||||
|
||||
if (t < 1.0f)
|
||||
{
|
||||
return change / 2.0f * Mathf.Pow(2.0f, 10.0f * (t - 1.0f)) + start - change * 0.0005f;
|
||||
}
|
||||
|
||||
t -= 1.0f;
|
||||
return change / 2.0f * 1.0005f * (-Mathf.Pow(2.0f, -10.0f * t) + 2.0f) + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseOutInExpo when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutExpo);
|
||||
case UxrEasing.EaseOutInExpo: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInExpo);
|
||||
|
||||
///////////////////////////////////////// CIRC //////////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.EaseInCirc: return -change * (Mathf.Sqrt(1.0f - t * t) - 1.0f) + start;
|
||||
|
||||
case UxrEasing.EaseOutCirc:
|
||||
t -= 1.0f;
|
||||
return change * Mathf.Sqrt(1.0f - t * t) + start;
|
||||
|
||||
case UxrEasing.EaseInOutCirc:
|
||||
{
|
||||
t *= 2.0f;
|
||||
|
||||
if (t < 1.0f)
|
||||
{
|
||||
return -change / 2.0f * (Mathf.Sqrt(1.0f - t * t) - 1.0f) + start;
|
||||
}
|
||||
|
||||
t -= 2.0f;
|
||||
return change / 2.0f * (Mathf.Sqrt(1.0f - t * t) + 1.0f) + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseOutInCirc when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutCirc);
|
||||
case UxrEasing.EaseOutInCirc: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInCirc);
|
||||
|
||||
///////////////////////////////////////// BACK //////////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.EaseInBack:
|
||||
{
|
||||
float s = 1.70158f;
|
||||
return change * (t * t * ((s + 1.0f) * t - s)) + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseOutBack:
|
||||
{
|
||||
float s = 1.70158f;
|
||||
t = t - 1.0f;
|
||||
return change * (t * t * ((s + 1.0f) * t + s) + 1.0f) + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseInOutBack:
|
||||
{
|
||||
float s = 1.70158f;
|
||||
t *= 2.0f;
|
||||
|
||||
if (t < 1.0f)
|
||||
{
|
||||
s *= 1.525f;
|
||||
return change / 2.0f * (t * t * ((s + 1.0f) * t - s)) + start;
|
||||
}
|
||||
|
||||
s *= 1.525f;
|
||||
t -= 2.0f;
|
||||
return change / 2.0f * (t * t * ((s + 1.0f) * t + s) + 2.0f) + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseOutInBack when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutBack);
|
||||
case UxrEasing.EaseOutInBack: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInBack);
|
||||
|
||||
///////////////////////////////////////// ELASTIC ///////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.EaseInElastic:
|
||||
{
|
||||
float p = 0.3f;
|
||||
Vector4 a = change;
|
||||
float s = p / 4.0f;
|
||||
|
||||
t -= 1.0f;
|
||||
|
||||
return -(Mathf.Pow(2.0f, 10.0f * t) * Mathf.Sin((t - s) * (2.0f * Mathf.PI) / p) * a) + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseOutElastic:
|
||||
{
|
||||
float p = 0.3f;
|
||||
Vector4 a = change;
|
||||
float s = p / 4.0f;
|
||||
return Mathf.Pow(2.0f, -10.0f * t) * Mathf.Sin((t - s) * (2.0f * Mathf.PI) / p) * a + change + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseInOutElastic:
|
||||
{
|
||||
t *= 2.0f;
|
||||
float p = 0.3f * 1.5f;
|
||||
Vector4 a = change;
|
||||
float s = p / 4.0f;
|
||||
|
||||
if (t < 1.0f)
|
||||
{
|
||||
t -= 1.0f;
|
||||
return -0.5f * (Mathf.Pow(2.0f, 10.0f * t) * Mathf.Sin((t - s) * (2.0f * Mathf.PI) / p) * a) + start;
|
||||
}
|
||||
|
||||
t -= 1.0f;
|
||||
return 0.5f * Mathf.Pow(2.0f, -10.0f * t) * Mathf.Sin((t - s) * (2.0f * Mathf.PI) / p) * a + change + start;
|
||||
}
|
||||
|
||||
case UxrEasing.EaseOutInElastic when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutElastic);
|
||||
case UxrEasing.EaseOutInElastic: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInElastic);
|
||||
|
||||
///////////////////////////////////////// BOUNCE ////////////////////////////////////////////////////
|
||||
|
||||
case UxrEasing.EaseInBounce: return change - InterpolateVector4(Vector4.zero, change, 1.0f - t, UxrEasing.EaseOutBounce) + start;
|
||||
case UxrEasing.EaseOutBounce when t < 1.0f / 2.75f: return change * (7.5625f * t * t) + start;
|
||||
|
||||
case UxrEasing.EaseOutBounce when t < 2.0f / 2.75f:
|
||||
t -= 1.5f / 2.75f;
|
||||
return change * (7.5625f * t * t + 0.75f) + start;
|
||||
|
||||
case UxrEasing.EaseOutBounce when t < 2.5 / 2.75:
|
||||
t -= 2.25f / 2.75f;
|
||||
return change * (7.5625f * t * t + 0.9375f) + start;
|
||||
|
||||
case UxrEasing.EaseOutBounce:
|
||||
t -= 2.625f / 2.75f;
|
||||
return change * (7.5625f * t * t + 0.984375f) + start;
|
||||
|
||||
case UxrEasing.EaseInOutBounce when t < 0.5f: return InterpolateVector4(Vector4.zero, change, t * 2.0f, UxrEasing.EaseInBounce) * 0.5f + start;
|
||||
case UxrEasing.EaseInOutBounce: return InterpolateVector4(Vector4.zero, change, (t - 0.5f) * 2.0f, UxrEasing.EaseOutBounce) * 0.5f + change * 0.5f + start;
|
||||
case UxrEasing.EaseOutInBounce when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutBounce);
|
||||
case UxrEasing.EaseOutInBounce: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInBounce);
|
||||
|
||||
default:
|
||||
#if UNITY_EDITOR
|
||||
if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.AnimationModule}: {nameof(UxrInterpolator)} Unknown easing mode");
|
||||
}
|
||||
#endif
|
||||
return Vector4.zero;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Constant used in SmoothDamp methods that controls the speed at which the interpolation will be performed.
|
||||
/// </summary>
|
||||
private const float MaxSmoothSpeed = 20.0f;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrInterpolator.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrInterpolator.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89bc39af06e56fd448af1d651e3406ff
|
||||
timeCreated: 1519632737
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrLoopMode.cs
vendored
Normal file
28
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrLoopMode.cs
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrLoopMode.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Supported interpolation loop modes.
|
||||
/// </summary>
|
||||
public enum UxrLoopMode
|
||||
{
|
||||
/// <summary>
|
||||
/// No looping.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Will start from the beginning again when reaching the end.
|
||||
/// </summary>
|
||||
Loop,
|
||||
|
||||
/// <summary>
|
||||
/// Will go back and forth from beginning to end.
|
||||
/// </summary>
|
||||
PingPong
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrLoopMode.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrLoopMode.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69c8ab420cd7412f874ff3ad7346f6f0
|
||||
timeCreated: 1642849559
|
||||
@@ -0,0 +1,47 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrQuaternionInterpolator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interpolator for <see cref="Quaternion" />.
|
||||
/// </summary>
|
||||
public class UxrQuaternionInterpolator : UxrVarInterpolator<Quaternion>
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="smoothDamp">Smooth damp for interpolation [0.0, 1.0] where 0.0 means no smoothing</param>
|
||||
/// <param name="useStep">Whether to use step interpolation, where the interpolation will always return the start value</param>
|
||||
public UxrQuaternionInterpolator(float smoothDamp = 0.0f, bool useStep = false) : base(smoothDamp, useStep)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrVarInterpolator<Quaternion>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Quaternion GetInterpolatedValue(Quaternion a, Quaternion b, float t)
|
||||
{
|
||||
return Quaternion.Slerp(a, b, t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Default interpolator with smoothing.
|
||||
/// </summary>
|
||||
public static readonly UxrQuaternionInterpolator DefaultInterpolator = new UxrQuaternionInterpolator(0.0f, false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4d58a25345c54847a2aa0b8ee01846b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
102
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVarInterpolator.cs
vendored
Normal file
102
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVarInterpolator.cs
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrVarInterpolator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Base for interpolator classes that interpolate with optional smooth damping.
|
||||
/// Child classes provide interpolation for different variable types.
|
||||
/// </summary>
|
||||
public abstract class UxrVarInterpolator
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether to always return the first variable when interpolating.
|
||||
/// </summary>
|
||||
public bool UseStep { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the smoothing value [0.0, 1.0]. 0 means no smoothing.
|
||||
/// </summary>
|
||||
public float SmoothDamp
|
||||
{
|
||||
get => _smoothDamp;
|
||||
set => _smoothDamp = Mathf.Clamp01(value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="smoothDamp">Smooth damp value [0.0, 1.0]</param>
|
||||
/// <param name="useStep">Whether to use step interpolation, where the interpolation will always return the start value</param>
|
||||
protected UxrVarInterpolator(float smoothDamp = 0.0f, bool useStep = false)
|
||||
{
|
||||
SmoothDamp = smoothDamp;
|
||||
UseStep = useStep;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between 2 values.
|
||||
/// </summary>
|
||||
/// <param name="a">Start value</param>
|
||||
/// <param name="b">End value</param>
|
||||
/// <param name="t">Interpolation factor [0.0, 1.0]</param>
|
||||
/// <returns>Interpolated value</returns>
|
||||
/// <remarks>
|
||||
/// The interpolated value will be affected by smoothing if the object was initialized with a smoothDamp value
|
||||
/// greater than 0.
|
||||
/// </remarks>
|
||||
public abstract object Interpolate(object a, object b, float t);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the "memory" of the smooth damp effect, so that the interpolation will restart from the next time
|
||||
/// <see cref="Interpolate" /> is called.
|
||||
/// </summary>
|
||||
public void RestartSmoothDamp()
|
||||
{
|
||||
RequiresSmoothDampRestart = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Clears the smooth damp restart variable.
|
||||
/// </summary>
|
||||
protected void ClearSmoothDampRestart()
|
||||
{
|
||||
RequiresSmoothDampRestart = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the smooth damp needs to be restarted the next time <see cref="Interpolate" /> is called.
|
||||
/// </summary>
|
||||
protected bool RequiresSmoothDampRestart { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private float _smoothDamp;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
11
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVarInterpolator.cs.meta
vendored
Normal file
11
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVarInterpolator.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5837862ce9de7a4b83241489dea3040
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
103
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVarInterpolator_1.cs
vendored
Normal file
103
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVarInterpolator_1.cs
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrVarInterpolator_1.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic base for interpolator classes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type the class will interpolate</typeparam>
|
||||
public abstract class UxrVarInterpolator<T> : UxrVarInterpolator
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="smoothDamp">Smooth damp value [0.0, 1.0]</param>
|
||||
/// <param name="useStep">Whether to use step interpolation, where the interpolation will always return the start value</param>
|
||||
protected UxrVarInterpolator(float smoothDamp, bool useStep) : base(smoothDamp, useStep)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides UxrVarInterpolator
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Interpolate(object a, object b, float t)
|
||||
{
|
||||
if (UseStep)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
if (a is not T ta)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
if (b is not T tb)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return Interpolate(ta, tb, t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between 2 values.
|
||||
/// </summary>
|
||||
/// <param name="a">Start value</param>
|
||||
/// <param name="b">End value</param>
|
||||
/// <param name="t">Interpolation factor [0.0, 1.0]</param>
|
||||
/// <returns>Interpolated value</returns>
|
||||
/// <remarks>
|
||||
/// The interpolated value will be affected by smoothing if the object was initialized with a smoothDamp value
|
||||
/// greater than 0
|
||||
/// </remarks>
|
||||
public T Interpolate(T a, T b, float t)
|
||||
{
|
||||
T result = GetInterpolatedValue(a, b, t);
|
||||
|
||||
if (!RequiresSmoothDampRestart && SmoothDamp > 0.0f)
|
||||
{
|
||||
result = GetInterpolatedValue(_lastValue, result, UxrInterpolator.GetSmoothInterpolationValue(SmoothDamp, Time.deltaTime));
|
||||
}
|
||||
|
||||
ClearSmoothDampRestart();
|
||||
|
||||
_lastValue = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between 2 values. To be interpolated in child classes.
|
||||
/// </summary>
|
||||
/// <param name="a">Start value</param>
|
||||
/// <param name="b">End value</param>
|
||||
/// <param name="t">Interpolation factor [0.0, 1.0]</param>
|
||||
/// <returns>Interpolated value</returns>
|
||||
protected abstract T GetInterpolatedValue(T a, T b, float t);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private T _lastValue;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 771aad43346641e6800a127a336ab6f2
|
||||
timeCreated: 1709572336
|
||||
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVector2Interpolator.cs
vendored
Normal file
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVector2Interpolator.cs
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrVector2Interpolator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interpolator for <see cref="Vector2" />.
|
||||
/// </summary>
|
||||
public class UxrVector2Interpolator : UxrVarInterpolator<Vector2>
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="smoothDamp">Smooth damp for interpolation [0.0, 1.0] where 0.0 means no smoothing</param>
|
||||
/// <param name="useStep">Whether to use step interpolation, where the interpolation will always return the start value</param>
|
||||
public UxrVector2Interpolator(float smoothDamp = 0.0f, bool useStep = false) : base(smoothDamp, useStep)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrVarInterpolator<Vector2>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Vector2 GetInterpolatedValue(Vector2 a, Vector2 b, float t)
|
||||
{
|
||||
return Vector2.Lerp(a, b, t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Default interpolator with smoothing.
|
||||
/// </summary>
|
||||
public static readonly UxrVector2Interpolator DefaultInterpolator = new UxrVector2Interpolator(0.0f, false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eff4611b1ef6b94499a241654ef279db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVector3Interpolator.cs
vendored
Normal file
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVector3Interpolator.cs
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrVector3Interpolator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interpolator for <see cref="Vector3" />.
|
||||
/// </summary>
|
||||
public class UxrVector3Interpolator : UxrVarInterpolator<Vector3>
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="smoothDamp">Smooth damp for interpolation [0.0, 1.0] where 0.0 means no smoothing</param>
|
||||
/// <param name="useStep">Whether to use step interpolation, where the interpolation will always return the start value</param>
|
||||
public UxrVector3Interpolator(float smoothDamp = 0.0f, bool useStep = false) : base(smoothDamp, useStep)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrVarInterpolator<Vector3>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Vector3 GetInterpolatedValue(Vector3 a, Vector3 b, float t)
|
||||
{
|
||||
return Vector3.Lerp(a, b, t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Default interpolator with smoothing.
|
||||
/// </summary>
|
||||
public static readonly UxrVector3Interpolator DefaultInterpolator = new UxrVector3Interpolator(0.0f, false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c94c2ed8d15094e429c52fc69cac5256
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVector4Interpolator.cs
vendored
Normal file
47
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Interpolation/UxrVector4Interpolator.cs
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrVector4Interpolator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Interpolation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interpolator for <see cref="Vector4" />.
|
||||
/// </summary>
|
||||
public class UxrVector4Interpolator : UxrVarInterpolator<Vector4>
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="smoothDamp">Smooth damp for interpolation [0.0, 1.0] where 0.0 means no smoothing</param>
|
||||
/// <param name="useStep">Whether to use step interpolation, where the interpolation will always return the start value</param>
|
||||
public UxrVector4Interpolator(float smoothDamp = 0.0f, bool useStep = false) : base(smoothDamp, useStep)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrVarInterpolator<Vector4>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Vector4 GetInterpolatedValue(Vector4 a, Vector4 b, float t)
|
||||
{
|
||||
return Vector4.Lerp(a, b, t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Default interpolator with smoothing.
|
||||
/// </summary>
|
||||
public static readonly UxrVector4Interpolator DefaultInterpolator = new UxrVector4Interpolator(0.0f, false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31352d3b132104a4b8f58490f88c8253
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Lights.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Lights.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 731650480b7d431eb51cb9c5a685a927
|
||||
timeCreated: 1643799599
|
||||
265
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Lights/UxrAnimatedLightIntensity.cs
vendored
Normal file
265
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Lights/UxrAnimatedLightIntensity.cs
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAnimatedLightIntensity.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Animation.Interpolation;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Lights
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to animate a light's intensity.
|
||||
/// </summary>
|
||||
public class UxrAnimatedLightIntensity : UxrAnimatedComponent<UxrAnimatedLightIntensity>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Light _light;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.Speed" />
|
||||
public new float Speed
|
||||
{
|
||||
get => base.Speed.x;
|
||||
set => base.Speed = ToVector4(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.InterpolatedValueStart" />
|
||||
public new float InterpolatedValueStart
|
||||
{
|
||||
get => base.InterpolatedValueStart.x;
|
||||
set => base.InterpolatedValueStart = ToVector4(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.InterpolatedValueEnd" />
|
||||
public new float InterpolatedValueEnd
|
||||
{
|
||||
get => base.InterpolatedValueEnd.x;
|
||||
set => base.InterpolatedValueEnd = ToVector4(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.InterpolatedValueWhenDisabled" />
|
||||
public new float InterpolatedValueWhenDisabled
|
||||
{
|
||||
get => base.InterpolatedValueWhenDisabled.x;
|
||||
set => base.InterpolatedValueWhenDisabled = ToVector4(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.NoiseValueMin" />
|
||||
public new float NoiseValueMin
|
||||
{
|
||||
get => base.NoiseValueMin.x;
|
||||
set => base.NoiseValueMin = ToVector4(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.NoiseValueMax" />
|
||||
public new float NoiseValueMax
|
||||
{
|
||||
get => base.NoiseValueMax.x;
|
||||
set => base.NoiseValueMax = ToVector4(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.NoiseValueStart" />
|
||||
public new float NoiseValueStart
|
||||
{
|
||||
get => base.NoiseValueStart.x;
|
||||
set => base.NoiseValueStart = ToVector4(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.NoiseValueEnd" />
|
||||
public new float NoiseValueEnd
|
||||
{
|
||||
get => base.NoiseValueEnd.x;
|
||||
set => base.NoiseValueEnd = ToVector4(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.NoiseFrequency" />
|
||||
public new float NoiseFrequency
|
||||
{
|
||||
get => base.NoiseFrequency.x;
|
||||
set => base.NoiseFrequency = ToVector4(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.NoiseOffset" />
|
||||
public new float NoiseOffset
|
||||
{
|
||||
get => base.NoiseOffset.x;
|
||||
set => base.NoiseOffset = ToVector4(value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Starts an animation at a constant speed
|
||||
/// </summary>
|
||||
/// <param name="light">The light component to apply the animation to</param>
|
||||
/// <param name="speed">
|
||||
/// The animation speed. For int/float values use .x, for Vector2 use x and y. For
|
||||
/// Vector3 use x, y, z. etc.
|
||||
/// </param>
|
||||
/// <param name="useUnscaledTime">
|
||||
/// If it is true then Time.unscaledTime will be used
|
||||
/// to count seconds. By default it is false meaning Time.time will be used instead.
|
||||
/// Time.time is affected by Time.timeScale which in many cases is used for application pauses
|
||||
/// or bullet-time effects, while Time.unscaledTime is not.
|
||||
/// </param>
|
||||
/// <returns>Animation component</returns>
|
||||
public static UxrAnimatedLightIntensity Animate(Light light, float speed, bool useUnscaledTime = false)
|
||||
{
|
||||
UxrAnimatedLightIntensity component = light.gameObject.GetOrAddComponent<UxrAnimatedLightIntensity>();
|
||||
|
||||
if (component)
|
||||
{
|
||||
component._light = light;
|
||||
component.AnimationMode = UxrAnimationMode.Speed;
|
||||
component.Speed = speed;
|
||||
component.UseUnscaledTime = useUnscaledTime;
|
||||
component.StartTimer();
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts an animation using an interpolation curve
|
||||
/// </summary>
|
||||
/// <param name="light">The light component to apply the animation to</param>
|
||||
/// <param name="startValue">The start intensity value</param>
|
||||
/// <param name="endValue">The end intensity value</param>
|
||||
/// <param name="settings">The interpolation settings with the curve parameters</param>
|
||||
/// <param name="finishedCallback">Optional callback when the animation finished</param>
|
||||
/// <returns>Animation component</returns>
|
||||
public static UxrAnimatedLightIntensity AnimateInterpolation(Light light, float startValue, float endValue, UxrInterpolationSettings settings, Action finishedCallback = null)
|
||||
{
|
||||
UxrAnimatedLightIntensity component = light.gameObject.GetOrAddComponent<UxrAnimatedLightIntensity>();
|
||||
|
||||
if (component)
|
||||
{
|
||||
component._light = light;
|
||||
component.AnimationMode = UxrAnimationMode.Interpolate;
|
||||
component.InterpolatedValueStart = startValue;
|
||||
component.InterpolatedValueEnd = endValue;
|
||||
component.InterpolationSettings = settings;
|
||||
component._finishedCallback = finishedCallback;
|
||||
component.StartTimer();
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts an animation using noise.
|
||||
/// </summary>
|
||||
/// <param name="light">The light component to apply the animation to</param>
|
||||
/// <param name="noiseTimeStart">The time in seconds the noise will start (Time.time or Time.unscaledTime value)</param>
|
||||
/// <param name="noiseTimeDuration">The duration in seconds of the noise animation</param>
|
||||
/// <param name="noiseValueStart">The start intensity value</param>
|
||||
/// <param name="noiseValueEnd">The end intensity value</param>
|
||||
/// <param name="noiseValueMin">The minimum intensity value for the noise</param>
|
||||
/// <param name="noiseValueMax">The maximum intensity value for the noise</param>
|
||||
/// <param name="noiseValueFrequency">The noise frequency</param>
|
||||
/// <param name="useUnscaledTime">If true it will use Time.unscaledTime, if false it will use Time.time</param>
|
||||
/// <param name="finishedCallback">Optional callback when the animation finished</param>
|
||||
/// <returns>Animation component</returns>
|
||||
public static UxrAnimatedLightIntensity AnimateNoise(Light light,
|
||||
float noiseTimeStart,
|
||||
float noiseTimeDuration,
|
||||
float noiseValueStart,
|
||||
float noiseValueEnd,
|
||||
float noiseValueMin,
|
||||
float noiseValueMax,
|
||||
float noiseValueFrequency,
|
||||
bool useUnscaledTime = false,
|
||||
Action finishedCallback = null)
|
||||
{
|
||||
UxrAnimatedLightIntensity component = light.gameObject.GetOrAddComponent<UxrAnimatedLightIntensity>();
|
||||
|
||||
if (component)
|
||||
{
|
||||
component._light = light;
|
||||
component.AnimationMode = UxrAnimationMode.Noise;
|
||||
component.NoiseTimeStart = noiseTimeStart;
|
||||
component.NoiseDurationSeconds = noiseTimeDuration;
|
||||
component.NoiseValueStart = noiseValueStart;
|
||||
component.NoiseValueEnd = noiseValueEnd;
|
||||
component.NoiseValueMin = noiseValueMin;
|
||||
component.NoiseValueMax = noiseValueMax;
|
||||
component.NoiseFrequency = noiseValueFrequency;
|
||||
component.UseUnscaledTime = useUnscaledTime;
|
||||
component._finishedCallback = finishedCallback;
|
||||
component.StartTimer();
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Stores the initial light intensity
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
_initialIntensity = _light != null ? _light.intensity : 0.0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.OnFinished" />
|
||||
protected override void OnFinished(UxrAnimatedLightIntensity anim)
|
||||
{
|
||||
base.OnFinished(anim);
|
||||
_finishedCallback?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrAnimatedComponent<UxrAnimatedLightIntensity>
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.RestoreOriginalValue" />
|
||||
protected override void RestoreOriginalValue()
|
||||
{
|
||||
if (_light != null)
|
||||
{
|
||||
_light.intensity = _initialIntensity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.GetParameterValue" />
|
||||
protected override Vector4 GetParameterValue()
|
||||
{
|
||||
return ToVector4(_light != null ? _light.intensity : 0.0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.SetParameterValue" />
|
||||
protected override void SetParameterValue(Vector4 value)
|
||||
{
|
||||
if (_light != null)
|
||||
{
|
||||
_light.intensity = value.x;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private float _initialIntensity;
|
||||
private Action _finishedCallback;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Lights/UxrAnimatedLightIntensity.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Lights/UxrAnimatedLightIntensity.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9bfd497bfea50143a9aaebc8203630b
|
||||
timeCreated: 1516185390
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials.meta
vendored
Normal file
8
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 315df74e11e8d5440821cbd873a70ec2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
702
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrAnimatedMaterial.cs
vendored
Normal file
702
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrAnimatedMaterial.cs
vendored
Normal file
@@ -0,0 +1,702 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAnimatedMaterial.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Animation.Interpolation;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Materials
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to animate material properties.
|
||||
/// </summary>
|
||||
public class UxrAnimatedMaterial : UxrAnimatedComponent<UxrAnimatedMaterial>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _animateSelf = true;
|
||||
[SerializeField] private GameObject _targetGameObject;
|
||||
[SerializeField] private int _materialSlot;
|
||||
[SerializeField] private UxrMaterialMode _materialMode = UxrMaterialMode.InstanceOnly;
|
||||
[SerializeField] private bool _restoreWhenFinished = true;
|
||||
[SerializeField] private UxrMaterialParameterType _parameterType = UxrMaterialParameterType.Vector4;
|
||||
[SerializeField] private string _parameterName = "";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// The default blink frequency
|
||||
/// </summary>
|
||||
public const float DefaultBlinkFrequency = 3.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the original material value should be restored when finished.
|
||||
/// </summary>
|
||||
public bool RestoreWhenFinished
|
||||
{
|
||||
get => _restoreWhenFinished;
|
||||
set => _restoreWhenFinished = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the animation will be applied to the GameObject where the component is, or an external one.
|
||||
/// </summary>
|
||||
public bool AnimateSelf
|
||||
{
|
||||
get => _animateSelf;
|
||||
set => _animateSelf = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target GameObject when <see cref="AnimateSelf" /> is true.
|
||||
/// </summary>
|
||||
public GameObject TargetGameObject
|
||||
{
|
||||
get => _targetGameObject;
|
||||
set => _targetGameObject = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the material slot to apply the material animation to.
|
||||
/// </summary>
|
||||
public int MaterialSlot
|
||||
{
|
||||
get => _materialSlot;
|
||||
set => _materialSlot = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the material mode, whether to use the instanced material or the shared material.
|
||||
/// </summary>
|
||||
public UxrMaterialMode MaterialMode
|
||||
{
|
||||
get => _materialMode;
|
||||
set => _materialMode = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the material's parameter type.
|
||||
/// </summary>
|
||||
public UxrMaterialParameterType ParameterType
|
||||
{
|
||||
get => _parameterType;
|
||||
set => _parameterType = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the material's parameter name.
|
||||
/// </summary>
|
||||
public string ParameterName
|
||||
{
|
||||
get => _parameterName;
|
||||
set => _parameterName = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Starts an animation at a constant speed
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The GameObject with the material to apply the animation to</param>
|
||||
/// <param name="materialSlot">The renderer material slot where the material is</param>
|
||||
/// <param name="materialMode">
|
||||
/// The material mode. Use instance to animate the material of a single object,
|
||||
/// use shared to also affect all other objects that share the same material
|
||||
/// </param>
|
||||
/// <param name="parameterType">Selects the type of the parameter to animate</param>
|
||||
/// <param name="parameterName">
|
||||
/// Selects the name of the parameter to animate.
|
||||
/// This name is the name in the shader, not in the inspector!
|
||||
/// </param>
|
||||
/// <param name="speed">
|
||||
/// The animation speed. For int/float values use .x, for Vector2 use x and y.
|
||||
/// For Vector3 use x, y, z. etc.
|
||||
/// </param>
|
||||
/// <param name="durationSeconds">
|
||||
/// Duration in seconds of the animation. Use a negative value to keep updating until stopped
|
||||
/// manually.
|
||||
/// </param>
|
||||
/// <param name="useUnscaledTime">
|
||||
/// If it is true then Time.unscaledTime will be used
|
||||
/// to count seconds. By default it is false meaning Time.time will be used instead.
|
||||
/// Time.time is affected by Time.timeScale which in many cases is used for application pauses
|
||||
/// or bullet-time effects, while Time.unscaledTime is not.
|
||||
/// </param>
|
||||
/// <param name="finishedCallback">Optional callback when the animation finished</param>
|
||||
/// <returns>The animation component</returns>
|
||||
public static UxrAnimatedMaterial Animate(GameObject gameObject,
|
||||
int materialSlot,
|
||||
UxrMaterialMode materialMode,
|
||||
UxrMaterialParameterType parameterType,
|
||||
string parameterName,
|
||||
Vector4 speed,
|
||||
float durationSeconds = -1.0f,
|
||||
bool useUnscaledTime = false,
|
||||
Action finishedCallback = null)
|
||||
{
|
||||
UxrAnimatedMaterial component = gameObject.GetOrAddComponent<UxrAnimatedMaterial>();
|
||||
|
||||
if (component)
|
||||
{
|
||||
component._restoreWhenFinished = false;
|
||||
component._animateSelf = true;
|
||||
component._materialSlot = materialSlot;
|
||||
component._materialMode = materialMode;
|
||||
component._parameterType = parameterType;
|
||||
component._parameterName = parameterName;
|
||||
component.AnimationMode = UxrAnimationMode.Speed;
|
||||
component.Speed = speed;
|
||||
component.UseUnscaledTime = useUnscaledTime;
|
||||
component.SpeedDurationSeconds = durationSeconds;
|
||||
component._finishedCallback = finishedCallback;
|
||||
component.Initialize();
|
||||
component.StartTimer();
|
||||
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a material parameter animation using an interpolation curve
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The GameObject with the material to apply the animation to</param>
|
||||
/// <param name="materialSlot">The renderer material slot where the material is</param>
|
||||
/// <param name="materialMode">
|
||||
/// The material mode. Use instance to animate the material of a single object,
|
||||
/// use shared to also affect all other objects that share the same material
|
||||
/// </param>
|
||||
/// <param name="parameterType">Selects the type of the parameter to animate</param>
|
||||
/// <param name="parameterName">
|
||||
/// Selects the name of the parameter to animate.
|
||||
/// This name is the name in the shader, not in the inspector!
|
||||
/// </param>
|
||||
/// <param name="startValue">
|
||||
/// The start value. For int/float values use .x, for Vector2 use x and y.
|
||||
/// For Vector3 use x, y, z. etc.
|
||||
/// </param>
|
||||
/// <param name="endValue">
|
||||
/// The end value. For int/float values use .x, for Vector2 use x and y.
|
||||
/// For Vector3 use x, y, z. etc.
|
||||
/// </param>
|
||||
/// <param name="settings">The interpolation settings with the curve parameters</param>
|
||||
/// <param name="finishedCallback">Optional callback when the animation finished</param>
|
||||
/// <returns>The animation component</returns>
|
||||
public static UxrAnimatedMaterial AnimateInterpolation(GameObject gameObject,
|
||||
int materialSlot,
|
||||
UxrMaterialMode materialMode,
|
||||
UxrMaterialParameterType parameterType,
|
||||
string parameterName,
|
||||
Vector4 startValue,
|
||||
Vector4 endValue,
|
||||
UxrInterpolationSettings settings,
|
||||
Action finishedCallback = null)
|
||||
{
|
||||
UxrAnimatedMaterial component = gameObject.GetOrAddComponent<UxrAnimatedMaterial>();
|
||||
|
||||
if (component)
|
||||
{
|
||||
component._restoreWhenFinished = false;
|
||||
component._animateSelf = true;
|
||||
component._materialSlot = materialSlot;
|
||||
component._materialMode = materialMode;
|
||||
component._parameterType = parameterType;
|
||||
component._parameterName = parameterName;
|
||||
component.AnimationMode = UxrAnimationMode.Interpolate;
|
||||
component.InterpolatedValueStart = startValue;
|
||||
component.InterpolatedValueEnd = endValue;
|
||||
component.InterpolationSettings = settings;
|
||||
component._finishedCallback = finishedCallback;
|
||||
component.Initialize();
|
||||
component.StartTimer();
|
||||
component.InterpolatedValueWhenDisabled = component._valueBeforeAnimation;
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a material parameter animation using noise
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The GameObject with the material to apply the animation to</param>
|
||||
/// <param name="materialSlot">The renderer material slot where the material is</param>
|
||||
/// <param name="materialMode">
|
||||
/// The material mode. Use instance to animate the material of a single object,
|
||||
/// use shared to also affect all other objects that share the same material
|
||||
/// </param>
|
||||
/// <param name="parameterType">Selects the type of the parameter to animate</param>
|
||||
/// <param name="parameterName">
|
||||
/// Selects the name of the parameter to animate.
|
||||
/// This name is the name in the shader, not in the inspector!
|
||||
/// </param>
|
||||
/// <param name="noiseTimeStart">The time in seconds the noise will start (Time.time or Time.unscaledTime value)</param>
|
||||
/// <param name="noiseTimeDuration">The duration in seconds of the noise animation</param>
|
||||
/// <param name="noiseValueStart">
|
||||
/// The start value. For int/float values use .x, for Vector2 use x and y.
|
||||
/// For Vector3 use x, y, z. etc.
|
||||
/// </param>
|
||||
/// <param name="noiseValueEnd">
|
||||
/// The end value. For int/float values use .x, for Vector2 use x and y.
|
||||
/// For Vector3 use x, y, z. etc.
|
||||
/// </param>
|
||||
/// <param name="noiseValueMin">
|
||||
/// The minimum intensity value for the noise. For int/float values use .x, for Vector2 use x and y.
|
||||
/// For Vector3 use x, y, z. etc.
|
||||
/// </param>
|
||||
/// <param name="noiseValueMax">
|
||||
/// The maximum intensity value for the noise. For int/float values use .x, for Vector2 use x and y.
|
||||
/// For Vector3 use x, y, z. etc.
|
||||
/// </param>
|
||||
/// <param name="noiseValueFrequency">
|
||||
/// The noise frequency. For int/float values use .x, for Vector2 use x and y.
|
||||
/// For Vector3 use x, y, z. etc.
|
||||
/// </param>
|
||||
/// <param name="finishedCallback">Optional callback when the animation finished</param>
|
||||
/// <param name="useUnscaledTime">If true it will use Time.unscaledTime, if false it will use Time.time</param>
|
||||
public static void AnimateNoise(GameObject gameObject,
|
||||
int materialSlot,
|
||||
UxrMaterialMode materialMode,
|
||||
UxrMaterialParameterType parameterType,
|
||||
string parameterName,
|
||||
float noiseTimeStart,
|
||||
float noiseTimeDuration,
|
||||
Vector4 noiseValueStart,
|
||||
Vector4 noiseValueEnd,
|
||||
Vector4 noiseValueMin,
|
||||
Vector4 noiseValueMax,
|
||||
Vector4 noiseValueFrequency,
|
||||
bool useUnscaledTime = false,
|
||||
Action finishedCallback = null)
|
||||
{
|
||||
UxrAnimatedMaterial component = gameObject.GetOrAddComponent<UxrAnimatedMaterial>();
|
||||
|
||||
if (component)
|
||||
{
|
||||
component._restoreWhenFinished = false;
|
||||
component._animateSelf = true;
|
||||
component._materialSlot = materialSlot;
|
||||
component._materialMode = materialMode;
|
||||
component._parameterType = parameterType;
|
||||
component._parameterName = parameterName;
|
||||
component.AnimationMode = UxrAnimationMode.Noise;
|
||||
component.NoiseTimeStart = noiseTimeStart;
|
||||
component.NoiseDurationSeconds = noiseTimeDuration;
|
||||
component.NoiseValueStart = noiseValueStart;
|
||||
component.NoiseValueEnd = noiseValueEnd;
|
||||
component.NoiseValueMin = noiseValueMin;
|
||||
component.NoiseValueMax = noiseValueMax;
|
||||
component.NoiseFrequency = noiseValueFrequency;
|
||||
component.UseUnscaledTime = useUnscaledTime;
|
||||
component._finishedCallback = finishedCallback;
|
||||
component.Initialize();
|
||||
component.StartTimer();
|
||||
component.InterpolatedValueWhenDisabled = component._valueBeforeAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts animating a GameObject's material making one if its float parameters blink.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">GameObject whose material to animate</param>
|
||||
/// <param name="varNameFloat">The float var name</param>
|
||||
/// <param name="valueMin">The minimum float value in the blink</param>
|
||||
/// <param name="valueMax">The maximum float value in the blink</param>
|
||||
/// <param name="blinkFrequency">The blinking frequency</param>
|
||||
/// <param name="durationSeconds">
|
||||
/// The duration in seconds. Use a negative value to keep keep blinking until stopping
|
||||
/// manually.
|
||||
/// </param>
|
||||
/// <param name="materialMode">The material mode</param>
|
||||
/// <param name="finishedCallback">Optional callback when the animation finished</param>
|
||||
/// <returns>Material animation component</returns>
|
||||
public static UxrAnimatedMaterial AnimateFloatBlink(GameObject gameObject,
|
||||
string varNameFloat,
|
||||
float valueMin = 0.0f,
|
||||
float valueMax = 1.0f,
|
||||
float blinkFrequency = DefaultBlinkFrequency,
|
||||
float durationSeconds = -1.0f,
|
||||
UxrMaterialMode materialMode = UxrMaterialMode.InstanceOnly,
|
||||
Action finishedCallback = null)
|
||||
{
|
||||
return AnimateInterpolation(gameObject,
|
||||
0,
|
||||
materialMode,
|
||||
UxrMaterialParameterType.Float,
|
||||
varNameFloat,
|
||||
Vector4.one * valueMin,
|
||||
Vector4.one * valueMax,
|
||||
new UxrInterpolationSettings(1.0f / blinkFrequency * 0.5f, 0.0f, UxrEasing.EaseInOutSine, UxrLoopMode.PingPong, durationSeconds),
|
||||
finishedCallback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts animating a GameObject's material making one if its color parameters blink.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">GameObject whose material to animate</param>
|
||||
/// <param name="varNameColor">The float var name</param>
|
||||
/// <param name="colorOff">The minimum color value in the blink</param>
|
||||
/// <param name="colorOn">The maximum color value in the blink</param>
|
||||
/// <param name="blinkFrequency">The blinking frequency</param>
|
||||
/// <param name="durationSeconds">
|
||||
/// The duration in seconds. Use a negative value to keep keep blinking until stopping
|
||||
/// manually.
|
||||
/// </param>
|
||||
/// <param name="materialMode">The material mode</param>
|
||||
/// <param name="finishedCallback">Optional callback when the animation finished</param>
|
||||
/// <returns>Material animation component</returns>
|
||||
public static UxrAnimatedMaterial AnimateBlinkColor(GameObject gameObject,
|
||||
string varNameColor,
|
||||
Color colorOff,
|
||||
Color colorOn,
|
||||
float blinkFrequency = DefaultBlinkFrequency,
|
||||
float durationSeconds = -1.0f,
|
||||
UxrMaterialMode materialMode = UxrMaterialMode.InstanceOnly,
|
||||
Action finishedCallback = null)
|
||||
{
|
||||
return AnimateInterpolation(gameObject,
|
||||
0,
|
||||
materialMode,
|
||||
UxrMaterialParameterType.Color,
|
||||
varNameColor,
|
||||
colorOff,
|
||||
colorOn,
|
||||
new UxrInterpolationSettings(1.0f / blinkFrequency * 0.5f, 0.0f, UxrEasing.EaseInOutSine, UxrLoopMode.PingPong, durationSeconds),
|
||||
finishedCallback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the original (shared) material. This may have some performance advantages.
|
||||
/// </summary>
|
||||
public void RestoreOriginalSharedMaterial()
|
||||
{
|
||||
if (_renderer)
|
||||
{
|
||||
if (_materialSlot == 0)
|
||||
{
|
||||
_renderer.sharedMaterial = _originalMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
_renderer.sharedMaterials = _originalMaterials;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes internal variables
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <inheritdoc cref="UxrAnimatedComponent{T}.OnFinished" />
|
||||
protected override void OnFinished(UxrAnimatedMaterial anim)
|
||||
{
|
||||
base.OnFinished(anim);
|
||||
|
||||
if (RestoreWhenFinished && MaterialMode == UxrMaterialMode.InstanceOnly)
|
||||
{
|
||||
RestoreOriginalSharedMaterial();
|
||||
}
|
||||
|
||||
_finishedCallback?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrAnimatedComponent<UxrAnimatedMaterial>
|
||||
|
||||
/// <summary>
|
||||
/// Restores the original value before the animation started.
|
||||
/// </summary>
|
||||
protected override void RestoreOriginalValue()
|
||||
{
|
||||
if (_valueBeforeAnimationInitialized)
|
||||
{
|
||||
SetParameterValue(_valueBeforeAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter value from the material
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Vector4 containing the value. This value may not use all components depending on the parameter type.
|
||||
/// </returns>
|
||||
protected override Vector4 GetParameterValue()
|
||||
{
|
||||
if (_renderer && _materialSlot < _renderer.sharedMaterials.Length)
|
||||
{
|
||||
switch (_parameterType)
|
||||
{
|
||||
case UxrMaterialParameterType.Int:
|
||||
|
||||
if (_materialMode == UxrMaterialMode.InstanceOnly)
|
||||
{
|
||||
return _materialSlot == 0 ? new Vector4(_renderer.material.GetInt(_parameterName), 0, 0, 0) : new Vector4(_renderer.materials[_materialSlot].GetInt(_parameterName), 0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _materialSlot == 0 ? new Vector4(_renderer.sharedMaterial.GetInt(_parameterName), 0, 0, 0) : new Vector4(_renderer.sharedMaterials[_materialSlot].GetInt(_parameterName), 0, 0, 0);
|
||||
}
|
||||
|
||||
case UxrMaterialParameterType.Float:
|
||||
|
||||
if (_materialMode == UxrMaterialMode.InstanceOnly)
|
||||
{
|
||||
return _materialSlot == 0 ? new Vector4(_renderer.material.GetFloat(_parameterName), 0, 0, 0) : new Vector4(_renderer.materials[_materialSlot].GetFloat(_parameterName), 0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _materialSlot == 0 ? new Vector4(_renderer.sharedMaterial.GetFloat(_parameterName), 0, 0, 0) : new Vector4(_renderer.sharedMaterials[_materialSlot].GetFloat(_parameterName), 0, 0, 0);
|
||||
}
|
||||
|
||||
case UxrMaterialParameterType.Vector2:
|
||||
case UxrMaterialParameterType.Vector3:
|
||||
case UxrMaterialParameterType.Vector4:
|
||||
|
||||
if (_materialMode == UxrMaterialMode.InstanceOnly)
|
||||
{
|
||||
return _materialSlot == 0 ? _renderer.material.GetVector(_parameterName) : _renderer.materials[_materialSlot].GetVector(_parameterName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _materialSlot == 0 ? _renderer.sharedMaterial.GetVector(_parameterName) : _renderer.sharedMaterials[_materialSlot].GetVector(_parameterName);
|
||||
}
|
||||
|
||||
case UxrMaterialParameterType.Color:
|
||||
|
||||
if (_materialMode == UxrMaterialMode.InstanceOnly)
|
||||
{
|
||||
return _materialSlot == 0 ? _renderer.material.GetColor(_parameterName) : _renderer.materials[_materialSlot].GetColor(_parameterName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _materialSlot == 0 ? _renderer.sharedMaterial.GetColor(_parameterName) : _renderer.sharedMaterials[_materialSlot].GetColor(_parameterName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Warnings)
|
||||
{
|
||||
Debug.LogWarning($"{UxrConstants.AnimationModule} Material slot {_materialSlot} for {this.GetPathUnderScene()} is not valid");
|
||||
}
|
||||
|
||||
return Vector4.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the material parameter value
|
||||
/// </summary>
|
||||
/// <param name="value">
|
||||
/// Vector4 containing the value. This value may not use all components depending on the parameter type
|
||||
/// </param>
|
||||
protected override void SetParameterValue(Vector4 value)
|
||||
{
|
||||
Material[] materials = null;
|
||||
|
||||
if (_renderer && _materialSlot < _renderer.sharedMaterials.Length)
|
||||
{
|
||||
switch (_parameterType)
|
||||
{
|
||||
case UxrMaterialParameterType.Int:
|
||||
|
||||
if (_materialMode == UxrMaterialMode.InstanceOnly)
|
||||
{
|
||||
if (_materialSlot == 0)
|
||||
{
|
||||
_renderer.material.SetInt(_parameterName, Mathf.RoundToInt(value.x));
|
||||
}
|
||||
else
|
||||
{
|
||||
materials = _renderer.materials;
|
||||
materials[_materialSlot].SetInt(_parameterName, Mathf.RoundToInt(value.x));
|
||||
_renderer.materials = materials;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_materialSlot == 0)
|
||||
{
|
||||
_renderer.sharedMaterial.SetInt(_parameterName, Mathf.RoundToInt(value.x));
|
||||
}
|
||||
else
|
||||
{
|
||||
materials = _renderer.sharedMaterials;
|
||||
materials[_materialSlot].SetInt(_parameterName, Mathf.RoundToInt(value.x));
|
||||
_renderer.sharedMaterials = materials;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
case UxrMaterialParameterType.Float:
|
||||
|
||||
if (_materialMode == UxrMaterialMode.InstanceOnly)
|
||||
{
|
||||
if (_materialSlot == 0)
|
||||
{
|
||||
_renderer.material.SetFloat(_parameterName, value.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
materials = _renderer.materials;
|
||||
materials[_materialSlot].SetFloat(_parameterName, value.x);
|
||||
_renderer.materials = materials;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_materialSlot == 0)
|
||||
{
|
||||
_renderer.sharedMaterial.SetFloat(_parameterName, value.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
materials = _renderer.sharedMaterials;
|
||||
materials[_materialSlot].SetFloat(_parameterName, value.x);
|
||||
_renderer.sharedMaterials = materials;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
case UxrMaterialParameterType.Vector2:
|
||||
case UxrMaterialParameterType.Vector3:
|
||||
case UxrMaterialParameterType.Vector4:
|
||||
|
||||
if (_materialMode == UxrMaterialMode.InstanceOnly)
|
||||
{
|
||||
if (_materialSlot == 0)
|
||||
{
|
||||
_renderer.material.SetVector(_parameterName, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
materials = _renderer.materials;
|
||||
materials[_materialSlot].SetVector(_parameterName, value);
|
||||
_renderer.materials = materials;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_materialSlot == 0)
|
||||
{
|
||||
_renderer.sharedMaterial.SetVector(_parameterName, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
materials = _renderer.sharedMaterials;
|
||||
materials[_materialSlot].SetVector(_parameterName, value);
|
||||
_renderer.sharedMaterials = materials;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
case UxrMaterialParameterType.Color:
|
||||
|
||||
if (_materialMode == UxrMaterialMode.InstanceOnly)
|
||||
{
|
||||
if (_materialSlot == 0)
|
||||
{
|
||||
_renderer.material.SetColor(_parameterName, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
materials = _renderer.materials;
|
||||
materials[_materialSlot].SetColor(_parameterName, value);
|
||||
_renderer.materials = materials;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_materialSlot == 0)
|
||||
{
|
||||
_renderer.sharedMaterial.SetColor(_parameterName, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
materials = _renderer.sharedMaterials;
|
||||
materials[_materialSlot].SetColor(_parameterName, value);
|
||||
_renderer.sharedMaterials = materials;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Warnings)
|
||||
{
|
||||
Debug.LogWarning($"{UxrConstants.AnimationModule} Material slot " + _materialSlot + " for object " + name + " is not valid");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initializes internal data
|
||||
/// </summary>
|
||||
private void Initialize()
|
||||
{
|
||||
if (_renderer == null)
|
||||
{
|
||||
_renderer = _animateSelf || !_targetGameObject ? GetComponent<Renderer>() : _targetGameObject.GetComponent<Renderer>();
|
||||
|
||||
if (_renderer)
|
||||
{
|
||||
if (_materialSlot == 0)
|
||||
{
|
||||
_originalMaterial = _renderer.sharedMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
_originalMaterials = _renderer.sharedMaterials;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_renderer && !string.IsNullOrEmpty(_parameterName) && !_valueBeforeAnimationInitialized)
|
||||
{
|
||||
_valueBeforeAnimation = GetParameterValue();
|
||||
_valueBeforeAnimationInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private Renderer _renderer;
|
||||
private Material _originalMaterial;
|
||||
private Material[] _originalMaterials;
|
||||
private bool _valueBeforeAnimationInitialized;
|
||||
private Vector4 _valueBeforeAnimation;
|
||||
private Action _finishedCallback;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrAnimatedMaterial.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrAnimatedMaterial.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bbad2839f9d42d41ba6b2d584190630
|
||||
timeCreated: 1516185390
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
350
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrAnimatedTextureFlipbook.cs
vendored
Normal file
350
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrAnimatedTextureFlipbook.cs
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAnimatedTextureFlipbook.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace UltimateXR.Animation.Materials
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to animate a material's texture that contains multiple animation frames.
|
||||
/// </summary>
|
||||
public class UxrAnimatedTextureFlipbook : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _animateSelf = true;
|
||||
[SerializeField] private GameObject _targetGameObject;
|
||||
[SerializeField] private string _scaleOffsetVarName = UxrConstants.Shaders.StandardMainTextureScaleOffsetVarName;
|
||||
[SerializeField] private int _flipBookColumns = 1;
|
||||
[SerializeField] private int _flipBookRows = 1;
|
||||
[SerializeField] private int _totalFrames = 1;
|
||||
[SerializeField] private UxrFlipbookAnimationMode _loopMode = UxrFlipbookAnimationMode.SingleSequence;
|
||||
[SerializeField] private bool _randomFrameStart;
|
||||
[SerializeField] private float _fps = 10;
|
||||
[SerializeField] private UxrFlipbookFinishedAction _whenFinished = UxrFlipbookFinishedAction.DoNothing;
|
||||
[SerializeField] private bool _useUnscaledTime;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Called when the animation finished.
|
||||
/// </summary>
|
||||
public event Action Finished;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target renderer whose material will be animated.
|
||||
/// </summary>
|
||||
public Renderer TargetRenderer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the material's shader scale/offset variable name, usually _MainTex_ST.
|
||||
/// </summary>
|
||||
public string ScaleOffsetVarName
|
||||
{
|
||||
get => _scaleOffsetVarName;
|
||||
set => _scaleOffsetVarName = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of columns in the texture animation sheet.
|
||||
/// </summary>
|
||||
public int FlipBookColumns
|
||||
{
|
||||
get => _flipBookColumns;
|
||||
set => _flipBookColumns = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of rows in the texture animation sheet.
|
||||
/// </summary>
|
||||
public int FlipBookRows
|
||||
{
|
||||
get => _flipBookRows;
|
||||
set => _flipBookRows = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total number of frames in the texture animation sheet.
|
||||
/// </summary>
|
||||
public int TotalFrames
|
||||
{
|
||||
get => _totalFrames;
|
||||
set => _totalFrames = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the animation loop mode.
|
||||
/// </summary>
|
||||
public UxrFlipbookAnimationMode LoopMode
|
||||
{
|
||||
get => _loopMode;
|
||||
set => _loopMode = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to start the animation in a random frame position.
|
||||
/// </summary>
|
||||
public bool RandomFrameStart
|
||||
{
|
||||
get => _randomFrameStart;
|
||||
set => _randomFrameStart = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the frames per second to play the animation.
|
||||
/// </summary>
|
||||
public float FPS
|
||||
{
|
||||
get => _fps;
|
||||
set => _fps = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the action to perform when the animation finished. The only animation that can finish is when
|
||||
/// <see cref="AnimationPlayMode" /> is <see cref="UxrFlipbookAnimationMode.SingleSequence" />.
|
||||
/// </summary>
|
||||
public UxrFlipbookFinishedAction WhenFinished
|
||||
{
|
||||
get => _whenFinished;
|
||||
set => _whenFinished = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes internal variables
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
if (TargetRenderer == null)
|
||||
{
|
||||
TargetRenderer = _animateSelf || !_targetGameObject ? GetComponent<Renderer>() : _targetGameObject.GetComponent<Renderer>();
|
||||
}
|
||||
|
||||
_hasFinished = false;
|
||||
_frameStart = 0;
|
||||
SetFrame(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called each time the object is enabled. Reset timer and set the curve state to unfinished.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_startTime = _useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
_hasFinished = false;
|
||||
_lastFrame = -1;
|
||||
_lastLinearFrame = -1;
|
||||
|
||||
if (_randomFrameStart)
|
||||
{
|
||||
_frameStart = Mathf.RoundToInt(Random.value * (_totalFrames - 1));
|
||||
}
|
||||
|
||||
if (TargetRenderer && _whenFinished == UxrFlipbookFinishedAction.DisableRenderer)
|
||||
{
|
||||
TargetRenderer.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the correct flipbook frame and checks if it finished
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (_hasFinished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float currentTime = _useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
int linearFrame = (int)((currentTime - _startTime) * _fps);
|
||||
|
||||
switch (_loopMode)
|
||||
{
|
||||
case UxrFlipbookAnimationMode.SingleSequence:
|
||||
|
||||
if (linearFrame >= _totalFrames)
|
||||
{
|
||||
ExecuteFinishAction();
|
||||
_hasFinished = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFrame(_totalFrames > 0 ? (linearFrame + _frameStart) % _totalFrames : 0);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UxrFlipbookAnimationMode.Loop:
|
||||
|
||||
SetFrame(_totalFrames > 0 ? (linearFrame + _frameStart) % _totalFrames : 0);
|
||||
|
||||
break;
|
||||
|
||||
case UxrFlipbookAnimationMode.PingPong:
|
||||
|
||||
if (_totalFrames > 1)
|
||||
{
|
||||
if (linearFrame < _totalFrames)
|
||||
{
|
||||
SetFrame(linearFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool forward = ((linearFrame - _totalFrames) / (_totalFrames - 1) & 1) == 1;
|
||||
int correctFrame = (linearFrame - _totalFrames) % (_totalFrames - 1);
|
||||
SetFrame(forward ? correctFrame + 1 : _totalFrames - correctFrame - 2);
|
||||
}
|
||||
}
|
||||
else if (_lastFrame != 0)
|
||||
{
|
||||
SetFrame(0);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UxrFlipbookAnimationMode.RandomFrame:
|
||||
|
||||
if (linearFrame != _lastLinearFrame)
|
||||
{
|
||||
SetFrame(Random.Range(0, _totalFrames));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UxrFlipbookAnimationMode.RandomFrameNoRepetition:
|
||||
|
||||
if (linearFrame != _lastLinearFrame)
|
||||
{
|
||||
if (_totalFrames < 2)
|
||||
{
|
||||
SetFrame(0);
|
||||
}
|
||||
else if (_totalFrames == 2)
|
||||
{
|
||||
SetFrame(_lastFrame == 0 ? 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
int frame = Random.Range(0, _totalFrames);
|
||||
|
||||
while (frame == _lastFrame)
|
||||
{
|
||||
frame = Random.Range(0, _totalFrames);
|
||||
}
|
||||
|
||||
SetFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
_lastLinearFrame = linearFrame;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current flipbook texture frame
|
||||
/// </summary>
|
||||
/// <param name="frame">Flipbook frame</param>
|
||||
private void SetFrame(int frame)
|
||||
{
|
||||
if (TargetRenderer && _lastFrame != frame)
|
||||
{
|
||||
Vector4 vecScaleOffset = TargetRenderer.material.GetVector(_scaleOffsetVarName);
|
||||
|
||||
if (_flipBookColumns > 0)
|
||||
{
|
||||
int column = frame % _flipBookColumns;
|
||||
vecScaleOffset.x = 1.0f / _flipBookColumns;
|
||||
vecScaleOffset.z = column * vecScaleOffset.x;
|
||||
}
|
||||
|
||||
if (_flipBookRows > 0 && _flipBookColumns > 0)
|
||||
{
|
||||
int row = frame / _flipBookColumns;
|
||||
vecScaleOffset.y = 1.0f / _flipBookRows;
|
||||
vecScaleOffset.w = 1.0f - (row + 1) * vecScaleOffset.y;
|
||||
}
|
||||
|
||||
TargetRenderer.material.SetVector(_scaleOffsetVarName, vecScaleOffset);
|
||||
_lastFrame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the action when the animation finished.
|
||||
/// </summary>
|
||||
private void ExecuteFinishAction()
|
||||
{
|
||||
Finished?.Invoke();
|
||||
|
||||
switch (_whenFinished)
|
||||
{
|
||||
case UxrFlipbookFinishedAction.DoNothing: break;
|
||||
|
||||
case UxrFlipbookFinishedAction.DisableRenderer:
|
||||
|
||||
if (TargetRenderer)
|
||||
{
|
||||
TargetRenderer.enabled = false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UxrFlipbookFinishedAction.DisableGameObject:
|
||||
|
||||
if (TargetRenderer)
|
||||
{
|
||||
TargetRenderer.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UxrFlipbookFinishedAction.DestroyGameObject:
|
||||
|
||||
if (TargetRenderer)
|
||||
{
|
||||
Destroy(TargetRenderer.gameObject);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private int _frameStart;
|
||||
private float _startTime;
|
||||
private bool _hasFinished;
|
||||
private int _lastFrame = -1;
|
||||
private int _lastLinearFrame = -1;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13ede170b3667dc4882754072020a994
|
||||
timeCreated: 1516185390
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
40
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrFlipbookAnimationMode.cs
vendored
Normal file
40
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrFlipbookAnimationMode.cs
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFlipbookAnimationMode.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Animation.Materials
|
||||
{
|
||||
/// <summary>
|
||||
/// The different animation modes available in <see cref="UxrAnimatedTextureFlipbook" />
|
||||
/// </summary>
|
||||
public enum UxrFlipbookAnimationMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Frames are played back in a sequence, ending with the last frame.
|
||||
/// </summary>
|
||||
SingleSequence,
|
||||
|
||||
/// <summary>
|
||||
/// Frames are played back in a sequence up to the last frame. The sequence starts again from the beginning
|
||||
/// indefinitely.
|
||||
/// </summary>
|
||||
Loop,
|
||||
|
||||
/// <summary>
|
||||
/// Frames are played back in a sequence up to the last frame and then back to the beginning again. This process is
|
||||
/// repeated indefinitely.
|
||||
/// </summary>
|
||||
PingPong,
|
||||
|
||||
/// <summary>
|
||||
/// Random frames are played indefinitely.
|
||||
/// </summary>
|
||||
RandomFrame,
|
||||
|
||||
/// <summary>
|
||||
/// Random frames are played indefinitely but there are never two same frames played one after the other.
|
||||
/// </summary>
|
||||
RandomFrameNoRepetition,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04054975fe5140d6a343f675c1e9d284
|
||||
timeCreated: 1643216355
|
||||
34
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrFlipbookFinishedAction.cs
vendored
Normal file
34
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrFlipbookFinishedAction.cs
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFlipbookFinishedAction.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Animation.Materials
|
||||
{
|
||||
/// <summary>
|
||||
/// What should be done when a <see cref="UxrAnimatedTextureFlipbook" /> animation finished. This is only supported
|
||||
/// with <see cref="UxrFlipbookAnimationMode.SingleSequence" />.
|
||||
/// </summary>
|
||||
public enum UxrFlipbookFinishedAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Nothing happens when the animation finished.
|
||||
/// </summary>
|
||||
DoNothing,
|
||||
|
||||
/// <summary>
|
||||
/// After showing the last frame, the renderer is disabled.
|
||||
/// </summary>
|
||||
DisableRenderer,
|
||||
|
||||
/// <summary>
|
||||
/// After showing the last frame, the GameObject the component is attached to is disabled.
|
||||
/// </summary>
|
||||
DisableGameObject,
|
||||
|
||||
/// <summary>
|
||||
/// After showing the last frame, the GameObject the component is attached to is destroyed.
|
||||
/// </summary>
|
||||
DestroyGameObject
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eef28cb2025e45128264c3a742b1a9ac
|
||||
timeCreated: 1643216370
|
||||
24
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrMaterialMode.cs
vendored
Normal file
24
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrMaterialMode.cs
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrMaterialMode.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Animation.Materials
|
||||
{
|
||||
/// <summary>
|
||||
/// The material modes supported by <see cref="UxrAnimatedMaterial" />. It can animate the object's instanced material
|
||||
/// or all the objects that share the same material.
|
||||
/// </summary>
|
||||
public enum UxrMaterialMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Animate this instance of the material only.
|
||||
/// </summary>
|
||||
InstanceOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Animate the material, so that all renderers that share the same material are affected too.
|
||||
/// </summary>
|
||||
Shared
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrMaterialMode.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrMaterialMode.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9746372080bd49198a73205527919f3e
|
||||
timeCreated: 1643214925
|
||||
43
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrMaterialParameterType.cs
vendored
Normal file
43
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrMaterialParameterType.cs
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrMaterialParameterType.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Animation.Materials
|
||||
{
|
||||
/// <summary>
|
||||
/// Material parameter types that can be animated by <see cref="UxrAnimatedMaterial" />.
|
||||
/// </summary>
|
||||
public enum UxrMaterialParameterType
|
||||
{
|
||||
/// <summary>
|
||||
/// Integer value.
|
||||
/// </summary>
|
||||
Int,
|
||||
|
||||
/// <summary>
|
||||
/// Single floating point value.
|
||||
/// </summary>
|
||||
Float,
|
||||
|
||||
/// <summary>
|
||||
/// Vector2 value representing two floating points.
|
||||
/// </summary>
|
||||
Vector2,
|
||||
|
||||
/// <summary>
|
||||
/// Vector3 value representing three floating points.
|
||||
/// </summary>
|
||||
Vector3,
|
||||
|
||||
/// <summary>
|
||||
/// Vector4 value representing four floating points.
|
||||
/// </summary>
|
||||
Vector4,
|
||||
|
||||
/// <summary>
|
||||
/// Color represented by 4 values RGBA.
|
||||
/// </summary>
|
||||
Color
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18df256b817b45148477817eeaa9dc4d
|
||||
timeCreated: 1643214917
|
||||
89
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrMaterialRenderQueue.cs
vendored
Normal file
89
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrMaterialRenderQueue.cs
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrMaterialRenderQueue.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Materials
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that changes the RenderQueue of a material. Changes will be applied at runtime.
|
||||
/// </summary>
|
||||
public class UxrMaterialRenderQueue : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _instanceOnly;
|
||||
[SerializeField] private bool _everyFrame = true;
|
||||
[SerializeField] private int _slot;
|
||||
[SerializeField] private int _value;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Gets the component and applies the RenderQueue value.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
_renderer = GetComponent<Renderer>();
|
||||
Apply();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the RenderQueue each frame if required.
|
||||
/// </summary>
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (_everyFrame)
|
||||
{
|
||||
Apply();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Internal method that applies the RenderQueue value.
|
||||
/// </summary>
|
||||
private void Apply()
|
||||
{
|
||||
if (_renderer != null)
|
||||
{
|
||||
if (_instanceOnly)
|
||||
{
|
||||
Material[] materials = _renderer.materials;
|
||||
if (_slot >= 0 && _slot < materials.Length)
|
||||
{
|
||||
materials[_slot].renderQueue = _value;
|
||||
}
|
||||
_renderer.materials = materials;
|
||||
}
|
||||
else
|
||||
{
|
||||
Material[] materials = _renderer.sharedMaterials;
|
||||
if (_slot >= 0 && _slot < materials.Length)
|
||||
{
|
||||
materials[_slot].renderQueue = _value;
|
||||
}
|
||||
_renderer.sharedMaterials = materials;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private Renderer _renderer;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrMaterialRenderQueue.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Materials/UxrMaterialRenderQueue.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f664d8d69876084f9aaa68e8d224537
|
||||
timeCreated: 1527163303
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/ParticleSystems.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/ParticleSystems.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d63fe01f3012402c98871d81a03bdd96
|
||||
timeCreated: 1643801495
|
||||
170
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/ParticleSystems/UxrToggleEmitParticles.cs
vendored
Normal file
170
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/ParticleSystems/UxrToggleEmitParticles.cs
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrToggleEmitParticles.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Extensions.System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.ParticleSystems
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to toggle particle emission enabled state back and forth at random times.
|
||||
/// </summary>
|
||||
public class UxrToggleEmitParticles : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private ParticleSystem _particleSystem;
|
||||
[SerializeField] private GameObject[] _toggleAdditionalGameObjects;
|
||||
[SerializeField] private float _enabledDurationMin;
|
||||
[SerializeField] private float _enabledDurationMax;
|
||||
[SerializeField] private float _disabledDurationMin;
|
||||
[SerializeField] private float _disabledDurationMax;
|
||||
[SerializeField] private bool _useUnscaledTime;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// The particle system to toggle.
|
||||
/// </summary>
|
||||
public ParticleSystem TargetParticleSystem
|
||||
{
|
||||
get => _particleSystem;
|
||||
set => _particleSystem = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additional objects whose active state is toggled too.
|
||||
/// </summary>
|
||||
public GameObject[] ToggleAdditionalGameObjects
|
||||
{
|
||||
get => _toggleAdditionalGameObjects;
|
||||
set => _toggleAdditionalGameObjects = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The minimum amount of seconds the emission will be enabled when toggled on.
|
||||
/// </summary>
|
||||
public float EnabledSecondsMin
|
||||
{
|
||||
get => _enabledDurationMin;
|
||||
set => _enabledDurationMin = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of seconds the emission will be enabled when toggled on.
|
||||
/// </summary>
|
||||
public float EnabledSecondsMax
|
||||
{
|
||||
get => _enabledDurationMax;
|
||||
set => _enabledDurationMax = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The minimum amount of seconds the emission will be disabled when toggled off.
|
||||
/// </summary>
|
||||
public float DisabledSecondsMin
|
||||
{
|
||||
get => _disabledDurationMin;
|
||||
set => _disabledDurationMin = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The minimum amount of seconds the emission will be disabled when toggled off.
|
||||
/// </summary>
|
||||
public float DisabledSecondsMax
|
||||
{
|
||||
get => _disabledDurationMax;
|
||||
set => _disabledDurationMax = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use <see cref="Time.time" /> or <see cref="Time.unscaledTime" /> for timing.
|
||||
/// </summary>
|
||||
public bool UseUnscaledTime
|
||||
{
|
||||
get => _useUnscaledTime;
|
||||
set => _useUnscaledTime = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Called each time the component is enabled. Sets up the next toggle time.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_startTime = _useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
_nextToggleTime = GetNextRelativeToggleTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called on each update. Checks if it is time to toggle the emission state.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
float time = CurrentTime - _startTime;
|
||||
|
||||
if (time > _nextToggleTime)
|
||||
{
|
||||
// Toggle GameObjects
|
||||
|
||||
_toggleAdditionalGameObjects.ForEach(go => go.SetActive(!go.activeSelf));
|
||||
|
||||
// Toggle particle emission
|
||||
|
||||
if (_particleSystem != null)
|
||||
{
|
||||
ParticleSystem.EmissionModule emissionModule = _particleSystem.emission;
|
||||
emissionModule.enabled = !emissionModule.enabled;
|
||||
}
|
||||
|
||||
// Setup next toggle time
|
||||
|
||||
_startTime = CurrentTime;
|
||||
_nextToggleTime = GetNextRelativeToggleTime();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next time the components will be toggled
|
||||
/// </summary>
|
||||
/// <returns>Next toggle time in seconds relative to the current time</returns>
|
||||
private float GetNextRelativeToggleTime()
|
||||
{
|
||||
if (_particleSystem != null && _particleSystem.emission.enabled)
|
||||
{
|
||||
return Random.Range(_enabledDurationMin, _enabledDurationMax);
|
||||
}
|
||||
if (_particleSystem != null && !_particleSystem.emission.enabled)
|
||||
{
|
||||
return Random.Range(_disabledDurationMin, _disabledDurationMax);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private float CurrentTime => _useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
|
||||
private float _startTime;
|
||||
private float _nextToggleTime;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d641254e5316ee8448d1aefd9377ca1e
|
||||
timeCreated: 1522911056
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Splines.meta
vendored
Normal file
8
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Splines.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f84c254c56dd4624087f700babd193f4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
139
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Splines/UxrCatmullRomSpline.cs
vendored
Normal file
139
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Splines/UxrCatmullRomSpline.cs
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrCatmullRomSpline.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Animation.Splines
|
||||
{
|
||||
/// <summary>
|
||||
/// Catmull-Rom spline. It is used to interpolate smoothly between a set of points.
|
||||
/// </summary>
|
||||
public class UxrCatmullRomSpline : UxrSpline
|
||||
{
|
||||
#region Public Overrides UxrSpline
|
||||
|
||||
/// <summary>
|
||||
/// Does the object contain valid data in order to evaluate the curve?
|
||||
/// </summary>
|
||||
public override bool HasValidData => _points != null && _points.Count > 1;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the curve
|
||||
/// </summary>
|
||||
/// <param name="t">Interpolation parameter [0.0f, 1.0f]</param>
|
||||
/// <param name="position">Interpolated point</param>
|
||||
/// <returns>Success or failure</returns>
|
||||
public override bool Evaluate(float t, out Vector3 position)
|
||||
{
|
||||
return Evaluate(t, out position, out float _);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Smoothly interpolates, using Catmull-Rom equations, from p1 to p2 using additional p0 and p3 points.
|
||||
/// </summary>
|
||||
/// <param name="p0">Point 0</param>
|
||||
/// <param name="p1">Point 1</param>
|
||||
/// <param name="p2">Point 2</param>
|
||||
/// <param name="p3">Point 3</param>
|
||||
/// <param name="t">Interpolation parameter [0.0f, 1.0f]</param>
|
||||
/// <returns>Interpolated point</returns>
|
||||
public static Vector3 Evaluate(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
|
||||
{
|
||||
Vector3 ret = new Vector3();
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
|
||||
ret.x = 0.5f * (2.0f * p1.x + (-p0.x + p2.x) * t + (2.0f * p0.x - 5.0f * p1.x + 4 * p2.x - p3.x) * t2 + (-p0.x + 3.0f * p1.x - 3.0f * p2.x + p3.x) * t3);
|
||||
ret.y = 0.5f * (2.0f * p1.y + (-p0.y + p2.y) * t + (2.0f * p0.y - 5.0f * p1.y + 4 * p2.y - p3.y) * t2 + (-p0.y + 3.0f * p1.y - 3.0f * p2.y + p3.y) * t3);
|
||||
ret.z = 0.5f * (2.0f * p1.z + (-p0.z + p2.z) * t + (2.0f * p0.z - 5.0f * p1.z + 4 * p2.z - p3.z) * t2 + (-p0.z + 3.0f * p1.z - 3.0f * p2.z + p3.z) * t3);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a spline. If <see cref="UxrSpline.UsePrecomputedSampleCount" /> > 0 it will also precompute samples in
|
||||
/// order to evaluate the spline using arc-length parameter.
|
||||
/// </summary>
|
||||
/// <param name="inOutMultiplier">
|
||||
/// Magnitude of spline start and end dummy tangent vectors
|
||||
/// compared to their respective control points. A value of 1 (default) will create dummies
|
||||
/// mirroring p1 and p(n-1) vectors. A different value will multiply these vectors by it.
|
||||
/// It can be used to change the spline start/end curvature.
|
||||
/// </param>
|
||||
/// <param name="points">Set of points defining the curve</param>
|
||||
/// <returns>Success or failure</returns>
|
||||
public bool Create(float inOutMultiplier = 1.0f, params Vector3[] points)
|
||||
{
|
||||
_points = new List<Vector3>(points);
|
||||
|
||||
if (points.Length < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_dummyStart = points[0] + (points[0] - points[1]) * inOutMultiplier;
|
||||
_dummyEnd = points[points.Length - 1] + (points[points.Length - 1] - points[points.Length - 2]) * inOutMultiplier;
|
||||
|
||||
ComputeArcLengthSamples();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates the curve using Catmull-Rom equations.
|
||||
/// </summary>
|
||||
/// <param name="t">Interpolation parameter [0.0f, 1.0f]</param>
|
||||
/// <param name="position">Interpolated position</param>
|
||||
/// <param name="segmentLength">Length of the segment that this point belongs to</param>
|
||||
/// <returns>Success or failure</returns>
|
||||
private bool Evaluate(float t, out Vector3 position, out float segmentLength)
|
||||
{
|
||||
position = Vector3.zero;
|
||||
segmentLength = 0.0f;
|
||||
|
||||
t = Mathf.Clamp01(t);
|
||||
|
||||
// Compute the index of p1 and build a Catmull segment with 4 points from there
|
||||
int indexA = Mathf.FloorToInt(t * (_points.Count - 1));
|
||||
float segmentT = t * (_points.Count - 1) - indexA;
|
||||
|
||||
if (indexA >= _points.Count - 1)
|
||||
{
|
||||
indexA = _points.Count - 2;
|
||||
segmentT = 1.0f;
|
||||
}
|
||||
|
||||
Vector3 p0 = indexA == 0 ? _dummyStart : _points[indexA - 1];
|
||||
Vector3 p1 = _points[indexA];
|
||||
Vector3 p2 = _points[indexA + 1];
|
||||
Vector3 p3 = indexA >= _points.Count - 2 ? _dummyEnd : _points[indexA + 2];
|
||||
|
||||
segmentLength = Vector3.Distance(p1, p2);
|
||||
|
||||
// Interpolate
|
||||
position = Evaluate(p0, p1, p2, p3, segmentT);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private List<Vector3> _points;
|
||||
private Vector3 _dummyStart;
|
||||
private Vector3 _dummyEnd;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Splines/UxrCatmullRomSpline.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Runtime/Scripts/Animation/Splines/UxrCatmullRomSpline.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d8fc5a7ef3e485fb34cf8b6e1873e86
|
||||
timeCreated: 1617136491
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user