Add ultimate xr
This commit is contained in:
3
Assets/UltimateXR/Runtime/Scripts/Core/Caching.meta
Normal file
3
Assets/UltimateXR/Runtime/Scripts/Core/Caching.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ffe6caaf52d499d93f347aa1c9726f5
|
||||
timeCreated: 1643819050
|
||||
@@ -0,0 +1,30 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="IUxrPrecacheable.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IUxrPrecacheable" /> interface is used in components that need to create instances at runtime and
|
||||
/// want a way to precache them so that there aren't any hiccups on instantiation.
|
||||
/// The <see cref="UxrManager" /> will look for <see cref="IUxrPrecacheable" /> components when the scene is loaded and
|
||||
/// will instantiate and render the objects specified by <see cref="PrecachedInstances" /> a certain amount of frames
|
||||
/// while the screen is still black.
|
||||
/// This will make sure their resources (meshes, textures) are cached in order to minimize instantiation delays.
|
||||
/// </summary>
|
||||
public interface IUxrPrecacheable
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GameObjects, usually prefabs, that will be precached when the scene is loaded.
|
||||
/// </summary>
|
||||
IEnumerable<GameObject> PrecachedInstances { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 454ce8b03452c5b46bc1b817c1e84ed0
|
||||
timeCreated: 1537182951
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/UltimateXR/Runtime/Scripts/Core/Components.meta
Normal file
3
Assets/UltimateXR/Runtime/Scripts/Core/Components.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d137c9b1b724424489edfe2883262f1e
|
||||
timeCreated: 1643819108
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 342bbee74e734c30970bd3ce59da488b
|
||||
timeCreated: 1644566116
|
||||
@@ -0,0 +1,155 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatarComponent.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Components.Composite
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic base class for components that are added to an <see cref="UxrAvatar" /> and we want to keep track of.
|
||||
/// It allows to enumerate components using static methods.
|
||||
/// This class could have instead inherited from <see cref="UxrComponent{TP,TC}" /> but we avoided
|
||||
/// this to not have redundancy between Avatar/Parent properties and methods.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Component type</typeparam>
|
||||
public abstract class UxrAvatarComponent<T> : UxrComponent<T> where T : UxrAvatarComponent<T>
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the components, enabled or not, of this specific type that belong to the local avatar.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Components that have never been enabled are not returned. Components are automatically registered through their
|
||||
/// Awake() call, which is never called if the object has never been enabled. In this case it is recommended to resort
|
||||
/// to <see cref="UnityEngine.GameObject.GetComponentsInChildren{T}(bool)">GetComponentsInChildren</see>.
|
||||
/// </remarks>
|
||||
public static IEnumerable<T> AllComponentsInLocalAvatar => AllComponents.Where(c => c.Avatar != null && c.Avatar.AvatarMode == UxrAvatarMode.Local);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the enabled components of this specific type that belong to the local avatar.
|
||||
/// </summary>
|
||||
public static IEnumerable<T> EnabledComponentsInLocalAvatar => AllComponents.Where(c => c.Avatar != null && c.Avatar.AvatarMode == UxrAvatarMode.Local && c.isActiveAndEnabled);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local avatar or null if there is none.
|
||||
/// </summary>
|
||||
public static UxrAvatar LocalAvatar
|
||||
{
|
||||
get
|
||||
{
|
||||
T component = AllComponents.FirstOrDefault(c => c.Avatar != null && c.Avatar.AvatarMode == UxrAvatarMode.Local);
|
||||
return component == null ? null : component.Avatar;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the components, enabled of not, of this specific type that belong to this instance of the avatar.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Components that have never been enabled are not returned. Components are automatically registered through their
|
||||
/// Awake() call, which is never called if the object has never been enabled. In this case it is recommended to resort
|
||||
/// to <see cref="GameObject.GetComponentsInChildren{T}(bool)" />.
|
||||
/// </remarks>
|
||||
public IEnumerable<T> AllComponentsInAvatar => GetComponents(Avatar, true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets only the enabled components of this specific type that belong to this instance of the avatar.
|
||||
/// </summary>
|
||||
public IEnumerable<T> EnabledComponentsInAvatar => GetComponents(Avatar);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="UxrAvatar" /> the component belongs to.
|
||||
/// </summary>
|
||||
public UxrAvatar Avatar
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_avatar == null)
|
||||
{
|
||||
_avatar = this.SafeGetComponentInParent<UxrAvatar>();
|
||||
}
|
||||
|
||||
return _avatar;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the components of a specific avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to get the components from</param>
|
||||
/// <param name="includeDisabled">Whether to include disabled components or not</param>
|
||||
/// <returns>Components meeting the criteria</returns>
|
||||
/// <remarks>
|
||||
/// When using the <paramref name="includeDisabled" /> parameter, components that have never been enabled are not
|
||||
/// returned. Components are automatically registered through their Awake() call, which is never called if the object
|
||||
/// has never been enabled. In this case it is recommended to resort to
|
||||
/// <see cref="GameObject.GetComponentsInChildren{T}(bool)" />.
|
||||
/// </remarks>
|
||||
public static IEnumerable<T> GetComponents(UxrAvatar avatar, bool includeDisabled = false)
|
||||
{
|
||||
if (includeDisabled)
|
||||
{
|
||||
return AllComponents.Where(c => c.Avatar == avatar);
|
||||
}
|
||||
return AllComponents.Where(c => c.Avatar == avatar && c.isActiveAndEnabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the components of a specific avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to get the components from</param>
|
||||
/// <param name="includeDisabled">Whether to include disabled components or not</param>
|
||||
/// <returns>Components meeting the criteria</returns>
|
||||
/// <remarks>
|
||||
/// When using the <paramref name="includeDisabled" /> parameter, components that have never been enabled are not
|
||||
/// returned. Components are automatically registered through their Awake() call, which is never called if the object
|
||||
/// has never been enabled. In this case it is recommended to resort to
|
||||
/// <see cref="GameObject.GetComponentsInChildren{T}(bool)" />.
|
||||
/// </remarks>
|
||||
public static IEnumerable<TC> GetComponents<TC>(UxrAvatar avatar, bool includeDisabled = false) where TC : T
|
||||
{
|
||||
if (includeDisabled)
|
||||
{
|
||||
return AllComponents.OfType<TC>().Where(c => c.Avatar == avatar);
|
||||
}
|
||||
|
||||
return AllComponents.OfType<TC>().Where(c => c.Avatar == avatar && c.isActiveAndEnabled);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Pre-caches the avatar component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
if (_avatar == null)
|
||||
{
|
||||
_avatar = GetComponentInParent<UxrAvatar>();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrAvatar _avatar;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73f120c94e676034eaa2f80d76c32e85
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,320 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabbableObjectComponent.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Manipulation;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Components.Composite
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic base class for components belonging to an object that also has a <see cref="UxrGrabbableObject" />
|
||||
/// or in any of its parents. It allows to leverage some of the work related to accessing the
|
||||
/// <see cref="UxrGrabbableObject" /> component and processing the events without the need to subscribe or
|
||||
/// unsubscribe to them. Instead, events can be processed by overriding the different event triggers
|
||||
/// (OnXXX methods).
|
||||
/// <para>
|
||||
/// The component has also all the benefits derived from <see cref="UxrComponent" />.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Component type</typeparam>
|
||||
public abstract class UxrGrabbableObjectComponent<T> : UxrComponent<T> where T : UxrGrabbableObjectComponent<T>
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the grabbable object is currently being grabbed.
|
||||
/// </summary>
|
||||
public bool IsBeingGrabbed => GrabbableObject && GrabbableObject.IsBeingGrabbed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbable object component.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject GrabbableObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_grabbableObject == null)
|
||||
{
|
||||
_grabbableObject = this.SafeGetComponentInParent<UxrGrabbableObject>();
|
||||
}
|
||||
|
||||
return _grabbableObject;
|
||||
}
|
||||
private set => _grabbableObject = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Caches the grabbable object component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
GrabbableObject = GetComponentInParent<UxrGrabbableObject>();
|
||||
|
||||
if (GrabbableObject == null && IsGrabbableObjectRequired)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelManipulation >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"Component {nameof(T)} requires a {nameof(UxrGrabbableObject)} component in the same object or any of its parents.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to events.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
if (GrabbableObject)
|
||||
{
|
||||
GrabbableObject.Grabbing += GrabbableObject_Grabbing;
|
||||
GrabbableObject.Grabbed += GrabbableObject_Grabbed;
|
||||
GrabbableObject.Releasing += GrabbableObject_Releasing;
|
||||
GrabbableObject.Released += GrabbableObject_Released;
|
||||
GrabbableObject.Placing += GrabbableObject_Placing;
|
||||
GrabbableObject.Placed += GrabbableObject_Placed;
|
||||
GrabbableObject.ConstraintsApplying += GrabbableObject_ConstraintsApplying;
|
||||
GrabbableObject.ConstraintsApplied += GrabbableObject_ConstraintsApplied;
|
||||
GrabbableObject.ConstraintsFinished += GrabbableObject_ConstraintsFinished;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
if (GrabbableObject)
|
||||
{
|
||||
GrabbableObject.Grabbing -= GrabbableObject_Grabbing;
|
||||
GrabbableObject.Grabbed -= GrabbableObject_Grabbed;
|
||||
GrabbableObject.Releasing -= GrabbableObject_Releasing;
|
||||
GrabbableObject.Released -= GrabbableObject_Released;
|
||||
GrabbableObject.Placing -= GrabbableObject_Placing;
|
||||
GrabbableObject.Placed -= GrabbableObject_Placed;
|
||||
GrabbableObject.ConstraintsApplying -= GrabbableObject_ConstraintsApplying;
|
||||
GrabbableObject.ConstraintsApplied -= GrabbableObject_ConstraintsApplied;
|
||||
GrabbableObject.ConstraintsFinished -= GrabbableObject_ConstraintsFinished;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Event handling method for the <see cref="UxrGrabbableObject.Grabbing" /> event. It will call the overridable event
|
||||
/// trigger so that child classes don't need to subscribe to the event and can override the method instead.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender</param>
|
||||
/// <param name="e">The event parameters</param>
|
||||
private void GrabbableObject_Grabbing(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
OnObjectGrabbing(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handling method for the <see cref="UxrGrabbableObject.Grabbed" /> event. It will call the overridable event
|
||||
/// trigger so that child classes don't need to subscribe to the event and can override the method instead.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender</param>
|
||||
/// <param name="e">The event parameters</param>
|
||||
private void GrabbableObject_Grabbed(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
OnObjectGrabbed(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handling method for the <see cref="UxrGrabbableObject.Releasing" /> event. It will call the overridable event
|
||||
/// trigger so that child classes don't need to subscribe to the event and can override the method instead.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender</param>
|
||||
/// <param name="e">The event parameters</param>
|
||||
private void GrabbableObject_Releasing(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
OnObjectReleasing(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handling method for the <see cref="UxrGrabbableObject.Released" /> event. It will call the overridable event
|
||||
/// trigger so that child classes don't need to subscribe to the event and can override the method instead.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender</param>
|
||||
/// <param name="e">The event parameters</param>
|
||||
private void GrabbableObject_Released(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
OnObjectReleased(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handling method for the <see cref="UxrGrabbableObject.Placing" /> event. It will call the overridable event
|
||||
/// trigger so that child classes don't need to subscribe to the event and can override the method instead.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender</param>
|
||||
/// <param name="e">The event parameters</param>
|
||||
private void GrabbableObject_Placing(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
OnObjectPlacing(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handling method for the <see cref="UxrGrabbableObject.Placed" /> event. It will call the overridable event
|
||||
/// trigger so that child classes don't need to subscribe to the event and can override the method instead.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender</param>
|
||||
/// <param name="e">The event parameters</param>
|
||||
private void GrabbableObject_Placed(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
OnObjectPlaced(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handling method for the <see cref="UxrGrabbableObject.ConstraintsApplying" /> event. It will call the
|
||||
/// overridable event trigger so that child classes don't need to subscribe to the event and can override the method instead.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender</param>
|
||||
/// <param name="e">The event parameters</param>
|
||||
private void GrabbableObject_ConstraintsApplying(object sender, UxrApplyConstraintsEventArgs e)
|
||||
{
|
||||
OnObjectConstraintsApplying(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handling method for the <see cref="UxrGrabbableObject.ConstraintsApplied" /> event. It will call the
|
||||
/// overridable event trigger so that child classes don't need to subscribe to the event and can override the method instead.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender</param>
|
||||
/// <param name="e">The event parameters</param>
|
||||
private void GrabbableObject_ConstraintsApplied(object sender, UxrApplyConstraintsEventArgs e)
|
||||
{
|
||||
OnObjectConstraintsApplied(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handling method for the <see cref="UxrGrabbableObject.ConstraintsFinished" /> event. It will call the
|
||||
/// overridable event trigger so that child classes don't need to subscribe to the event and can override the method instead.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender</param>
|
||||
/// <param name="e">The event parameters</param>
|
||||
private void GrabbableObject_ConstraintsFinished(object sender, UxrApplyConstraintsEventArgs e)
|
||||
{
|
||||
OnObjectConstraintsFinished(e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Overridable event trigger method for the <see cref="UxrGrabbableObject.Grabbing" /> event that can be used to
|
||||
/// handle it without requiring to subscribe/unsubscribe.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnObjectGrabbing(UxrManipulationEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable event trigger method for the <see cref="UxrGrabbableObject.Grabbed" /> event that can be used to
|
||||
/// handle it without requiring to subscribe/unsubscribe.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnObjectGrabbed(UxrManipulationEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable event trigger method for the <see cref="UxrGrabbableObject.Releasing" /> event that can be used to
|
||||
/// handle it without requiring to subscribe/unsubscribe.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnObjectReleasing(UxrManipulationEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable event trigger method for the <see cref="UxrGrabbableObject.Released" /> event that can be used to
|
||||
/// handle it without requiring to subscribe/unsubscribe.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnObjectReleased(UxrManipulationEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable event trigger method for the <see cref="UxrGrabbableObject.Placing" /> event that can be used to
|
||||
/// handle it without requiring to subscribe/unsubscribe.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnObjectPlacing(UxrManipulationEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable event trigger method for the <see cref="UxrGrabbableObject.Placed" /> event that can be used to
|
||||
/// handle it without requiring to subscribe/unsubscribe.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnObjectPlaced(UxrManipulationEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable event trigger method for the <see cref="UxrGrabbableObject.ConstraintsApplying" /> event that can be
|
||||
/// used to handle it without requiring to subscribe/unsubscribe.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnObjectConstraintsApplying(UxrApplyConstraintsEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable event trigger method for the <see cref="UxrGrabbableObject.ConstraintsApplied" /> event that can be
|
||||
/// used to handle it without requiring to subscribe/unsubscribe.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnObjectConstraintsApplied(UxrApplyConstraintsEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable event trigger method for the <see cref="UxrGrabbableObject.ConstraintsFinished" /> event that can be
|
||||
/// used to handle it without requiring to subscribe/unsubscribe.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnObjectConstraintsFinished(UxrApplyConstraintsEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the grabbable object component is required or it's not. By default it is required but it can be
|
||||
/// overriden in child classes so that it is optional.
|
||||
/// </summary>
|
||||
protected virtual bool IsGrabbableObjectRequired => true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrGrabbableObject _grabbableObject;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea24cad3e26c6e740960c91340ca565b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8dbb0a618ef647dd974f5201e2e40347
|
||||
timeCreated: 1644249537
|
||||
@@ -0,0 +1,14 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="IUxrSingleton.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Core.Components.Singleton
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for all classes that are singletons.
|
||||
/// </summary>
|
||||
public interface IUxrSingleton
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14ce13186a856264bb9788d2a2e4a1dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAbstractSingleton.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Core.Components.Singleton
|
||||
{
|
||||
/// <summary>
|
||||
/// Singleton base class.
|
||||
/// </summary>
|
||||
public abstract class UxrAbstractSingleton : UxrComponent
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <summary>
|
||||
/// Singletons generate unique ID based on full type name. This ensures that the IDs are the same on all devices and
|
||||
/// message exchanges will work correctly.
|
||||
/// </summary>
|
||||
protected override bool UniqueIdIsTypeName => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override int SerializationOrder => UxrConstants.Serialization.SerializationOrderSingleton;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Whether the singleton requires <see cref="UnityEngine.Object.DontDestroyOnLoad" /> applied to the GameObject so
|
||||
/// that it doesn't get destroyed when a new scene is loaded.
|
||||
/// </summary>
|
||||
protected virtual bool NeedsDontDestroyOnLoad => true;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e12d9e4e2a7b44819a93a23b5d53ddcc
|
||||
timeCreated: 1644249666
|
||||
@@ -0,0 +1,239 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAbstractSingleton.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Threading;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Core.Threading;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace UltimateXR.Core.Components.Singleton
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Base class for singletons that can be used with abstract classes.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The difference with <see cref="UxrSingleton{T}" /> is that <see cref="UxrSingleton{T}" /> guarantees
|
||||
/// that an instance will always be available in the scene by instantiating the component if it's not found.
|
||||
/// This means <see cref="UxrSingleton{T}.Instance" /> will always be non-null and can be used with or
|
||||
/// without an instance available in the scene. <see cref="UxrSingleton{T}" /> also allows to use automatic
|
||||
/// prefab instantiation if a compatible singleton prefab is present in a special Resources folder.
|
||||
/// Since abstract classes can't be instantiated, <see cref="Instance" /> in <see cref="UxrAbstractSingleton{T}" />
|
||||
/// will be non-null only if a child component is available somewhere in the scene.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For design purposes, a singleton may still be desirable when programming an abstract class, hence
|
||||
/// this <see cref="UxrAbstractSingleton{T}" /> component base class.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Class the singleton is for</typeparam>
|
||||
/// <remarks>
|
||||
/// <list type="bullet">
|
||||
/// <item>Make sure to call base.Awake() first in child classes where <see cref="Awake" /> is used.</item>
|
||||
/// <item>Use <see cref="HasInstance" /> to check whether the instance exists.</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public abstract class UxrAbstractSingleton<T> : UxrAbstractSingleton, IUxrSingleton where T : UxrAbstractSingleton<T>
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique, global instance of the given component.
|
||||
/// </summary>
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_instance is null)
|
||||
{
|
||||
UxrMonoDispatcher.RunOnMainThread(FindInstance);
|
||||
while (s_instance is null && !UxrMonoDispatcher.IsCurrentThreadMain)
|
||||
{
|
||||
Thread.Sleep(25);
|
||||
}
|
||||
}
|
||||
|
||||
return s_instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether there is a singleton instance available.
|
||||
/// </summary>
|
||||
public static bool HasInstance => s_instance != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the singleton has been initialized.
|
||||
/// </summary>
|
||||
public bool IsInitialized { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Tries to set the singleton instance.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
TrySetInstance(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the singleton instance.
|
||||
/// </summary>
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (ReferenceEquals(this, s_instance))
|
||||
{
|
||||
Release();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the singleton instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected static T GetInstance()
|
||||
{
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to set the singleton instance.
|
||||
/// </summary>
|
||||
/// <param name="value">Candidate to set as singleton instance</param>
|
||||
/// <returns>Whether the instance was set</returns>
|
||||
protected static bool TrySetInstance(UxrAbstractSingleton<T> value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(s_instance, value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(s_instance is null))
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Warnings)
|
||||
{
|
||||
Debug.LogWarning($"{UxrConstants.CoreModule} {typeof(T).Name} singleton already added. Destroying second instance @ {value.gameObject}", value);
|
||||
}
|
||||
|
||||
Destroy(value);
|
||||
return false;
|
||||
}
|
||||
|
||||
Assert.IsTrue(value is T, $"Incoherent types: {value.GetType().Name} vs {typeof(T).Name}");
|
||||
|
||||
if (Application.isPlaying && value.NeedsDontDestroyOnLoad)
|
||||
{
|
||||
DontDestroyOnLoad(value.gameObject);
|
||||
}
|
||||
|
||||
s_instance = (T)value;
|
||||
s_instance.Init();
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Relevant)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.CoreModule} {typeof(T).Name} singleton successfully initialized", s_instance);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default internal initialization. Child classes can override this method if they require initialization code.
|
||||
/// </summary>
|
||||
/// <param name="initializedCallback">Callback called when the initialization finished.</param>
|
||||
protected virtual void InitInternal(Action initializedCallback)
|
||||
{
|
||||
initializedCallback?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default internal release. Child classes can override this method if they required deallocation code.
|
||||
/// </summary>
|
||||
/// <param name="releasedCallback">Callback called when the releasing finished.</param>
|
||||
protected virtual void ReleaseInternal(Action releasedCallback)
|
||||
{
|
||||
releasedCallback?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Called by Unity before a scene is loaded. Calls <see cref="TryFindInstance" />.
|
||||
/// </summary>
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
private static void BeforeSceneLoad()
|
||||
{
|
||||
TryFindInstance();
|
||||
}
|
||||
*/
|
||||
/// <summary>
|
||||
/// Tries to find a pre-existing instance in the scene and set it as the singleton instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static bool TryFindInstance()
|
||||
{
|
||||
return TrySetInstance(FindObjectOfType<T>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find a pre-existing instance in the scene.
|
||||
/// </summary>
|
||||
private static void FindInstance()
|
||||
{
|
||||
TryFindInstance();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton.
|
||||
/// </summary>
|
||||
private void Init()
|
||||
{
|
||||
InitInternal(() => IsInitialized = true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases any resources allocated by the singleton.
|
||||
/// </summary>
|
||||
private void Release()
|
||||
{
|
||||
ReleaseInternal(() =>
|
||||
{
|
||||
if (!IsApplicationQuitting)
|
||||
{
|
||||
s_instance = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private static T s_instance;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e9a4c7a7122d8c419e61e67a6e8d4e0
|
||||
timeCreated: 1644249666
|
||||
@@ -0,0 +1,54 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAsyncInitAbstractSingleton.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UltimateXR.Core.Threading.TaskControllers;
|
||||
|
||||
namespace UltimateXR.Core.Components.Singleton
|
||||
{
|
||||
/// <summary>
|
||||
/// Same as <see cref="UxrAsyncInitSingleton{T}" /> but allows asynchronous initialization.
|
||||
/// This can be useful where singletons require initialization through config files that are loaded asynchronously from
|
||||
/// disk or through network.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type the singleton is for</typeparam>
|
||||
public abstract class UxrAsyncInitAbstractSingleton<T> : UxrAbstractSingleton<T> where T : UxrAsyncInitAbstractSingleton<T>
|
||||
{
|
||||
#region Protected Overrides UxrAbstractSingleton<T>
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton asynchronously. Calls <see cref="InitAsync" /> which is required to be implemented in
|
||||
/// child classes.
|
||||
/// </summary>
|
||||
/// <param name="initializedCallback">Callback required to run when the initialization finished.</param>
|
||||
protected override void InitInternal(Action initializedCallback)
|
||||
{
|
||||
_initController = (UxrTaskController)InitAsync;
|
||||
_initController.Completed += (o, e) => initializedCallback?.Invoke();
|
||||
_initController.Start();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="ct">Allows to cancel the asynchronous process if necessary</param>
|
||||
/// <returns>Task representing the initialization</returns>
|
||||
protected abstract Task InitAsync(CancellationToken ct = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrTaskController _initController;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8353e634dcf55c542b8bfe6dc96a0e59
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAsyncInitSingleton.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UltimateXR.Core.Threading.TaskControllers;
|
||||
|
||||
namespace UltimateXR.Core.Components.Singleton
|
||||
{
|
||||
/// <summary>
|
||||
/// Same as <see cref="UxrSingleton{T}" /> but allows asynchronous initialization.
|
||||
/// This can be useful where singletons require initialization through config files that are loaded asynchronously from
|
||||
/// disk or through network.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type the singleton is for</typeparam>
|
||||
public abstract class UxrAsyncInitSingleton<T> : UxrSingleton<T> where T : UxrAsyncInitSingleton<T>
|
||||
{
|
||||
#region Protected Overrides UxrAbstractSingleton<T>
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton asynchronously. Calls <see cref="InitAsync" /> which is required to be implemented in
|
||||
/// child classes.
|
||||
/// </summary>
|
||||
/// <param name="initializedCallback">Callback required to run when the initialization finished.</param>
|
||||
protected override void InitInternal(Action initializedCallback)
|
||||
{
|
||||
_initController = (UxrTaskController)InitAsync;
|
||||
_initController.Completed += (o, e) => initializedCallback?.Invoke();
|
||||
_initController.Start();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="ct">Allows to cancel the asynchronous process if necessary</param>
|
||||
/// <returns>Task representing the initialization</returns>
|
||||
protected abstract Task InitAsync(CancellationToken ct = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrTaskController _initController;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 512ee11458e34e01ad20c7f127ee1b78
|
||||
timeCreated: 1611582011
|
||||
@@ -0,0 +1,185 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrSingleton.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Core.Threading;
|
||||
using UltimateXR.Extensions.System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Components.Singleton
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// An improved singleton implementation over <see cref="UxrAbstractSingleton{T}" /> for non-abstract classes.
|
||||
/// <see cref="UxrSingleton{T}" /> guarantees that an <see cref="Instance" /> will always be available by
|
||||
/// instantiating the singleton if it wasn't found in the scene. Additionally, it can instantiate a prefab
|
||||
/// if there is one available in a well-known location.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The steps followed by this singleton implementation to assign the instance are the:
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <list type="number">
|
||||
/// <item>
|
||||
/// The singleton component is searched in the scene to see if it was pre-instantiated or is already
|
||||
/// available.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// If not found, the component tries to be instantiated in the scene using a prefab in a well known
|
||||
/// Resources folder. The well known path is <see cref="UxrConstants.Paths.SingletonResources" /> in any
|
||||
/// Resources folder and the prefab name is the singleton class name.
|
||||
/// A prefab allows to assign initial properties to the component and also hang additional resources
|
||||
/// (meshes, textures) from the GameObject if needed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// If not found, a new <see cref="GameObject" /> is instantiated and the singleton is added using
|
||||
/// <see cref="GameObject.AddComponent{T}" />.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The singleton can be a pre-existing component in a scene. If not, <see cref="Instance" /> takes care of
|
||||
/// instancing it and make it the singleton.
|
||||
/// This singleton can only be used in sealed classes. For use in abstract classes check
|
||||
/// <see cref="UxrAbstractSingleton{T}" /> instead.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">
|
||||
/// Type the singleton is for. This template can only be used with a hierarchy where <typeparamref name="T" /> is
|
||||
/// specified at its lowers level (sealed). For use in abstract classes, check <see cref="UxrAbstractSingleton{T}" />.
|
||||
/// </typeparam>
|
||||
public abstract class UxrSingleton<T> : UxrAbstractSingleton<T> where T : UxrSingleton<T>
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <inheritdoc cref="UxrAbstractSingleton{T}.Instance" />
|
||||
public new static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GetInstance() is null)
|
||||
{
|
||||
UxrMonoDispatcher.RunOnMainThread(FindOrAddInstance);
|
||||
while (GetInstance() is null && !UxrMonoDispatcher.IsCurrentThreadMain)
|
||||
{
|
||||
Thread.Sleep(25);
|
||||
}
|
||||
}
|
||||
|
||||
return GetInstance();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Dummy method forcing <see cref="Instance" /> to run the instance finding/creation process.
|
||||
/// </summary>
|
||||
public void Poke()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find a pre-existing instance in the scene and set it as the singleton instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static bool TryFindInstance()
|
||||
{
|
||||
return TrySetInstance(FindObjectOfType<T>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Tries to find the singleton instance in the scene. If it was not found, a new singleton is instantiated
|
||||
/// and assigned as the singleton instance. The following steps are used:
|
||||
/// </para>
|
||||
/// <list type="number">
|
||||
/// <item>
|
||||
/// The singleton component is searched in the scene to see if it was pre-instantiated or is already
|
||||
/// available.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// If not found, the component tries to be instantiated in the scene using a prefab in a well known
|
||||
/// Resources folder. The well known path is <see cref="UxrConstants.Paths.SingletonResources" /> in any
|
||||
/// Resources
|
||||
/// folder and the prefab name is the singleton class name.
|
||||
/// A prefab can be used to assign initial properties to the component and also hang additional resources
|
||||
/// (meshes, textures) from the GameObject.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// If not found, a new <see cref="GameObject" /> is instantiated and the singleton is added using
|
||||
/// <see cref="GameObject.AddComponent{T}" />.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
private static void FindOrAddInstance()
|
||||
{
|
||||
// First: Try to find instance in scene
|
||||
|
||||
if (TryFindInstance())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Second: Try to instantiate it from Resources singleton folder
|
||||
|
||||
T prefab = Resources.Load<T>(ResourcesPrefabPath);
|
||||
T instance = null;
|
||||
|
||||
if (prefab != null)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Relevant)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.CoreModule} {typeof(T).Name} was able to load prefab from Resources folder: {ResourcesPrefabPath}");
|
||||
}
|
||||
|
||||
instance = Instantiate(prefab);
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} {typeof(T).Name}: Unable to instantiate prefab. Instance is null. Path is {ResourcesPrefabPath}", prefab);
|
||||
}
|
||||
}
|
||||
|
||||
instance.name = prefab.name;
|
||||
}
|
||||
|
||||
// Third: Try to instantiate it by creating a GameObject and adding the component
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new GameObject(typeof(T).Name).AddComponent<T>();
|
||||
}
|
||||
|
||||
// Try to set it as the current singleton instance
|
||||
|
||||
if (!TrySetInstance(instance))
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} {typeof(T).Name} singleton failed to initialize", instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private static string ResourcesPrefabPath => PathExt.Combine(UxrConstants.Paths.SingletonResources, typeof(T).Name);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a6ca3fc203c1924db6eeaa1fb9cab40
|
||||
timeCreated: 1519373620
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1203
Assets/UltimateXR/Runtime/Scripts/Core/Components/UxrComponent.cs
Normal file
1203
Assets/UltimateXR/Runtime/Scripts/Core/Components/UxrComponent.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cc18680f6e41d34bb63902248cd4cf9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,221 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrComponent_1.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Like <see cref="UxrComponent"/> but it allows to enumerate all components of a specific type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Component type</typeparam>
|
||||
/// <remarks>
|
||||
/// Components get registered through their Awake() call. This means that components get registered
|
||||
/// the first time they are enabled. Disabled objects that have been enabled at some point are enumerated, but objects
|
||||
/// that have never been enabled don't get enumerated, which means that they will not appear in
|
||||
/// <see cref="AllComponents" />.
|
||||
/// </remarks>
|
||||
/// <seealso cref="UxrAvatarComponent{T}" />
|
||||
public abstract class UxrComponent<T> : UxrComponent where T : UxrComponent<T>
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Called before registering a component.
|
||||
/// </summary>
|
||||
public new static event Action<T> GlobalRegistering;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a component was registered.
|
||||
/// </summary>
|
||||
public new static event Action<T> GlobalRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Called before unregistering a component.
|
||||
/// </summary>
|
||||
public new static event Action<T> GlobalUnregistering;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a component was unregistered.
|
||||
/// </summary>
|
||||
public new static event Action<T> GlobalUnregistered;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a component was enabled.
|
||||
/// </summary>
|
||||
public new static event Action<T> GlobalEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a component was disabled.
|
||||
/// </summary>
|
||||
public new static event Action<T> GlobalDisabled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the components of this specific type, enabled or not, in all open scenes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Components that have never been enabled are not returned. Components are automatically registered through their
|
||||
/// Awake() call, which is never called if the object has never been enabled. In this case it is recommended to resort
|
||||
/// to <see cref="GameObject.GetComponentsInChildren{T}(bool)" /> or
|
||||
/// <see cref="UnityEngine.Object.FindObjectsOfType{T}(bool)" />.
|
||||
/// </remarks>
|
||||
public new static IEnumerable<T> AllComponents => s_typeComponents;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all components of this specific type that are enabled, in all open scenes.
|
||||
/// </summary>
|
||||
public new static IEnumerable<T> EnabledComponents => s_typeComponents.Where(c => c.isActiveAndEnabled);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the internal list of components. This is useful if iterating over the components requires a certain order.
|
||||
/// </summary>
|
||||
/// <param name="comparison">Comparison to use for sorting</param>
|
||||
public static void SortComponents(Comparison<T> comparison)
|
||||
{
|
||||
s_typeComponents.Sort(comparison);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys all components.
|
||||
/// </summary>
|
||||
public new static void DestroyAllComponents()
|
||||
{
|
||||
foreach (T component in s_typeComponents)
|
||||
{
|
||||
Destroy(component);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys all gameObjects the components belong to.
|
||||
/// </summary>
|
||||
public new static void DestroyAllGameObjects()
|
||||
{
|
||||
foreach (T component in s_typeComponents)
|
||||
{
|
||||
Destroy(component.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Registers itself in the static list of components.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
OnRegistering();
|
||||
s_typeComponents.Add((T)this);
|
||||
OnRegistered();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes itself from the static list of components.
|
||||
/// </summary>
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
OnUnregistering();
|
||||
s_typeComponents.Remove((T)this);
|
||||
OnUnregistered();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers enabled events.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
OnEnabled();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers disabled events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
OnDisabled();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="GlobalRegistering" /> event trigger.
|
||||
/// </summary>
|
||||
private void OnRegistering()
|
||||
{
|
||||
GlobalRegistering?.Invoke(this as T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="GlobalRegistered" /> event trigger.
|
||||
/// </summary>
|
||||
private void OnRegistered()
|
||||
{
|
||||
GlobalRegistered?.Invoke(this as T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="GlobalUnregistering" /> event trigger.
|
||||
/// </summary>
|
||||
private void OnUnregistering()
|
||||
{
|
||||
GlobalUnregistering?.Invoke(this as T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="GlobalUnregistered" /> event trigger.
|
||||
/// </summary>
|
||||
private void OnUnregistered()
|
||||
{
|
||||
GlobalUnregistered?.Invoke(this as T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="GlobalEnabled" /> event trigger.
|
||||
/// </summary>
|
||||
private void OnEnabled()
|
||||
{
|
||||
GlobalEnabled?.Invoke(this as T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="GlobalDisabled" /> event trigger.
|
||||
/// </summary>
|
||||
private void OnDisabled()
|
||||
{
|
||||
GlobalDisabled?.Invoke(this as T);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Static list containing all registered components of this type.
|
||||
/// </summary>
|
||||
private static readonly List<T> s_typeComponents = new List<T>();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c05b7b33de81624c87ffc921508809e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,82 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrComponent_2.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Like <see cref="UxrComponent{T}" /> but the component belongs to a hierarchy
|
||||
/// with a parent that has a component of a certain type <typeparamref name="TP" />.
|
||||
/// This allows to enumerate and keep track of only the components that hang from the hierarchy
|
||||
/// under each parent component separately.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// In the case of keeping track of all components of a same type that are in or hang from an avatar (
|
||||
/// <see cref="UxrAvatar" />) there is a special component for that in <see cref="UxrAvatarComponent{T}" />.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="TP">Parent component type</typeparam>
|
||||
/// <typeparam name="TC">Component type</typeparam>
|
||||
public abstract class UxrComponent<TP, TC> : UxrComponent<TC>
|
||||
where TP : Component
|
||||
where TC : UxrComponent<TC>
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the components, enabled of not, of this specific type that hang from the same parent.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Components that have never been enabled are not returned. Components are automatically registered through their
|
||||
/// Awake() call, which is never called if the object has never been enabled. In this case it is recommended to resort
|
||||
/// to <see cref="GameObject.GetComponentsInChildren{T}(bool)" />.
|
||||
/// </remarks>
|
||||
public IEnumerable<TC> AllChildrenFromParent => GetParentChildren(Parent, true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets only the enabled components of this specific type that hang from the same parent.
|
||||
/// </summary>
|
||||
public IEnumerable<TC> EnabledChildrenFromParent => GetParentChildren(Parent);
|
||||
|
||||
/// <summary>
|
||||
/// Parent the component belongs to.
|
||||
/// </summary>
|
||||
public TP Parent => gameObject.SafeGetComponentInParent<TP>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children from a specific parent.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent to get the components from</param>
|
||||
/// <param name="includeDisabled">Whether to include disabled components or not</param>
|
||||
/// <returns>Components meeting the criteria</returns>
|
||||
/// <remarks>
|
||||
/// When using the <paramref name="includeDisabled" /> parameter, components that have never been enabled are not
|
||||
/// returned. Components are automatically registered through their Awake() call, which is never called if the object
|
||||
/// has never been enabled. In this case it is recommended to resort to
|
||||
/// <see cref="GameObject.GetComponentsInChildren{T}(bool)" />.
|
||||
/// </remarks>
|
||||
public static IEnumerable<TC> GetParentChildren(TP parent, bool includeDisabled = false)
|
||||
{
|
||||
if (includeDisabled)
|
||||
{
|
||||
return AllComponents.Where(c => c is UxrComponent<TP, TC> child && child.Parent == parent);
|
||||
}
|
||||
return AllComponents.Where(c => c is UxrComponent<TP, TC> child && child.Parent == parent && c.isActiveAndEnabled);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cd7afe7a9ff9314c8ef1e7db3c16085
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrSyncObject.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Core.Components
|
||||
{
|
||||
public partial class UxrSyncObject
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool SaveStateWhenDisabled => _syncWhileDisabled;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool SerializeActiveAndEnabledState => _syncActiveAndEnabled;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 115582f99ade41d98a39ff08c14120ff
|
||||
timeCreated: 1715540449
|
||||
@@ -0,0 +1,56 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrSyncObject.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.StateSave;
|
||||
using UltimateXR.Core.Unique;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimal <see cref="UxrComponent" /> component that can be used to identify GameObjects that don't have any other
|
||||
/// <see cref="UxrComponent" />. Additionally, it can control which Transform to sync, if any.
|
||||
/// This can be useful in multiplayer environments to reference objects. For example, an avatar might be parented to a
|
||||
/// moving platform and this operation needs to be executed in other client PCs.
|
||||
/// If there is no reference to this platform (it might be the result of performing a raycast and not because a script
|
||||
/// has a reference to it, for instance), there needs to be a way to get the same GameObject in the other PCS.
|
||||
/// Since <see cref="UxrComponent" /> has a way to identify and obtain components (<see cref="UxrComponent.UniqueId" />
|
||||
/// and <see cref="UxrUniqueIdImplementer.TryGetComponentById" />), a simple way to send references through the network
|
||||
/// is by using a <see cref="UxrSyncObject" /> component on the GameObject.
|
||||
/// </summary>
|
||||
public partial class UxrSyncObject : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _syncTransform;
|
||||
[SerializeField] private UxrTransformSpace _transformSpace = UxrTransformSpace.World;
|
||||
[SerializeField] private bool _syncActiveAndEnabled;
|
||||
[SerializeField] private bool _syncWhileDisabled;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the transform should be synchronized.
|
||||
/// </summary>
|
||||
public bool SyncTransform => _syncTransform;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override UxrTransformSpace TransformStateSaveSpace => _transformSpace;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool RequiresTransformSerialization(UxrStateSaveLevel level)
|
||||
{
|
||||
return SyncTransform;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d87e695c3ef3c74b80a160f7594fd30
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45164fba55c670d439fa1c4e8182e6e1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrInstanceEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Instantiation
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiation event args for <see cref="UxrInstanceManager" />.
|
||||
/// </summary>
|
||||
public class UxrInstanceEventArgs : EventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the instantiation/destroy operation. Will be null on
|
||||
/// <see cref="UxrInstanceManager.Instantiating" /> and
|
||||
/// <see cref="UxrInstanceManager.Destroyed" /> events, since the instance will not exist.
|
||||
/// </summary>
|
||||
public GameObject Instance { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the prefab of the instantiation/destroy operation. Null when using
|
||||
/// <see cref="UxrInstanceManager.InstantiateEmptyGameObject" />.
|
||||
/// </summary>
|
||||
public GameObject Prefab { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the prefab Id of the instantiation/destroy operation. The prefab Id is the Id assigned by Unity to the prefab
|
||||
/// asset. Null when using <see cref="UxrInstanceManager.InstantiateEmptyGameObject" /> for instantiation.
|
||||
/// </summary>
|
||||
public string PrefabId { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <inheritdoc />
|
||||
public UxrInstanceEventArgs(GameObject instance, GameObject prefab, string prefabId)
|
||||
{
|
||||
Instance = instance;
|
||||
Prefab = prefab;
|
||||
PrefabId = prefabId;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8959f68d36624e93b7749b11fc9192f6
|
||||
timeCreated: 1708255563
|
||||
@@ -0,0 +1,185 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrInstanceManager.InstanceInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Serialization;
|
||||
using UltimateXR.Core.Unique;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Instantiation
|
||||
{
|
||||
public partial class UxrInstanceManager
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores instantiation information.
|
||||
/// </summary>
|
||||
private class InstanceInfo : IUxrSerializable
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Id of the prefab that was instantiated. Null if it's an empty GameObject.
|
||||
/// </summary>
|
||||
public string PrefabId => _prefabId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the empty instance, if the instance was created using
|
||||
/// <see cref="UxrInstanceManager.InstantiateEmptyGameObject" />.
|
||||
/// </summary>
|
||||
public string Name => _name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent if the object was parented to any.
|
||||
/// </summary>
|
||||
public IUxrUniqueId Parent => _parent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position. It will contain relative position to the parent if parented.
|
||||
/// </summary>
|
||||
public Vector3 Position => _position;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation. It will contain relative rotation to the parent if parented.
|
||||
/// </summary>
|
||||
public Quaternion Rotation => _rotation;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local scale.
|
||||
/// </summary>
|
||||
public Vector3 Scale => _scale;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance</param>
|
||||
/// <param name="prefabId">
|
||||
/// The id of the prefab that was instantiated or null if the instance was created using
|
||||
/// <see cref="UxrInstanceManager.InstantiateEmptyGameObject" />.
|
||||
/// </param>
|
||||
public InstanceInfo(IUxrUniqueId instance, string prefabId)
|
||||
{
|
||||
_prefabId = prefabId;
|
||||
_name = string.IsNullOrEmpty(prefabId) ? instance.GameObject.name : null;
|
||||
_parent = instance.Transform.parent != null ? instance.Transform.parent.GetComponent<IUxrUniqueId>() : null;
|
||||
_position = _parent != null ? instance.Transform.localPosition : instance.Transform.position;
|
||||
_rotation = _parent != null ? instance.Transform.localRotation : instance.Transform.rotation;
|
||||
_scale = instance.Transform.localScale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor required for serialization.
|
||||
/// </summary>
|
||||
private InstanceInfo()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrSerializable
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SerializationVersion => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(IUxrSerializer serializer, int serializationVersion)
|
||||
{
|
||||
serializer.Serialize(ref _prefabId);
|
||||
serializer.Serialize(ref _name);
|
||||
serializer.SerializeUniqueComponent(ref _parent);
|
||||
serializer.Serialize(ref _position);
|
||||
serializer.Serialize(ref _rotation);
|
||||
serializer.Serialize(ref _scale);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the info using the current object data.
|
||||
/// </summary>
|
||||
/// <param name="instance">The GameObject to update the information of</param>
|
||||
public void UpdateInfoUsingObject(GameObject instance)
|
||||
{
|
||||
Transform transform = instance.transform;
|
||||
IUxrUniqueId parent = transform.parent != null ? transform.parent.GetComponent<IUxrUniqueId>() : null;
|
||||
Transform parentTransform = parent?.Transform;
|
||||
|
||||
if (parentTransform != null)
|
||||
{
|
||||
// Use relative parent data
|
||||
_parent = parent;
|
||||
_position = transform.localPosition;
|
||||
_rotation = transform.localRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use world data
|
||||
_parent = null;
|
||||
_position = transform.position;
|
||||
_rotation = transform.rotation;
|
||||
}
|
||||
|
||||
_scale = transform.localScale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the object using the current information.
|
||||
/// </summary>
|
||||
/// <param name="instance">The GameObject to update</param>
|
||||
public void UpdateObjectUsingInfo(GameObject instance)
|
||||
{
|
||||
Transform transform = instance.transform;
|
||||
Transform parentTransform = Parent?.Transform;
|
||||
|
||||
if (parentTransform != null)
|
||||
{
|
||||
// Use relative parent data
|
||||
|
||||
if (transform.parent != parentTransform)
|
||||
{
|
||||
transform.SetParent(parentTransform);
|
||||
}
|
||||
|
||||
TransformExt.SetLocalPositionAndRotation(transform, Position, Rotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use world
|
||||
|
||||
if (transform.parent != null)
|
||||
{
|
||||
transform.SetParent(null);
|
||||
}
|
||||
|
||||
transform.SetPositionAndRotation(Position, Rotation);
|
||||
}
|
||||
|
||||
transform.localScale = Scale;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private string _prefabId;
|
||||
private string _name;
|
||||
private IUxrUniqueId _parent;
|
||||
private Vector3 _position;
|
||||
private Quaternion _rotation;
|
||||
private Vector3 _scale;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afa00701315f483ab45afb6d9b6fa2b7
|
||||
timeCreated: 1708512591
|
||||
@@ -0,0 +1,144 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrInstanceManager.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Core.StateSave;
|
||||
using UltimateXR.Core.Unique;
|
||||
using UltimateXR.Extensions.System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Instantiation
|
||||
{
|
||||
public partial class UxrInstanceManager
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override int SerializationOrder => UxrConstants.Serialization.SerializationOrderInstanceManager;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
// Individual instantiations are already handled through events.
|
||||
// We save all generated instances in higher save levels.
|
||||
|
||||
if (level > UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
// When writing, update instance info first
|
||||
|
||||
if (!isReading)
|
||||
{
|
||||
foreach (KeyValuePair<Guid, InstanceInfo> pair in _currentInstancedPrefabs)
|
||||
{
|
||||
if (_currentInstances.TryGetValue(pair.Key, out GameObject instance) && instance != null)
|
||||
{
|
||||
pair.Value.UpdateInfoUsingObject(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to compare dictionaries, we save the instantiation info always by using null as name to avoid overhead.
|
||||
SerializeStateValue(level, options, null, ref _currentInstancedPrefabs);
|
||||
|
||||
if (isReading)
|
||||
{
|
||||
// When reading we need to update the scene:
|
||||
// -Destroy instances that do not exist anymore in the deserialized data
|
||||
// -Create instances that are not yet in the scene, from the deserialized data
|
||||
|
||||
// First destroy existing instances in the scene that are not present in the deserialized list anymore:
|
||||
|
||||
List<Guid> toRemove = null;
|
||||
|
||||
foreach (KeyValuePair<Guid, GameObject> pair in _currentInstances)
|
||||
{
|
||||
if (pair.Value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_currentInstancedPrefabs == null || !_currentInstancedPrefabs.ContainsKey(pair.Key))
|
||||
{
|
||||
// Before destroying the GameObject, unregister the components ahead of time too in case they are going to be re-created.
|
||||
IUxrUniqueId[] components = pair.Value.GetComponentsInChildren<IUxrUniqueId>(true);
|
||||
components.ForEach(c => c.Unregister());
|
||||
|
||||
Destroy(pair.Value);
|
||||
|
||||
if (toRemove == null)
|
||||
{
|
||||
toRemove = new List<Guid>();
|
||||
}
|
||||
|
||||
toRemove.Add(pair.Key);
|
||||
}
|
||||
else if(_currentInstancedPrefabs != null && _currentInstancedPrefabs.TryGetValue(pair.Key, out InstanceInfo info))
|
||||
{
|
||||
// If the object is still present, update it to the current state
|
||||
info.UpdateObjectUsingInfo(pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemove != null)
|
||||
{
|
||||
foreach (Guid guid in toRemove)
|
||||
{
|
||||
_currentInstances.Remove(guid);
|
||||
}
|
||||
}
|
||||
|
||||
// Now instantiate prefabs that are present in the deserialized list but not in the scene:
|
||||
|
||||
List<(Guid CombineGuid, InstanceInfo Info)> toAdd = null;
|
||||
|
||||
if (_currentInstancedPrefabs != null)
|
||||
{
|
||||
foreach (KeyValuePair<Guid, InstanceInfo> pair in _currentInstancedPrefabs)
|
||||
{
|
||||
if (!_currentInstances.ContainsKey(pair.Key))
|
||||
{
|
||||
if (toAdd == null)
|
||||
{
|
||||
toAdd = new List<(Guid, InstanceInfo)>();
|
||||
}
|
||||
|
||||
toAdd.Add((pair.Key, pair.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toAdd != null)
|
||||
{
|
||||
foreach ((Guid CombineGuid, InstanceInfo Info) pair in toAdd)
|
||||
{
|
||||
Vector3 position = pair.Info.Parent != null ? pair.Info.Parent.Transform.TransformPoint(pair.Info.Position) : pair.Info.Position;
|
||||
Quaternion rotation = pair.Info.Parent != null ? pair.Info.Parent.Transform.rotation * pair.Info.Rotation : pair.Info.Rotation;
|
||||
Vector3 scale = pair.Info.Scale;
|
||||
|
||||
if (!string.IsNullOrEmpty(pair.Info.PrefabId))
|
||||
{
|
||||
// Prefab
|
||||
GameObject newInstance = InstantiatePrefabInternal(pair.Info.PrefabId, pair.Info.Parent, position, rotation, 0, pair.CombineGuid);
|
||||
newInstance.transform.localScale = scale;
|
||||
CheckNetworkSpawnPostprocess(newInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Empty GameObject
|
||||
GameObject newObject = InstantiateEmptyGameObjectInternal(pair.Info.Name, pair.Info.Parent, position, rotation, 0, pair.CombineGuid);
|
||||
newObject.transform.localScale = scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5252f824b19944e38a1031be6ef5475c
|
||||
timeCreated: 1708445377
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 314ed13586b47e14182028e81ed64fed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrPrefabList.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Instantiation
|
||||
{
|
||||
/// <summary>
|
||||
/// List of user-defined instantiable prefabs used by <see cref="UxrInstanceManager" />.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "PrefabList", menuName = "UltimateXR/Prefab List", order = 1)]
|
||||
public class UxrPrefabList : ScriptableObject
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private List<GameObject> _prefabList;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the prefab list.
|
||||
/// </summary>
|
||||
public IReadOnlyList<GameObject> PrefabList => _prefabList;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc759d11379460f4aa1527771fc94d52
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/UltimateXR/Runtime/Scripts/Core/Math.meta
Normal file
3
Assets/UltimateXR/Runtime/Scripts/Core/Math.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebebb29280ab495c90fc5dce895bae8e
|
||||
timeCreated: 1643819154
|
||||
243
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrAxis.cs
Normal file
243
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrAxis.cs
Normal file
@@ -0,0 +1,243 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAxis.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that allows to have formatted axes (A combo box with X, Y, Z strings) instead of numerical fields.
|
||||
/// It also allows conversion from and to integers and <see cref="Vector3" /> types.
|
||||
/// See the UxrAxisPropertyDrawer editor class for the integration with Unity Editor.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrAxis : IEquatable<UxrAxis>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private int _axis;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
public const int X = 0;
|
||||
public const int Y = 1;
|
||||
public const int Z = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a perpendicular axis.
|
||||
/// </summary>
|
||||
public UxrAxis Perpendicular => (_axis + 1) % 3;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the other perpendicular axis.
|
||||
/// </summary>
|
||||
public UxrAxis OtherPerpendicular => (_axis + 2) % 3;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="axis">Axis as an integer value</param>
|
||||
public UxrAxis(int axis)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
if (axis < 0 || axis > 3)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} Assigning invalid value to axis: {axis}");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
_axis = Mathf.Clamp(axis, 0, 2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IEquatable<UxrAxis>
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(UxrAxis other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _axis == other._axis;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return _axis switch
|
||||
{
|
||||
0 => "Right",
|
||||
1 => "Up",
|
||||
_ => "Forward"
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as UxrAxis);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _axis.GetHashCode();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns the remaining axis which is not axis1 nor axis2.
|
||||
/// </summary>
|
||||
/// <param name="axis1">Axis 1</param>
|
||||
/// <param name="axis2">Axis 2</param>
|
||||
/// <returns>The remaining axis which is not axis1 nor axis2</returns>
|
||||
public static UxrAxis OtherThan(UxrAxis axis1, UxrAxis axis2)
|
||||
{
|
||||
if (axis1 == axis2)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} {nameof(UxrAxis)}: Got same axis for {nameof(OtherThan)} (axis1)");
|
||||
}
|
||||
|
||||
return axis1.Perpendicular;
|
||||
}
|
||||
|
||||
int smaller = axis1._axis < axis2._axis ? axis1._axis : axis2._axis;
|
||||
int bigger = axis1._axis > axis2._axis ? axis1._axis : axis2._axis;
|
||||
|
||||
if (smaller == 0 && bigger == 1)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
if (smaller == 0 && bigger == 2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color representing
|
||||
/// </summary>
|
||||
/// <param name="alpha"></param>
|
||||
/// <returns></returns>
|
||||
public Color GetColor(float alpha)
|
||||
{
|
||||
Vector3 axis = this;
|
||||
return new Color(Mathf.Abs(axis.x), Mathf.Abs(axis.y), Mathf.Abs(axis.z), alpha);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Unary minus operator. Negates the axis value.
|
||||
/// </summary>
|
||||
/// <param name="axis">Axis to negate</param>
|
||||
/// <returns>Negated axis</returns>
|
||||
public static Vector3 operator -(UxrAxis axis)
|
||||
{
|
||||
return -(Vector3)axis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion from an <see cref="UxrAxis" /> to a <see cref="Vector3" />.
|
||||
/// </summary>
|
||||
/// <param name="axis">Axis to convert</param>
|
||||
/// <returns>Converted <see cref="Vector3" /> value</returns>
|
||||
public static implicit operator Vector3(UxrAxis axis)
|
||||
{
|
||||
return axis._axis switch
|
||||
{
|
||||
0 => Vector3.right,
|
||||
1 => Vector3.up,
|
||||
_ => Vector3.forward
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion from an integer to an <see cref="UxrAxis" />.
|
||||
/// </summary>
|
||||
/// <param name="axis">Integer to convert</param>
|
||||
/// <returns>Converted <see cref="UxrAxis" /> value</returns>
|
||||
public static implicit operator UxrAxis(int axis)
|
||||
{
|
||||
return new UxrAxis(axis);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equality operator.
|
||||
/// </summary>
|
||||
/// <param name="a">Operand A</param>
|
||||
/// <param name="b">Operand B</param>
|
||||
/// <returns>Whether the two operands are equal</returns>
|
||||
public static bool operator ==(UxrAxis a, UxrAxis b)
|
||||
{
|
||||
if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inequality operator.
|
||||
/// </summary>
|
||||
/// <param name="a">Operand A</param>
|
||||
/// <param name="b">Operand B</param>
|
||||
/// <returns>Whether the two operands are different</returns>
|
||||
public static bool operator !=(UxrAxis a, UxrAxis b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion from an <see cref="UxrAxis" /> to an integer.
|
||||
/// </summary>
|
||||
/// <param name="axis">Axis to convert</param>
|
||||
/// <returns>Int value (0 = right, 1 = up, 2 = forward)</returns>
|
||||
public static implicit operator int(UxrAxis axis)
|
||||
{
|
||||
return axis._axis;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrAxis.cs.meta
Normal file
13
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrAxis.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79ee905395498e5428d29fba7ef65416
|
||||
timeCreated: 1538564096
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
193
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrMathUtils.cs
Normal file
193
Assets/UltimateXR/Runtime/Scripts/Core/Math/UxrMathUtils.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrMathUtils.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains math computations involving elements that do not belong to a specific class.
|
||||
/// Most math is available through extensions classes in namespaces such as
|
||||
/// <see cref="UltimateXR.Extensions.System.Math">UltimateXR.Extensions.System.Math</see> or
|
||||
/// <see cref="UltimateXR.Extensions.Unity.Math">UltimateXR.Extensions.Unity.Math</see>.
|
||||
/// Math related to animation is also available through classes in namespaces such as
|
||||
/// <see cref="UltimateXR.Animation.IK">UltimateXR.Animation.IK</see>,
|
||||
/// <see cref="UltimateXR.Animation.Interpolation">UltimateXRAnimation.Interpolation</see> or
|
||||
/// <see cref="UltimateXR.Animation.Splines">UltimateXR.Animation.Splines</see>.
|
||||
/// This class will contain math functionality that cannot be assigned to any extensions class.
|
||||
/// </summary>
|
||||
public static class UxrMathUtils
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the intersection(s) between a 2D line and a 2D circle
|
||||
/// Code from: http://csharphelper.com/blog/2014/09/determine-where-a-line-intersects-a-circle-in-c/
|
||||
/// </summary>
|
||||
/// <param name="linePoint1">Point A in the line</param>
|
||||
/// <param name="linePoint2">Point B in the line</param>
|
||||
/// <param name="circlePos">Circle position</param>
|
||||
/// <param name="radius">Circle radius</param>
|
||||
/// <param name="intersection1">Intersection 1 result, if it exists</param>
|
||||
/// <param name="intersection2">Intersection 2 result, if it exists</param>
|
||||
/// <returns>Number of intersections found (0, 1 or 2)</returns>
|
||||
public static int FindLineCircleIntersections2D(Vector2 linePoint1, Vector2 linePoint2, Vector2 circlePos, float radius, out Vector2 intersection1, out Vector2 intersection2)
|
||||
{
|
||||
float t;
|
||||
float dx = linePoint2.x - linePoint1.x;
|
||||
float dy = linePoint2.y - linePoint1.y;
|
||||
float a = dx * dx + dy * dy;
|
||||
float b = 2 * (dx * (linePoint1.x - circlePos.x) + dy * (linePoint1.y - circlePos.y));
|
||||
float c = (linePoint1.x - circlePos.x) * (linePoint1.x - circlePos.x) + (linePoint1.y - circlePos.y) * (linePoint1.y - circlePos.y) - radius * radius;
|
||||
float det = b * b - 4 * a * c;
|
||||
|
||||
if (a <= 0.0000001 || det < 0)
|
||||
{
|
||||
// No real solutions.
|
||||
intersection1 = new Vector3(float.NaN, float.NaN);
|
||||
intersection2 = new Vector3(float.NaN, float.NaN);
|
||||
return 0;
|
||||
}
|
||||
if (det == 0)
|
||||
{
|
||||
// One solution.
|
||||
t = -b / (2 * a);
|
||||
intersection1 = new Vector3(linePoint1.x + t * dx, linePoint1.y + t * dy);
|
||||
intersection2 = new Vector3(float.NaN, float.NaN);
|
||||
return 1;
|
||||
}
|
||||
// Two solutions.
|
||||
t = (-b + Mathf.Sqrt(det)) / (2 * a);
|
||||
intersection1 = new Vector3(linePoint1.x + t * dx, linePoint1.y + t * dy);
|
||||
t = (-b - Mathf.Sqrt(det)) / (2 * a);
|
||||
intersection2 = new Vector3(linePoint1.x + t * dx, linePoint1.y + t * dy);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies to <paramref name="position" /> and <paramref name="rotation" /> the transformation to make a transform
|
||||
/// defined by <paramref name="sourcePosition" /> and <paramref name="sourceRotation" /> move and rotate to
|
||||
/// <paramref name="targetPosition" /> and <paramref name="targetRotation" />.
|
||||
/// </summary>
|
||||
/// <param name="position">Position to apply the transformation to</param>
|
||||
/// <param name="rotation">Rotation to apply the transformation to</param>
|
||||
/// <param name="sourcePosition">Source position that will try to match <paramref name="targetPosition" /></param>
|
||||
/// <param name="sourceRotation">Source rotation that will try to match <paramref name="targetRotation" /></param>
|
||||
/// <param name="targetPosition">Target position</param>
|
||||
/// <param name="targetRotation">Target rotation</param>
|
||||
/// <param name="rotate">Allows to control whether to rotate or not</param>
|
||||
/// <param name="translate">Allows to control whether to translate or not</param>
|
||||
/// <param name="t">Optional interpolation value [0.0, 1.0]</param>
|
||||
public static void ApplyAlignment(ref Vector3 position,
|
||||
ref Quaternion rotation,
|
||||
Vector3 sourcePosition,
|
||||
Quaternion sourceRotation,
|
||||
Vector3 targetPosition,
|
||||
Quaternion targetRotation,
|
||||
bool rotate,
|
||||
bool translate,
|
||||
float t = 1.0f)
|
||||
{
|
||||
if (rotate)
|
||||
{
|
||||
rotation.ApplyAlignment(sourceRotation, targetRotation, t);
|
||||
}
|
||||
|
||||
if (translate)
|
||||
{
|
||||
position += (targetPosition - sourcePosition) * t;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a box is completely (all corners) inside a BoxCollider
|
||||
/// </summary>
|
||||
/// <param name="boxPosition">Position of the box to test if it is inside the BoxCollider</param>
|
||||
/// <param name="boxRotation">Rotation of the box to test if it is inside the BoxCollider</param>
|
||||
/// <param name="boxScale">Scale of the box to test if it is inside the BoxCollider</param>
|
||||
/// <param name="boxCenter">Center of the box (in local coordinates) to test if it is inside the BoxCollider</param>
|
||||
/// <param name="boxSize">Size of the box (in local coordinates) to test if it is inside the BoxCollider</param>
|
||||
/// <param name="boxVolume">BoxCollider to test against</param>
|
||||
/// <param name="margin">Allowed margin for each x, y, z component</param>
|
||||
/// <returns>True if all corners are inside the BoxCollider plus margin</returns>
|
||||
public static bool IsBoxInsideBox(Vector3 boxPosition, Quaternion boxRotation, Vector3 boxScale, Vector3 boxCenter, Vector3 boxSize, BoxCollider boxVolume, Vector3 margin = default)
|
||||
{
|
||||
Matrix4x4 boxMatrix = Matrix4x4.TRS(boxPosition, boxRotation, boxScale);
|
||||
Vector3[] corners = new Vector3[8];
|
||||
|
||||
corners[0] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(+boxSize.x, +boxSize.y, +boxSize.z) * 0.5f);
|
||||
corners[1] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(+boxSize.x, +boxSize.y, -boxSize.z) * 0.5f);
|
||||
corners[2] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(+boxSize.x, -boxSize.y, +boxSize.z) * 0.5f);
|
||||
corners[3] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(+boxSize.x, -boxSize.y, -boxSize.z) * 0.5f);
|
||||
corners[4] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(-boxSize.x, +boxSize.y, +boxSize.z) * 0.5f);
|
||||
corners[5] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(-boxSize.x, +boxSize.y, -boxSize.z) * 0.5f);
|
||||
corners[6] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(-boxSize.x, -boxSize.y, +boxSize.z) * 0.5f);
|
||||
corners[7] = boxMatrix.MultiplyPoint(boxCenter + new Vector3(-boxSize.x, -boxSize.y, -boxSize.z) * 0.5f);
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
if (!corners[i].IsInsideBox(boxVolume, margin))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if box1 is completely (all corners) inside box2
|
||||
/// </summary>
|
||||
/// <param name="box1Position">Position of box1</param>
|
||||
/// <param name="box1Rotation">Rotation of box1</param>
|
||||
/// <param name="box1Scale">Scale of box1</param>
|
||||
/// <param name="box1Center">Center of box1 in its own local coordinates</param>
|
||||
/// <param name="box1Size">Size of box1 in its own local coordinates</param>
|
||||
/// <param name="box2Position">Position of box2</param>
|
||||
/// <param name="box2Rotation">Rotation of box2</param>
|
||||
/// <param name="box2Scale">Scale of box2</param>
|
||||
/// <param name="box2Center">Center of box2 in its own local coordinates</param>
|
||||
/// <param name="box2Size">Size of box2 in its own local coordinates</param>
|
||||
/// <param name="margin">Allowed margin for each x, y, z component</param>
|
||||
/// <returns>True if all corners of box1 are inside box2 plus margin</returns>
|
||||
public static bool IsBoxInsideBox(Vector3 box1Position,
|
||||
Quaternion box1Rotation,
|
||||
Vector3 box1Scale,
|
||||
Vector3 box1Center,
|
||||
Vector3 box1Size,
|
||||
Vector3 box2Position,
|
||||
Quaternion box2Rotation,
|
||||
Vector3 box2Scale,
|
||||
Vector3 box2Center,
|
||||
Vector3 box2Size,
|
||||
Vector3 margin = default)
|
||||
{
|
||||
Matrix4x4 boxMatrix = Matrix4x4.TRS(box1Position, box1Rotation, box1Scale);
|
||||
Vector3[] corners = new Vector3[8];
|
||||
|
||||
corners[0] = boxMatrix.MultiplyPoint(box1Center + new Vector3(+box1Size.x, +box1Size.y, +box1Size.z) * 0.5f);
|
||||
corners[1] = boxMatrix.MultiplyPoint(box1Center + new Vector3(+box1Size.x, +box1Size.y, -box1Size.z) * 0.5f);
|
||||
corners[2] = boxMatrix.MultiplyPoint(box1Center + new Vector3(+box1Size.x, -box1Size.y, +box1Size.z) * 0.5f);
|
||||
corners[3] = boxMatrix.MultiplyPoint(box1Center + new Vector3(+box1Size.x, -box1Size.y, -box1Size.z) * 0.5f);
|
||||
corners[4] = boxMatrix.MultiplyPoint(box1Center + new Vector3(-box1Size.x, +box1Size.y, +box1Size.z) * 0.5f);
|
||||
corners[5] = boxMatrix.MultiplyPoint(box1Center + new Vector3(-box1Size.x, +box1Size.y, -box1Size.z) * 0.5f);
|
||||
corners[6] = boxMatrix.MultiplyPoint(box1Center + new Vector3(-box1Size.x, -box1Size.y, +box1Size.z) * 0.5f);
|
||||
corners[7] = boxMatrix.MultiplyPoint(box1Center + new Vector3(-box1Size.x, -box1Size.y, -box1Size.z) * 0.5f);
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
if (!corners[i].IsInsideBox(box2Position, box2Rotation, box2Scale, box2Center, box2Size, margin))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94fda00f50a40c245b7a2825b2448da9
|
||||
timeCreated: 1501489697
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,317 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrUniversalLocalAxes.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Different parts of the framework need to deal with axes. These algorithms like IK solvers or avatar
|
||||
/// components need to know exactly where 'forward' is or which axis points to the right in avatar-space.
|
||||
/// Since modelling packages and artists may rig objects using arbitrary coordinate systems we need a way to
|
||||
/// perform operations in a way that takes this into account. The code also needs to remain readable since many
|
||||
/// math operations may increase complexity. Readability is favoured here over performance.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This class allows to transform from arbitrary coordinate systems to a universal one where different rotations
|
||||
/// can then be performed and vice versa.
|
||||
/// One example would be a finger bone curl. We create the convention that forward is the axis from one bone to
|
||||
/// the next, up points upwards and right would be the axis around which the bone should rotate to curl. This is OK
|
||||
/// but now we face the problem that different modelling packages or artists rig fingers in completely different
|
||||
/// ways using all varieties of axis systems. The purpose of this class is to help creating a system where
|
||||
/// operations can be performed in this universal system to follow our conventions and then rotated "back" to any
|
||||
/// kind of coordinate system afterwards.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// tl;dr A class that helps us operate with rotations and angles of an object no matter which convention the
|
||||
/// 3D assets use. We call 'Universal' the coordinate system we use as convention for our computations, we then
|
||||
/// can use <see cref="UniversalToActualAxesRotation" /> to transform the object back to its actual axes.
|
||||
/// This way our computations do not care which coordinate system the assets use, and is essential to simplify
|
||||
/// operations like inverse kinematics or angle computations.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrUniversalLocalAxes
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Transform _transform;
|
||||
[SerializeField] private Vector3 _localRight = Vector3.right;
|
||||
[SerializeField] private Vector3 _localUp = Vector3.up;
|
||||
[SerializeField] private Vector3 _localForward = Vector3.forward;
|
||||
[SerializeField] private Quaternion _universalToActualAxesRotation = Quaternion.identity;
|
||||
[SerializeField] private Quaternion _initialRotation = Quaternion.identity;
|
||||
[SerializeField] private Quaternion _initialLocalRotation = Quaternion.identity;
|
||||
[SerializeField] private Quaternion _initialLocalReferenceRotation = Quaternion.identity;
|
||||
[SerializeField] private Quaternion _initialUniversalLocalReferenceRotation = Quaternion.identity;
|
||||
[SerializeField] private Vector3 _initialPosition = Vector3.zero;
|
||||
[SerializeField] private Vector3 _initialLocalPosition = Vector3.zero;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'right' direction in world space.
|
||||
/// </summary>
|
||||
public Vector3 WorldRight => _transform.TransformDirection(LocalRight);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'up' direction in world space.
|
||||
/// </summary>
|
||||
public Vector3 WorldUp => _transform.TransformDirection(LocalUp);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'forward' direction in world space.
|
||||
/// </summary>
|
||||
public Vector3 WorldForward => _transform.TransformDirection(LocalForward);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local object rotation in universal convention
|
||||
/// </summary>
|
||||
public Quaternion UniversalLocalRotation => _transform.localRotation * Quaternion.Inverse(UniversalToActualAxesRotation);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object rotation in universal convention
|
||||
/// </summary>
|
||||
public Quaternion UniversalRotation => _transform.rotation * Quaternion.Inverse(UniversalToActualAxesRotation);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'right' direction in transform's local space.
|
||||
/// </summary>
|
||||
public Vector3 LocalRight
|
||||
{
|
||||
get => _localRight;
|
||||
private set => _localRight = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'up' direction in transform's local space.
|
||||
/// </summary>
|
||||
public Vector3 LocalUp
|
||||
{
|
||||
get => _localUp;
|
||||
private set => _localUp = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal 'forward' direction in transform's local space.
|
||||
/// </summary>
|
||||
public Vector3 LocalForward
|
||||
{
|
||||
get => _localForward;
|
||||
private set => _localForward = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation that transforms from the universal axes to the convention that the transform follows.
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // universalRotation may be a rotation around the y axis, where we know
|
||||
/// // exactly that y points upwards in that space.
|
||||
/// // This rotation will rotate an object around the "universal" y axis no
|
||||
/// // matter where his actual axes point to.
|
||||
/// transform.rotation = universalRotation * UniversalToActualAxesRotation;
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
public Quaternion UniversalToActualAxesRotation
|
||||
{
|
||||
get => _universalToActualAxesRotation;
|
||||
private set => _universalToActualAxesRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's rotation at the time of setting the object up.
|
||||
/// </summary>
|
||||
public Quaternion InitialRotation
|
||||
{
|
||||
get => _initialRotation;
|
||||
private set => _initialRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's local rotation at the time of setting the object up.
|
||||
/// </summary>
|
||||
public Quaternion InitialLocalRotation
|
||||
{
|
||||
get => _initialLocalRotation;
|
||||
private set => _initialLocalRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's rotation with respect to the reference transform at the time of setting the object up.
|
||||
/// This will only contain a rotation when the constructor using a reference transform was used.
|
||||
/// </summary>
|
||||
public Quaternion InitialLocalReferenceRotation
|
||||
{
|
||||
get => _initialLocalReferenceRotation;
|
||||
private set => _initialLocalReferenceRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's rotation (in universal coordinates) with respect to the reference transform at the time of
|
||||
/// setting the object up.
|
||||
/// This will only contain a rotation when the constructor using a reference transform was used.
|
||||
/// </summary>
|
||||
public Quaternion InitialUniversalLocalReferenceRotation
|
||||
{
|
||||
get => _initialUniversalLocalReferenceRotation;
|
||||
private set => _initialUniversalLocalReferenceRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's position at the time of setting the object up.
|
||||
/// </summary>
|
||||
public Vector3 InitialPosition
|
||||
{
|
||||
get => _initialPosition;
|
||||
private set => _initialPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform's local position at the time of setting the object up.
|
||||
/// </summary>
|
||||
public Vector3 InitialLocalPosition
|
||||
{
|
||||
get => _initialLocalPosition;
|
||||
private set => _initialLocalPosition = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// Uses universalReference to check which axes of a transform are actually the ones
|
||||
/// that are right, up and forward. For example, universalReference may be the avatar
|
||||
/// root where we know that right, up and forward point to these actual directions and
|
||||
/// we want to know which axes of an upper body part point to these directions too.
|
||||
/// These may be completely different depending on the modelling package or artist.
|
||||
/// Using this class we can easily check which one points upwards and create a small
|
||||
/// chest torsion by rotating around this axis.
|
||||
/// </summary>
|
||||
/// <param name="transform">Transform to create the universal axes for</param>
|
||||
/// <param name="universalReference">
|
||||
/// The transform to use as a reference for the universal right, up and forward directions.
|
||||
/// </param>
|
||||
public UxrUniversalLocalAxes(Transform transform, Transform universalReference)
|
||||
{
|
||||
_transform = transform;
|
||||
|
||||
LocalRight = transform.InverseTransformDirection(universalReference != null ? universalReference.right : Vector3.right).GetClosestAxis();
|
||||
LocalUp = transform.InverseTransformDirection(universalReference != null ? universalReference.up : Vector3.up).GetClosestAxis();
|
||||
LocalForward = transform.InverseTransformDirection(universalReference != null ? universalReference.forward : Vector3.forward).GetClosestAxis();
|
||||
|
||||
UniversalToActualAxesRotation = GetUniversalToActualAxesRotation();
|
||||
|
||||
InitialRotation = transform.rotation;
|
||||
InitialLocalRotation = transform.localRotation;
|
||||
InitialLocalReferenceRotation = Quaternion.Inverse(universalReference.rotation) * transform.rotation;
|
||||
InitialUniversalLocalReferenceRotation = InitialLocalReferenceRotation * Quaternion.Inverse(UniversalToActualAxesRotation);
|
||||
InitialPosition = transform.position;
|
||||
InitialLocalPosition = transform.localPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor is private to make it inaccessible.
|
||||
/// </summary>
|
||||
private UxrUniversalLocalAxes()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates a UniversalLocalAxes object describing the universal local axes for the given transform.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transform the UniversalLocalAxes object is for</param>
|
||||
/// <param name="universalLocalRight">
|
||||
/// Which vector in the transform local coordinates points to the 'right' direction in the universal convention
|
||||
/// </param>
|
||||
/// <param name="universalLocalUp">
|
||||
/// Which vector in the transform local coordinates points to the 'up' direction in the universal convention
|
||||
/// </param>
|
||||
/// <param name="universalLocalForward">
|
||||
/// Which vector in the transform local coordinates points to the 'forward' direction in the universal convention
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// UniversalLocalAxes object that allows us to compute object rotations in a universal
|
||||
/// space and then apply it to a transform that can have any kind of axis convention
|
||||
/// (x may point up, z down...)
|
||||
/// </returns>
|
||||
public static UxrUniversalLocalAxes FromAxes(Transform transform, Vector3 universalLocalRight, Vector3 universalLocalUp, Vector3 universalLocalForward)
|
||||
{
|
||||
UxrUniversalLocalAxes localAxes = new UxrUniversalLocalAxes();
|
||||
|
||||
localAxes._transform = transform;
|
||||
localAxes.LocalRight = universalLocalRight;
|
||||
localAxes.LocalUp = universalLocalUp;
|
||||
localAxes.LocalForward = universalLocalForward;
|
||||
|
||||
localAxes.InitialRotation = transform.rotation;
|
||||
localAxes.InitialLocalRotation = transform.localRotation;
|
||||
localAxes.InitialLocalReferenceRotation = Quaternion.identity;
|
||||
localAxes.InitialUniversalLocalReferenceRotation = Quaternion.identity;
|
||||
localAxes.InitialPosition = transform.position;
|
||||
localAxes.InitialLocalPosition = transform.localPosition;
|
||||
|
||||
localAxes.UniversalToActualAxesRotation = localAxes.GetUniversalToActualAxesRotation();
|
||||
|
||||
return localAxes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="FromAxes" />.
|
||||
/// </summary>
|
||||
public static UxrUniversalLocalAxes FromRightUp(Transform transform, Vector3 universalLocalRight, Vector3 universalLocalUp)
|
||||
{
|
||||
return FromAxes(transform, universalLocalRight, universalLocalUp, Vector3.Cross(universalLocalRight, universalLocalUp));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="FromAxes" />.
|
||||
/// </summary>
|
||||
public static UxrUniversalLocalAxes FromRightForward(Transform transform, Vector3 universalLocalRight, Vector3 universalLocalForward)
|
||||
{
|
||||
return FromAxes(transform, universalLocalRight, Vector3.Cross(universalLocalForward, universalLocalRight), universalLocalForward);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="FromAxes" />.
|
||||
/// </summary>
|
||||
public static UxrUniversalLocalAxes FromUpForward(Transform transform, Vector3 universalLocalUp, Vector3 universalLocalForward)
|
||||
{
|
||||
return FromAxes(transform, Vector3.Cross(universalLocalUp, universalLocalForward), universalLocalUp, universalLocalForward);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Computes the rotation that transforms from the universal coordinate system to the convention that the transform
|
||||
/// follows.
|
||||
/// </summary>
|
||||
private Quaternion GetUniversalToActualAxesRotation()
|
||||
{
|
||||
Matrix4x4 matrix = new Matrix4x4();
|
||||
matrix.SetColumn(0, LocalRight);
|
||||
matrix.SetColumn(1, LocalUp);
|
||||
matrix.SetColumn(2, LocalForward);
|
||||
matrix.SetColumn(3, new Vector4(0, 0, 0, 1));
|
||||
|
||||
return Quaternion.Inverse(matrix.rotation);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92dff33db2971124397cffe499bf092a
|
||||
timeCreated: 1549525217
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2a7e91ab0a2ab049b171744aece6cb2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="IUxrSerializable.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Core.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface to add serialization capabilities to a class.
|
||||
/// </summary>
|
||||
public interface IUxrSerializable
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current serialization version of the class that implements the interface. It has the same goal as
|
||||
/// <see cref="UxrConstants.Serialization.CurrentBinaryVersion" /> but this version property is specific to each class
|
||||
/// that implements the <see cref="IUxrSerializable" /> interface, which may be used outside the UltimateXR scope,
|
||||
/// in user specific classes that want to benefit from serialization.<br />
|
||||
/// Each class that implement the <see cref="IUxrSerializable" /> interface may have its own version. It is a number
|
||||
/// that gets incremented by one each time the serialization format of the class that implements this interface
|
||||
/// changes, enabling backwards compatibility.
|
||||
/// </summary>
|
||||
int SerializationVersion { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes the object. The serializer interface uses the same methods to serialize and to
|
||||
/// deserialize, instead of requiring separate serialization and deserialization methods.
|
||||
/// This simplifies implementations and helps eliminating bugs due to inconsistencies between reading and writing.
|
||||
/// </summary>
|
||||
/// <param name="serializer">Serializer to use</param>
|
||||
/// <param name="serializationVersion">
|
||||
/// When reading it tells the <see cref="SerializationVersion" /> the data was
|
||||
/// serialized with. When writing it uses the latest <see cref="SerializationVersion" /> version.
|
||||
/// </param>
|
||||
void Serialize(IUxrSerializer serializer, int serializationVersion);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2aa06f29947837479c280d8f6e6cc07
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,293 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="IUxrSerializer.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Core.Math;
|
||||
using UltimateXR.Core.Unique;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface to serialize and deserialize data. It uses a single method that performs the correct operation (read or
|
||||
/// write) transparently, avoiding inconsistencies that can happen when using separate serialize/deserialize methods
|
||||
/// on complex data with versioning.
|
||||
/// </summary>
|
||||
public interface IUxrSerializer : IDisposable
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the serialization version. When reading it tells the version the data was serialized with. When writing it
|
||||
/// uses the latest version.
|
||||
/// </summary>
|
||||
int Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the operation is writing data (serializing) or reading data (deserializing).
|
||||
/// </summary>
|
||||
bool IsReading { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a boolean value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes an sbyte value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref sbyte value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a byte value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref byte value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a char value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref char value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes an int value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref int value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a uint value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref uint value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a long value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref long value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a ulong value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref ulong value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a float value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref float value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes double value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref double value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes decimal value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref decimal value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes string value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
/// <remarks>Implementations must support serializing null strings</remarks>
|
||||
void Serialize(ref string value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes an Enum value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void SerializeEnum<T>(ref T value) where T : Enum;
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a type value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref Type value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a <see cref="Guid" /> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref Guid value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a tuple.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize<T1, T2>(ref (T1, T2) value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes an array.
|
||||
/// </summary>
|
||||
/// <param name="values">The elements to serialize or deserialize</param>
|
||||
/// <typeparam name="T">The type of the elements</typeparam>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void Serialize<T>(ref T[] values);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes an array, where each element might be of a different type. The type of each element will
|
||||
/// be stored next to the object.
|
||||
/// </summary>
|
||||
/// <param name="values">The elements to serialize or deserialize</param>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void Serialize(ref object[] values);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a list.
|
||||
/// </summary>
|
||||
/// <param name="values">The elements to serialize or deserialize</param>
|
||||
/// <typeparam name="T">The type of the elements</typeparam>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void Serialize<T>(ref List<T> values);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a list of objects, where each element might be of a different type. The type of each
|
||||
/// element will be stored next to the object.
|
||||
/// </summary>
|
||||
/// <param name="values">The elements to serialize or deserialize</param>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void Serialize(ref List<object> values);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a hash set.
|
||||
/// </summary>
|
||||
/// <param name="values">The elements to serialize or deserialize</param>
|
||||
/// <typeparam name="T">The type of the elements</typeparam>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void Serialize<T>(ref HashSet<T> values);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a hash set of objects, where each element might be of a different type. The type of each
|
||||
/// element will be stored next to the object.
|
||||
/// </summary>
|
||||
/// <param name="values">The elements to serialize or deserialize</param>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void Serialize(ref HashSet<object> values);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a dictionary.
|
||||
/// </summary>
|
||||
/// <param name="values">The values</param>
|
||||
/// <typeparam name="TKey">The key type</typeparam>
|
||||
/// <typeparam name="TValue">The value type</typeparam>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void Serialize<TKey, TValue>(ref Dictionary<TKey, TValue> values);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a <see cref="DateTime" /> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref DateTime dateTime);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a <see cref="TimeSpan" /> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref TimeSpan timeSpan);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a Vector2 value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref Vector2 value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a Vector3 value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref Vector3 value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a Vector4 value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref Vector4 value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes Color value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref Color value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes Color32 value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref Color32 value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes Quaternion value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref Quaternion value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes Matrix4x4 value.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to serialize or deserialize</param>
|
||||
void Serialize(ref Matrix4x4 value);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a component with the <see cref="IUxrUniqueId" /> interface, storing only the
|
||||
/// <see cref="IUxrUniqueId.UniqueId" />.
|
||||
/// </summary>
|
||||
/// <param name="unique">The element to serialize or deserialize</param>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void SerializeUniqueComponent(ref IUxrUniqueId unique);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes a component with the <see cref="IUxrUniqueId" /> interface, storing only the
|
||||
/// <see cref="IUxrUniqueId.UniqueId" />.
|
||||
/// </summary>
|
||||
/// <param name="component">The element to serialize or deserialize</param>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void SerializeUniqueComponent<T>(ref T component) where T : Component, IUxrUniqueId;
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes an object that implements the <see cref="IUxrSerializable" /> interface.
|
||||
/// </summary>
|
||||
/// <param name="serializable">The element to serialize or deserialize</param>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void SerializeUxrSerializable(ref IUxrSerializable serializable);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes an object that implements the <see cref="IUxrSerializable" /> interface.
|
||||
/// </summary>
|
||||
/// <param name="obj">The element to serialize or deserialize</param>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void SerializeUxrSerializable<T>(ref T obj) where T : IUxrSerializable;
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes an <see cref="UxrAxis" /> value.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis to serialize or deserialize</param>
|
||||
void SerializeAxis(ref UxrAxis axis);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a variable of a type that is known only at runtime. When writing it will serialize the type
|
||||
/// together with the value so that it can be deserialized back when reading.
|
||||
/// </summary>
|
||||
/// <param name="obj">The element to serialize or deserialize</param>
|
||||
/// <remarks>Implementations must support null</remarks>
|
||||
void SerializeAnyVar<T>(ref T obj);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee82d6ded7c93524daf62a5181c40d91
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,587 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrBinarySerializer.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UltimateXR.Core.Math;
|
||||
using UltimateXR.Core.Unique;
|
||||
using UltimateXR.Extensions.System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that helps serializing/deserializing data using a single method instead of two separate Serialize
|
||||
/// and Deserialize methods. It also stores the serialization version when reading or writing data.
|
||||
/// </summary>
|
||||
public class UxrBinarySerializer : IUxrSerializer
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for deserialization.
|
||||
/// </summary>
|
||||
/// <param name="reader">Binary reader with the data</param>
|
||||
/// <param name="version">Version that the data was serialized with</param>
|
||||
public UxrBinarySerializer(BinaryReader reader, int version)
|
||||
{
|
||||
Version = version;
|
||||
IsReading = true;
|
||||
Reader = reader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for serialization.
|
||||
/// </summary>
|
||||
/// <param name="writer">Binary writer to output the data</param>
|
||||
public UxrBinarySerializer(BinaryWriter writer)
|
||||
{
|
||||
Version = UxrConstants.Serialization.CurrentBinaryVersion;
|
||||
IsReading = false;
|
||||
Writer = writer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrSerializer
|
||||
|
||||
/// <summary>
|
||||
/// Gets, when reading, the version that the data was serialized with. When writing, it gets the latest version that it
|
||||
/// is being serialized with, which is equal to <see cref="UxrConstants.Serialization.CurrentBinaryVersion" />.
|
||||
/// </summary>
|
||||
public int Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the serializer is reading (using <see cref="Reader" />) or writing (using <see cref="Writer" />).
|
||||
/// </summary>
|
||||
public bool IsReading { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref bool value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadBoolean();
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref sbyte value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadSByte();
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref byte value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadByte();
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref char value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadChar();
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref int value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadCompressedInt32(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteCompressedInt32(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref uint value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadCompressedUInt32(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteCompressedUInt32(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref long value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadCompressedInt64(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteCompressedInt64(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref ulong value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadCompressedUInt64(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteCompressedUInt64(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref float value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadSingle();
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref double value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadDouble();
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref decimal value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadDecimal();
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref string value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
bool nullCheck = Reader.ReadBoolean();
|
||||
value = nullCheck ? Reader.ReadString() : null;
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value != null);
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeEnum<T>(ref T value) where T : Enum
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadEnum<T>(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteEnum(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Type value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadType(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Guid value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadGuid(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize<T1, T2>(ref (T1, T2) value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadTuple<T1, T2>(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize<T>(ref T[] values)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
values = Reader.ReadArray<T>(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(values);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref object[] values)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
values = Reader.ReadObjectArray(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(values);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize<T>(ref List<T> values)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
values = Reader.ReadList<T>(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(values);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref List<object> values)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
values = Reader.ReadObjectList(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(values);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize<TKey, TValue>(ref Dictionary<TKey, TValue> values)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
values = Reader.ReadDictionary<TKey, TValue>(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(values);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize<T>(ref HashSet<T> values)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
values = Reader.ReadHashSet<T>(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(values);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref HashSet<object> values)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
values = Reader.ReadObjectHashSet(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(values);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref DateTime value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadDateTime(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref TimeSpan value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadTimeSpan(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Vector2 value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadVector2(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Vector3 value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadVector3(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Vector4 value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadVector4(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Color value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadColor(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Color32 value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadColor32(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Quaternion value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadQuaternion(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Matrix4x4 value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
value = Reader.ReadMatrix(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeUniqueComponent(ref IUxrUniqueId unique)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
unique = Reader.ReadUniqueComponent(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteUniqueComponent(unique);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeUniqueComponent<T>(ref T component) where T : Component, IUxrUniqueId
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
component = Reader.ReadUniqueComponent(Version) as T;
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteUniqueComponent(component);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeUxrSerializable(ref IUxrSerializable serializable)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
serializable = Reader.ReadUxrSerializable(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteUxrSerializable(serializable);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeUxrSerializable<T>(ref T obj) where T : IUxrSerializable
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
obj = (T)Reader.ReadUxrSerializable(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteUxrSerializable(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeAxis(ref UxrAxis axis)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
axis = Reader.ReadAxis(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteAxis(axis);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeAnyVar<T>(ref T obj)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
obj = (T)Reader.ReadAnyVar(Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteAnyVar(obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reader, if <see cref="IsReading" /> is true. Otherwise it is null.
|
||||
/// </summary>
|
||||
private BinaryReader Reader { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the writer, if <see cref="IsReading" /> is false. Otherwise it is null.
|
||||
/// </summary>
|
||||
private BinaryWriter Writer { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b432ceb3955a0343838c776354833dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,259 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrDummySerializer.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Core.Math;
|
||||
using UltimateXR.Core.StateSave;
|
||||
using UltimateXR.Core.Unique;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializer that doesn't read or write data. It can be used by <see cref="IUxrStateSave" /> implementations to cache
|
||||
/// initial object data without any read or write operations.
|
||||
/// </summary>
|
||||
public class UxrDummySerializer : IUxrSerializer
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for deserialization.
|
||||
/// </summary>
|
||||
/// <param name="isReading">Whether to initialize in read mode or write mode</param>
|
||||
/// <param name="version">Version that the data was serialized with</param>
|
||||
public UxrDummySerializer(bool isReading, int version)
|
||||
{
|
||||
Version = version;
|
||||
IsReading = isReading;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrSerializer
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Version { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsReading { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref bool value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref sbyte value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref byte value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref char value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref int value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref uint value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref long value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref ulong value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref float value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref double value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref decimal value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref string value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeEnum<T>(ref T value) where T : Enum
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Type value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Guid value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize<T1, T2>(ref (T1, T2) value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize<T>(ref T[] values)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref object[] values)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize<T>(ref List<T> values)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref List<object> values)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize<TKey, TValue>(ref Dictionary<TKey, TValue> values)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize<T>(ref HashSet<T> values)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref HashSet<object> values)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref DateTime value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref TimeSpan value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Vector2 value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Vector3 value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Vector4 value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Color value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Color32 value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Quaternion value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(ref Matrix4x4 value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeUniqueComponent(ref IUxrUniqueId unique)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeUniqueComponent<T>(ref T component) where T : Component, IUxrUniqueId
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeUxrSerializable(ref IUxrSerializable serializable)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeUxrSerializable<T>(ref T obj) where T : IUxrSerializable
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeAxis(ref UxrAxis axis)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SerializeAnyVar<T>(ref T obj)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dummy serializer initialized in read mode.
|
||||
/// </summary>
|
||||
public static UxrDummySerializer ReadModeSerializer = new UxrDummySerializer(true, UxrConstants.Serialization.CurrentBinaryVersion);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dummy serializer initialized in write mode.
|
||||
/// </summary>
|
||||
public static UxrDummySerializer WriteModeSerializer = new UxrDummySerializer(false, UxrConstants.Serialization.CurrentBinaryVersion);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 483be337e05e7b64786f8dedfb2a7eb5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrSerializationFormat.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Core.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different serialization formats that are supported.
|
||||
/// </summary>
|
||||
public enum UxrSerializationFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// Binary uncompressed format.
|
||||
/// </summary>
|
||||
BinaryUncompressed,
|
||||
|
||||
/// <summary>
|
||||
/// Binary compressed using Gzip.
|
||||
/// </summary>
|
||||
BinaryGzip
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9c879fce5d1bfb45b743e14774e4e79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,68 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrVarType.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.StateSync;
|
||||
|
||||
namespace UltimateXR.Core.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the variable types supported by serialization, for example
|
||||
/// <see cref="UxrPropertyChangedSyncEventArgs" />.
|
||||
/// </summary>
|
||||
public enum UxrVarType
|
||||
{
|
||||
Unknown = 0,
|
||||
|
||||
// C#
|
||||
|
||||
Bool = 1,
|
||||
SignedByte = 2,
|
||||
Byte = 3,
|
||||
Char = 4,
|
||||
Int = 5,
|
||||
UnsignedInt = 6,
|
||||
Long = 7,
|
||||
UnsignedLong = 8,
|
||||
Float = 9,
|
||||
Double = 10,
|
||||
Decimal = 11,
|
||||
String = 12,
|
||||
Enum = 13,
|
||||
Type = 20,
|
||||
Guid = 21,
|
||||
Tuple = 22,
|
||||
|
||||
// C# collections
|
||||
|
||||
Array = 30,
|
||||
ObjectArray = 31,
|
||||
List = 32,
|
||||
ObjectList = 33,
|
||||
Dictionary = 34,
|
||||
HashSet = 35,
|
||||
ObjectHashSet = 36,
|
||||
|
||||
// Other C# types
|
||||
|
||||
DateTime = 50,
|
||||
TimeSpan = 51,
|
||||
|
||||
// Unity
|
||||
|
||||
Vector2 = 100,
|
||||
Vector3 = 101,
|
||||
Vector4 = 102,
|
||||
Color = 103,
|
||||
Color32 = 104,
|
||||
Quaternion = 105,
|
||||
Matrix4x4 = 106,
|
||||
|
||||
// UXR
|
||||
|
||||
IUxrUnique = 200,
|
||||
IUxrSerializable = 201,
|
||||
UxrAxis = 202
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfeeeeda25242ae40886a7415517952b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,216 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrVarTypeExt.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Core.Math;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Core.Unique;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for <see cref="UxrVarType" />.
|
||||
/// </summary>
|
||||
public static class UxrVarTypeExt
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the type of an object that support serialization/deserialization using UltimateXR.
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to get the type of</param>
|
||||
/// <returns>Type from <see cref="UxrVarType" /> or <see cref="UxrVarType.Unknown" /> if not supported</returns>
|
||||
public static UxrVarType GetType<T>(T obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return UxrVarType.Unknown;
|
||||
}
|
||||
|
||||
Type type = obj.GetType();
|
||||
|
||||
// C# types
|
||||
|
||||
if (obj is bool)
|
||||
{
|
||||
return UxrVarType.Bool;
|
||||
}
|
||||
|
||||
if (obj is sbyte)
|
||||
{
|
||||
return UxrVarType.SignedByte;
|
||||
}
|
||||
|
||||
if (obj is byte)
|
||||
{
|
||||
return UxrVarType.Byte;
|
||||
}
|
||||
|
||||
if (obj is char)
|
||||
{
|
||||
return UxrVarType.Char;
|
||||
}
|
||||
|
||||
if (obj is int)
|
||||
{
|
||||
return UxrVarType.Int;
|
||||
}
|
||||
|
||||
if (obj is uint)
|
||||
{
|
||||
return UxrVarType.UnsignedInt;
|
||||
}
|
||||
|
||||
if (obj is long)
|
||||
{
|
||||
return UxrVarType.Long;
|
||||
}
|
||||
|
||||
if (obj is ulong)
|
||||
{
|
||||
return UxrVarType.UnsignedLong;
|
||||
}
|
||||
|
||||
if (obj is float)
|
||||
{
|
||||
return UxrVarType.Float;
|
||||
}
|
||||
|
||||
if (obj is double)
|
||||
{
|
||||
return UxrVarType.Double;
|
||||
}
|
||||
|
||||
if (obj is decimal)
|
||||
{
|
||||
return UxrVarType.Decimal;
|
||||
}
|
||||
|
||||
if (obj is string)
|
||||
{
|
||||
return UxrVarType.String;
|
||||
}
|
||||
|
||||
if (obj is Enum)
|
||||
{
|
||||
return UxrVarType.Enum;
|
||||
}
|
||||
|
||||
if (obj is Type)
|
||||
{
|
||||
return UxrVarType.Type;
|
||||
}
|
||||
|
||||
if (obj is Guid)
|
||||
{
|
||||
return UxrVarType.Guid;
|
||||
}
|
||||
|
||||
if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(Tuple<,>) || type.GetGenericTypeDefinition() == typeof(ValueTuple<,>)))
|
||||
{
|
||||
return UxrVarType.Tuple;
|
||||
}
|
||||
|
||||
// C# collections
|
||||
|
||||
if (type.IsArray)
|
||||
{
|
||||
return type.GetElementType() == typeof(object) ? UxrVarType.ObjectArray : UxrVarType.Array;
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
return type.GetElementType() == typeof(object) ? UxrVarType.ObjectList : UxrVarType.List;
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
{
|
||||
return UxrVarType.Dictionary;
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(HashSet<>))
|
||||
{
|
||||
return type.GetElementType() == typeof(object) ? UxrVarType.ObjectHashSet : UxrVarType.HashSet;
|
||||
}
|
||||
|
||||
// Other C# types
|
||||
|
||||
if (obj is DateTime)
|
||||
{
|
||||
return UxrVarType.DateTime;
|
||||
}
|
||||
|
||||
if (obj is TimeSpan)
|
||||
{
|
||||
return UxrVarType.TimeSpan;
|
||||
}
|
||||
|
||||
// Unity types
|
||||
|
||||
if (obj is Vector2)
|
||||
{
|
||||
return UxrVarType.Vector2;
|
||||
}
|
||||
|
||||
if (obj is Vector3)
|
||||
{
|
||||
return UxrVarType.Vector3;
|
||||
}
|
||||
|
||||
if (obj is Vector4)
|
||||
{
|
||||
return UxrVarType.Vector4;
|
||||
}
|
||||
|
||||
if (obj is Color)
|
||||
{
|
||||
return UxrVarType.Color;
|
||||
}
|
||||
|
||||
if (obj is Color32)
|
||||
{
|
||||
return UxrVarType.Color32;
|
||||
}
|
||||
|
||||
if (obj is Quaternion)
|
||||
{
|
||||
return UxrVarType.Quaternion;
|
||||
}
|
||||
|
||||
if (obj is Matrix4x4)
|
||||
{
|
||||
return UxrVarType.Matrix4x4;
|
||||
}
|
||||
|
||||
// UXR types
|
||||
|
||||
if (obj is IUxrUniqueId)
|
||||
{
|
||||
return UxrVarType.IUxrUnique;
|
||||
}
|
||||
|
||||
if (obj is IUxrSerializable)
|
||||
{
|
||||
return UxrVarType.IUxrSerializable;
|
||||
}
|
||||
|
||||
if (obj is UxrAxis)
|
||||
{
|
||||
return UxrVarType.UxrAxis;
|
||||
}
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} Unknown type in {nameof(GetType)} ({type.FullName})");
|
||||
}
|
||||
|
||||
return UxrVarType.Unknown;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a01bc1f70562035458a24f0420087494
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/UltimateXR/Runtime/Scripts/Core/Settings.meta
Normal file
8
Assets/UltimateXR/Runtime/Scripts/Core/Settings.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5c2c07e2d3697948bbbb560b2f23a70
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,245 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGlobalSettings.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.IO;
|
||||
using UltimateXR.Core.Serialization;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// UltimateXR global settings. The global settings is stores in a file called UxrGlobalSettings inside a /Resources
|
||||
/// folder so that it can be loaded at runtime.
|
||||
/// It can be accessed using the Tools->UltimateXR Unity menu.
|
||||
/// </summary>
|
||||
public class UxrGlobalSettings : ScriptableObject
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrLogLevel _logLevelAnimation = UxrLogLevel.Warnings;
|
||||
[SerializeField] private UxrLogLevel _logLevelAvatar = UxrLogLevel.Warnings;
|
||||
[SerializeField] private UxrLogLevel _logLevelCore = UxrLogLevel.Relevant;
|
||||
[SerializeField] private UxrLogLevel _logLevelDevices = UxrLogLevel.Relevant;
|
||||
[SerializeField] private UxrLogLevel _logLevelLocomotion = UxrLogLevel.Relevant;
|
||||
[SerializeField] private UxrLogLevel _logLevelManipulation = UxrLogLevel.Relevant;
|
||||
[SerializeField] private UxrLogLevel _logLevelNetworking = UxrLogLevel.Relevant;
|
||||
[SerializeField] private UxrLogLevel _logLevelRendering = UxrLogLevel.Relevant;
|
||||
[SerializeField] private UxrLogLevel _logLevelUI = UxrLogLevel.Warnings;
|
||||
[SerializeField] private UxrLogLevel _logLevelWeapons = UxrLogLevel.Relevant;
|
||||
|
||||
[SerializeField] private UxrSerializationFormat _netFormatInitialState = UxrSerializationFormat.BinaryGzip;
|
||||
[SerializeField] private UxrSerializationFormat _netFormatStateSync = UxrSerializationFormat.BinaryUncompressed;
|
||||
[SerializeField] private bool _syncGrabbablePhysics = true;
|
||||
[SerializeField] private float _grabbableSyncIntervalSeconds = UxrConstants.Networking.DefaultGrabbableSyncIntervalSeconds;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the global settings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Global settings must be stored in a file called UxrGlobalSettings inside a /Resources folder.
|
||||
/// If no global settings could be found, default settings are used
|
||||
/// </remarks>
|
||||
public static UxrGlobalSettings Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_current == null)
|
||||
{
|
||||
s_current = Resources.Load<UxrGlobalSettings>(nameof(UxrGlobalSettings));
|
||||
|
||||
if (s_current == null)
|
||||
{
|
||||
s_current = CreateInstance<UxrGlobalSettings>();
|
||||
}
|
||||
}
|
||||
|
||||
return s_current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log level for animation events.
|
||||
/// </summary>
|
||||
public UxrLogLevel LogLevelAnimation
|
||||
{
|
||||
get => _logLevelAnimation;
|
||||
set => _logLevelAnimation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log level for avatar events.
|
||||
/// </summary>
|
||||
public UxrLogLevel LogLevelAvatar
|
||||
{
|
||||
get => _logLevelAvatar;
|
||||
set => _logLevelAvatar = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log level for core events.
|
||||
/// </summary>
|
||||
public UxrLogLevel LogLevelCore
|
||||
{
|
||||
get => _logLevelCore;
|
||||
set => _logLevelCore = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log level for controller input device events.
|
||||
/// </summary>
|
||||
public UxrLogLevel LogLevelDevices
|
||||
{
|
||||
get => _logLevelDevices;
|
||||
set => _logLevelDevices = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log level for locomotion events.
|
||||
/// </summary>
|
||||
public UxrLogLevel LogLevelLocomotion
|
||||
{
|
||||
get => _logLevelLocomotion;
|
||||
set => _logLevelLocomotion = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log level for manipulation events.
|
||||
/// </summary>
|
||||
public UxrLogLevel LogLevelManipulation
|
||||
{
|
||||
get => _logLevelManipulation;
|
||||
set => _logLevelManipulation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log level for networking events.
|
||||
/// </summary>
|
||||
public UxrLogLevel LogLevelNetworking
|
||||
{
|
||||
get => _logLevelNetworking;
|
||||
set => _logLevelNetworking = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log level for rendering events.
|
||||
/// </summary>
|
||||
public UxrLogLevel LogLevelRendering
|
||||
{
|
||||
get => _logLevelRendering;
|
||||
set => _logLevelRendering = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log level for UI events.
|
||||
/// </summary>
|
||||
public UxrLogLevel LogLevelUI
|
||||
{
|
||||
get => _logLevelUI;
|
||||
set => _logLevelUI = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log level for weapon events.
|
||||
/// </summary>
|
||||
public UxrLogLevel LogLevelWeapons
|
||||
{
|
||||
get => _logLevelWeapons;
|
||||
set => _logLevelWeapons = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format of the network message that contains the initial state of the scene upon joining.
|
||||
/// </summary>
|
||||
public UxrSerializationFormat NetFormatInitialState
|
||||
{
|
||||
get => _netFormatInitialState;
|
||||
set => _netFormatInitialState = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format of the network messages to synchronize state changes.
|
||||
/// </summary>
|
||||
public UxrSerializationFormat NetFormatStateSync
|
||||
{
|
||||
get => _netFormatStateSync;
|
||||
set => _netFormatStateSync = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to manually sync physics-driven grabbable objects that do not have native networking
|
||||
/// components such as NetworkTransform/NetworkRigidbody.
|
||||
/// </summary>
|
||||
public bool SyncGrabbablePhysics
|
||||
{
|
||||
get => _syncGrabbablePhysics;
|
||||
set => _syncGrabbablePhysics = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets, when using <see cref="SyncGrabbablePhysics" />, the interval in seconds synchronization messages are
|
||||
/// sent.
|
||||
/// </summary>
|
||||
public float GrabbableSyncIntervalSeconds
|
||||
{
|
||||
get => _grabbableSyncIntervalSeconds;
|
||||
set => _grabbableSyncIntervalSeconds = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
/// <summary>
|
||||
/// Accesses the global settings using the Unity menu.
|
||||
/// </summary>
|
||||
[MenuItem(UxrConstants.Editor.MenuPathRoot + "Global Settings", priority = UxrConstants.Editor.PriorityMenuPathRoot)]
|
||||
public static void SelectInProject()
|
||||
{
|
||||
UxrGlobalSettings settings = Resources.Load<UxrGlobalSettings>(nameof(UxrGlobalSettings));
|
||||
|
||||
if (settings == null)
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Create global settings?", "Global settings file has not yet been created. Create one now?", "Yes", "Cancel"))
|
||||
{
|
||||
settings = CreateInstance<UxrGlobalSettings>();
|
||||
|
||||
string resourcesFolder = "Resources";
|
||||
string directory = $"{Application.dataPath}/{resourcesFolder}/";
|
||||
|
||||
if (!File.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
AssetDatabase.CreateAsset(settings, $"Assets/{resourcesFolder}/{nameof(UxrGlobalSettings)}.asset");
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Selection.activeObject = settings;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private static UxrGlobalSettings s_current;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b3db1704eb60974193c9750a4ac54e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/UltimateXR/Runtime/Scripts/Core/StateSave.meta
Normal file
8
Assets/UltimateXR/Runtime/Scripts/Core/StateSave.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1fdcd23b97672e45bc3130ba3dd44a3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,102 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="IUxrStateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Animation.Interpolation;
|
||||
using UltimateXR.Core.Serialization;
|
||||
using UltimateXR.Core.Unique;
|
||||
|
||||
namespace UltimateXR.Core.StateSave
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for components to load/save their partial or complete state. This can be used to save
|
||||
/// complete sessions to disk to restore the session later. It can also be used to save partial states
|
||||
/// in a timeline to implement replay functionality.<br />
|
||||
/// To leverage the implementation of this interface, consider using <see cref="UxrStateSaveImplementer{T}" />.<br />
|
||||
/// </summary>
|
||||
public interface IUxrStateSave : IUxrUniqueId
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current serialization version of the component type that implements the interface. It has the same goal as
|
||||
/// <see cref="UxrConstants.Serialization.CurrentBinaryVersion" /> but this version property is specific to each class
|
||||
/// that implements the <see cref="IUxrStateSave" /> interface, which may be used outside the UltimateXR scope,
|
||||
/// in user specific classes that want to benefit from state serialization.<br />
|
||||
/// Each class that implement the <see cref="IUxrStateSave" /> interface may have its own version. It is a number that
|
||||
/// gets incremented by one each time the serialization format of the class that implements this interface changes,
|
||||
/// enabling backwards compatibility.
|
||||
/// </summary>
|
||||
int StateSerializationVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the serialization order of the component. Components are serialized from lower to higher order values.
|
||||
/// </summary>
|
||||
int SerializationOrder { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the component state should be save even when it's disabled.
|
||||
/// This can be useful in components that have state changes even when being disabled. An example is when a
|
||||
/// disabled component is subscribed to an event and the event triggers changes in the component.
|
||||
/// </summary>
|
||||
bool SaveStateWhenDisabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether to save the enabled state of the component and the active state of the object.
|
||||
/// </summary>
|
||||
bool SerializeActiveAndEnabledState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the space the transform will be serialized in.
|
||||
/// </summary>
|
||||
UxrTransformSpace TransformStateSaveSpace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state save monitor.
|
||||
/// </summary>
|
||||
UxrStateSaveMonitor StateSaveMonitor { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transform should be serialized when serializing the state.
|
||||
/// </summary>
|
||||
/// <param name="level">The amount of data to serialize</param>
|
||||
/// <returns>Whether the transform should be serialized</returns>
|
||||
bool RequiresTransformSerialization(UxrStateSaveLevel level);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes or deserializes the component state.
|
||||
/// </summary>
|
||||
/// <param name="serializer">Serializer to use</param>
|
||||
/// <param name="stateSerializationVersion">
|
||||
/// When reading it tells the <see cref="StateSerializationVersion" /> the data was
|
||||
/// serialized with. When writing it uses the latest <see cref="StateSerializationVersion" /> version.
|
||||
/// </param>
|
||||
/// <param name="level">
|
||||
/// The amount of data to serialize.
|
||||
/// </param>
|
||||
/// <param name="options">Options</param>
|
||||
/// <returns>Whether there were any values in the state that changed</returns>
|
||||
bool SerializeState(IUxrSerializer serializer, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options = UxrStateSaveOptions.None);
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates state variables.
|
||||
/// </summary>
|
||||
/// <param name="vars">Variable names with their old and new values</param>
|
||||
/// <param name="t">Interpolation value [0.0, 1.0]</param>
|
||||
void InterpolateState(in UxrStateInterpolationVars vars, float t);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the interpolator for a given variable, allowing to use customize interpolation for different variables.
|
||||
/// </summary>
|
||||
/// <param name="varName">Name of the variable to get the interpolator for</param>
|
||||
/// <returns>Interpolator or null to not interpolate</returns>
|
||||
UxrVarInterpolator GetInterpolator(string varName);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2454fcd46309ac41b6faac7633c00fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateInterpolationVars.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UltimateXR.Core.StateSave
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores variables that can be interpolated during a frame.
|
||||
/// </summary>
|
||||
public struct UxrStateInterpolationVars
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the variables as a dictionary where the keys are the variable names and the values are tuples (oldValue,
|
||||
/// newValue).
|
||||
/// </summary>
|
||||
public IDictionary<string, (object OldValue, object NewValue)> Values { get; internal set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="values">Values</param>
|
||||
public UxrStateInterpolationVars(IDictionary<string, (object OldValue, object NewValue)> values)
|
||||
{
|
||||
Values = values;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6aad1af8823038042bf1d11124653655
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,108 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateSaveEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core.Serialization;
|
||||
|
||||
namespace UltimateXR.Core.StateSave
|
||||
{
|
||||
/// <summary>
|
||||
/// Event args for <see cref="UxrStateSaveMonitor" />.
|
||||
/// </summary>
|
||||
public class UxrStateSaveEventArgs : EventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the serializer. Useful to know whether it is reading or writing using <see cref="IUxrSerializer.IsReading" />.
|
||||
/// </summary>
|
||||
public IUxrSerializer Serializer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the serialization level.
|
||||
/// </summary>
|
||||
public UxrStateSaveLevel Level { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the serialization options.
|
||||
/// </summary>
|
||||
public UxrStateSaveOptions Options { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the serialized var name. Only for <see cref="UxrStateSaveMonitor.VarSerializing" /> and
|
||||
/// <see cref="UxrStateSaveMonitor.VarSerialized" />, or global events
|
||||
/// <see cref="UxrStateSaveImplementer.VarSerializing" /> and <see cref="UxrStateSaveImplementer.VarSerialized" />.
|
||||
/// </summary>
|
||||
public string VarName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value. For <see cref="UxrStateSaveMonitor.VarSerializing" /> and
|
||||
/// <see cref="UxrStateSaveMonitor.VarSerialized" /> it will contain the value before serialization. For
|
||||
/// <see cref="UxrStateSaveImplementer.VarSerializing" /> and <see cref="UxrStateSaveImplementer.VarSerialized" /> it
|
||||
/// will contain the value after serialization.
|
||||
/// </summary>
|
||||
public object Value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// For <see cref="UxrStateSaveMonitor.VarSerializing" /> and <see cref="UxrStateSaveMonitor.VarSerialized" /> it will
|
||||
/// contain the same value as <see cref="Value" />.
|
||||
/// For <see cref="UxrStateSaveImplementer.VarSerializing" /> and <see cref="UxrStateSaveImplementer.VarSerialized" />
|
||||
/// it will contain the value before serialization.
|
||||
/// In this case, <see cref="OldValue" /> will contain the value before serialization and <see cref="Value" /> will
|
||||
/// contain the value after serialization.
|
||||
/// </summary>
|
||||
public object OldValue { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="serializer">The serializer</param>
|
||||
/// <param name="level">The serialization level</param>
|
||||
/// <param name="options">The serialization options</param>
|
||||
/// <param name="name">The variable name or null when not serializing any var</param>
|
||||
/// <param name="value">The variable value or null when not serializing any var</param>
|
||||
/// <param name="oldValue">The value of the variable before assigning the new one. Only when reading.</param>
|
||||
public UxrStateSaveEventArgs(IUxrSerializer serializer, UxrStateSaveLevel level, UxrStateSaveOptions options, string name, object value, object oldValue = null)
|
||||
{
|
||||
Set(serializer, level, options, name, value, oldValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default Constructor.
|
||||
/// </summary>
|
||||
internal UxrStateSaveEventArgs()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current values.
|
||||
/// </summary>
|
||||
/// <param name="serializer">The serializer</param>
|
||||
/// <param name="level">The serialization level</param>
|
||||
/// <param name="options">The serialization options</param>
|
||||
/// <param name="name">The variable name or null when not serializing any var</param>
|
||||
/// <param name="value">The variable value or null when not serializing any var</param>
|
||||
/// <param name="oldValue">Only when deserializing, tells the old value before assigning the new one</param>
|
||||
public void Set(IUxrSerializer serializer, UxrStateSaveLevel level, UxrStateSaveOptions options, string name, object value, object oldValue = null)
|
||||
{
|
||||
Serializer = serializer;
|
||||
Level = level;
|
||||
Options = options;
|
||||
VarName = name;
|
||||
Value = value;
|
||||
OldValue = oldValue;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cf8c16cf48d45198ac7dce795dde9d9
|
||||
timeCreated: 1708596129
|
||||
@@ -0,0 +1,344 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateSaveImplementer.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Core.Components.Singleton;
|
||||
using UltimateXR.Core.Instantiation;
|
||||
using UltimateXR.Core.Serialization;
|
||||
|
||||
namespace UltimateXR.Core.StateSave
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for <see cref="UxrStateSaveImplementer{T}" />.
|
||||
/// It provides access to global state serialization events and components.
|
||||
/// </summary>
|
||||
public abstract class UxrStateSaveImplementer
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// The name assigned to the Transform when an object's Transform is being serialized.
|
||||
/// </summary>
|
||||
public const string SelfTransformVarName = "this.transform";
|
||||
|
||||
/// <summary>
|
||||
/// Event called when the state is about to be serialized using
|
||||
/// <see cref="UxrStateSaveImplementer{T}.SerializeState" />. The sender is the <see cref="IUxrStateSave" /> that is
|
||||
/// about to be serialized.
|
||||
/// </summary>
|
||||
public static event EventHandler<UxrStateSaveEventArgs> StateSerializing;
|
||||
|
||||
/// <summary>
|
||||
/// Event called when the state finished serializing using <see cref="UxrStateSaveImplementer{T}.SerializeState" />.
|
||||
/// The sender is the <see cref="IUxrStateSave" /> that was serialized.
|
||||
/// </summary>
|
||||
public static event EventHandler<UxrStateSaveEventArgs> StateSerialized;
|
||||
|
||||
/// <summary>
|
||||
/// Event called when a state variable is about to be serialized inside
|
||||
/// <see cref="UxrStateSaveImplementer{T}.SerializeState" />. The sender is the <see cref="IUxrStateSave" /> that is
|
||||
/// about to be serialized.
|
||||
/// </summary>
|
||||
public static event EventHandler<UxrStateSaveEventArgs> VarSerializing;
|
||||
|
||||
/// <summary>
|
||||
/// Event called when a state variable finished serializing inside
|
||||
/// <see cref="UxrStateSaveImplementer{T}.SerializeState" />. The sender is the <see cref="IUxrStateSave" /> that was
|
||||
/// serialized.
|
||||
/// </summary>
|
||||
public static event EventHandler<UxrStateSaveEventArgs> VarSerialized;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the components with an <see cref="IUxrStateSave" /> interface that save any data.
|
||||
/// The order will ensure that the first component will be the <see cref="UxrInstanceManager" /> if it exists, then all
|
||||
/// the components with a <see cref="IUxrSingleton" /> interface and then the rest.<br />
|
||||
/// To get the components sorted by <see cref="IUxrStateSave.SerializationOrder" /> use
|
||||
/// <c>AllSerializableComponents.OrderBy(s => s.SerializationOrder);</c>
|
||||
/// </summary>
|
||||
public static IEnumerable<IUxrStateSave> AllSerializableComponents
|
||||
{
|
||||
get
|
||||
{
|
||||
if (UxrInstanceManager.HasInstance)
|
||||
{
|
||||
yield return UxrInstanceManager.Instance;
|
||||
}
|
||||
|
||||
foreach (IUxrStateSave stateSave in s_allSingletons)
|
||||
{
|
||||
yield return stateSave;
|
||||
}
|
||||
|
||||
foreach (IUxrStateSave stateSave in s_allComponents)
|
||||
{
|
||||
yield return stateSave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the enabled components with an <see cref="IUxrStateSave" /> interface that save any data.
|
||||
/// The order will ensure that the first component will be the <see cref="UxrInstanceManager" /> if it exists, then all
|
||||
/// the components with a <see cref="IUxrSingleton" /> interface and then the rest.<br />
|
||||
/// To get the components sorted by <see cref="IUxrStateSave.SerializationOrder" /> use
|
||||
/// <c>EnabledSerializableComponents.OrderBy(s => s.SerializationOrder);</c>
|
||||
/// </summary>
|
||||
public static IEnumerable<IUxrStateSave> EnabledSerializableComponents
|
||||
{
|
||||
get
|
||||
{
|
||||
if (UxrInstanceManager.HasInstance && UxrInstanceManager.Instance.isActiveAndEnabled)
|
||||
{
|
||||
yield return UxrInstanceManager.Instance;
|
||||
}
|
||||
|
||||
foreach (IUxrStateSave stateSave in s_enabledSingletons)
|
||||
{
|
||||
yield return stateSave;
|
||||
}
|
||||
|
||||
foreach (IUxrStateSave stateSave in s_enabledComponents)
|
||||
{
|
||||
yield return stateSave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the components that should be saved when saving the current state of the scene, such as
|
||||
/// when using <see cref="UxrManager.SaveStateChanges" />.
|
||||
/// This includes all the enabled components and the components with <see cref="IUxrStateSave.SaveStateWhenDisabled" />
|
||||
/// set.<br />
|
||||
/// To get the components sorted by <see cref="IUxrStateSave.SerializationOrder" /> use
|
||||
/// <c>SaveRequiredComponents.OrderBy(s => s.SerializationOrder);</c>
|
||||
/// </summary>
|
||||
public static IEnumerable<IUxrStateSave> SaveRequiredComponents
|
||||
{
|
||||
get
|
||||
{
|
||||
if (UxrInstanceManager.HasInstance)
|
||||
{
|
||||
yield return UxrInstanceManager.Instance;
|
||||
}
|
||||
|
||||
foreach (IUxrStateSave stateSave in s_saveRequiredSingletons)
|
||||
{
|
||||
yield return stateSave;
|
||||
}
|
||||
|
||||
foreach (IUxrStateSave stateSave in s_saveRequiredComponents)
|
||||
{
|
||||
yield return stateSave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the end of frame in the UltimateXR update process.
|
||||
/// </summary>
|
||||
internal static void NotifyEndOfFrame()
|
||||
{
|
||||
foreach (UxrStateSaveImplementer implementer in s_pendingStoreInitialStates)
|
||||
{
|
||||
implementer.StoreInitialState();
|
||||
}
|
||||
|
||||
s_pendingStoreInitialStates.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="StateSerializing" />.
|
||||
/// </summary>
|
||||
/// <param name="stateSave">The <see cref="IUxrStateSave" /> that is about to be serialized</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnStateSerializing(IUxrStateSave stateSave, UxrStateSaveEventArgs e)
|
||||
{
|
||||
StateSerializing?.Invoke(stateSave, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="StateSerialized" />.
|
||||
/// </summary>
|
||||
/// <param name="stateSave">The <see cref="IUxrStateSave" /> that was serialized</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnStateSerialized(IUxrStateSave stateSave, UxrStateSaveEventArgs e)
|
||||
{
|
||||
StateSerialized?.Invoke(stateSave, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="VarSerializing" />.
|
||||
/// </summary>
|
||||
/// <param name="stateSave">The <see cref="IUxrStateSave" /> that is about to be serialized</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnVarSerializing(IUxrStateSave stateSave, UxrStateSaveEventArgs e)
|
||||
{
|
||||
VarSerializing?.Invoke(stateSave, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="VarSerialized" />.
|
||||
/// </summary>
|
||||
/// <param name="stateSave">The <see cref="IUxrStateSave" /> that was serialized</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected virtual void OnVarSerialized(IUxrStateSave stateSave, UxrStateSaveEventArgs e)
|
||||
{
|
||||
VarSerialized?.Invoke(stateSave, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Stores the initial state of a component.
|
||||
/// </summary>
|
||||
protected abstract void StoreInitialState();
|
||||
|
||||
/// <summary>
|
||||
/// Registers the component.
|
||||
/// </summary>
|
||||
protected void RegisterComponent(IUxrStateSave stateSave)
|
||||
{
|
||||
if (stateSave is UxrInstanceManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Do a serialization test and check if there is any state saving. If not we can ignore it because this component doesn't save any data.
|
||||
|
||||
if (!stateSave.SerializeState(UxrDummySerializer.WriteModeSerializer, stateSave.StateSerializationVersion, UxrStateSaveLevel.Complete, UxrStateSaveOptions.DontSerialize | UxrStateSaveOptions.DontCacheChanges))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (stateSave is IUxrSingleton)
|
||||
{
|
||||
s_allSingletons.Add(stateSave);
|
||||
s_saveRequiredSingletons.Add(stateSave);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_allComponents.Add(stateSave);
|
||||
s_saveRequiredComponents.Add(stateSave);
|
||||
}
|
||||
|
||||
s_pendingStoreInitialStates.Add(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the component
|
||||
/// </summary>
|
||||
protected void UnregisterComponent(IUxrStateSave stateSave)
|
||||
{
|
||||
if (stateSave is UxrInstanceManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (stateSave is IUxrSingleton)
|
||||
{
|
||||
s_allSingletons.Remove(stateSave);
|
||||
s_saveRequiredSingletons.Remove(stateSave);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_allComponents.Remove(stateSave);
|
||||
s_saveRequiredComponents.Remove(stateSave);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the component has been enabled.
|
||||
/// </summary>
|
||||
protected void NotifyOnEnable(IUxrStateSave stateSave)
|
||||
{
|
||||
if (stateSave is UxrInstanceManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We only register components that have been filtered by RegisterComponent, we want to discard components that don't save data.
|
||||
|
||||
if (stateSave is IUxrSingleton)
|
||||
{
|
||||
if (s_allSingletons.Contains(stateSave))
|
||||
{
|
||||
s_enabledSingletons.Add(stateSave);
|
||||
}
|
||||
|
||||
if (!stateSave.SaveStateWhenDisabled)
|
||||
{
|
||||
s_saveRequiredSingletons.Add(stateSave);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_allComponents.Contains(stateSave))
|
||||
{
|
||||
s_enabledComponents.Add(stateSave);
|
||||
}
|
||||
|
||||
if (!stateSave.SaveStateWhenDisabled)
|
||||
{
|
||||
s_saveRequiredComponents.Add(stateSave);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the component has been disabled.
|
||||
/// </summary>
|
||||
protected void NotifyOnDisable(IUxrStateSave stateSave)
|
||||
{
|
||||
if (stateSave is UxrInstanceManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (stateSave is IUxrSingleton)
|
||||
{
|
||||
s_enabledSingletons.Remove(stateSave);
|
||||
|
||||
if (!stateSave.SaveStateWhenDisabled)
|
||||
{
|
||||
s_saveRequiredSingletons.Remove(stateSave);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s_enabledComponents.Remove(stateSave);
|
||||
|
||||
if (!stateSave.SaveStateWhenDisabled)
|
||||
{
|
||||
s_saveRequiredComponents.Remove(stateSave);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private static readonly HashSet<IUxrStateSave> s_allComponents = new HashSet<IUxrStateSave>();
|
||||
private static readonly HashSet<IUxrStateSave> s_enabledComponents = new HashSet<IUxrStateSave>();
|
||||
private static readonly HashSet<IUxrStateSave> s_saveRequiredComponents = new HashSet<IUxrStateSave>();
|
||||
private static readonly HashSet<IUxrStateSave> s_allSingletons = new HashSet<IUxrStateSave>();
|
||||
private static readonly HashSet<IUxrStateSave> s_enabledSingletons = new HashSet<IUxrStateSave>();
|
||||
private static readonly HashSet<IUxrStateSave> s_saveRequiredSingletons = new HashSet<IUxrStateSave>();
|
||||
private static readonly HashSet<UxrStateSaveImplementer> s_pendingStoreInitialStates = new HashSet<UxrStateSaveImplementer>();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29f84440537e3e04badad419a324d90c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,883 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateSaveImplementer_1.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using UltimateXR.Animation.Interpolation;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Core.Serialization;
|
||||
using UltimateXR.Core.Unique;
|
||||
using UnityEngine;
|
||||
using ObjectExt = UltimateXR.Extensions.System.ObjectExt;
|
||||
|
||||
namespace UltimateXR.Core.StateSave
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class simplifying the implementation of the <see cref="IUxrStateSave" /> interface.
|
||||
/// This class includes functionality to serialize the state of components.
|
||||
/// It is utilized by <see cref="UxrComponent" /> to implement <see cref="IUxrStateSave" />.
|
||||
/// In scenarios where custom classes cannot inherit from <see cref="UxrComponent" /> for state saving capabilities,
|
||||
/// this class is designed to implement the interface.
|
||||
/// </summary>
|
||||
public class UxrStateSaveImplementer<T> : UxrStateSaveImplementer where T : Component, IUxrStateSave
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// State serialization handler.
|
||||
/// </summary>
|
||||
public delegate void SerializeStateHandler(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// State interpolation handler.
|
||||
/// </summary>
|
||||
public delegate void InterpolateStateHandler(in UxrStateInterpolationVars vars, float t);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state save monitor.
|
||||
/// </summary>
|
||||
public UxrStateSaveMonitor Monitor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets incremented each time a value is serialized or deserialized using <see cref="SerializeStateValue{TV}" />.
|
||||
/// This can be used to check whether a value was actually serialized. When it doesn't get incremented, it means that
|
||||
/// the value didn't change (when writing) or doesn't need to change (when reading).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When a value doesn't need serialization because it didn't change, a boolean is serialized to tell that no change
|
||||
/// was made. If all values in a component didn't change, the component can be ignored to avoid writing many false
|
||||
/// booleans and save space. This still requires a first
|
||||
/// pass using <see cref="UxrStateSaveOptions.DontCacheChanges" /> and <see cref="UxrStateSaveOptions.DontSerialize" />
|
||||
/// .
|
||||
/// </remarks>
|
||||
public int SerializeCounter { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="targetComponent">Target component for all the methods called on this object</param>
|
||||
public UxrStateSaveImplementer(T targetComponent)
|
||||
{
|
||||
_targetComponent = targetComponent;
|
||||
Monitor = new UxrStateSaveMonitor(targetComponent);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Registers the component if necessary.
|
||||
/// </summary>
|
||||
public void RegisterIfNecessary()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_registered)
|
||||
{
|
||||
RegisterComponent(_targetComponent);
|
||||
_registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the component.
|
||||
/// </summary>
|
||||
public void Unregister()
|
||||
{
|
||||
base.UnregisterComponent(_targetComponent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the component has been enabled.
|
||||
/// </summary>
|
||||
public void NotifyOnEnable()
|
||||
{
|
||||
base.NotifyOnEnable(_targetComponent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the component has been disabled.
|
||||
/// </summary>
|
||||
public void NotifyOnDisable()
|
||||
{
|
||||
base.NotifyOnDisable(_targetComponent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the state of a component, handling serialization of different elements if necessary:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// The enabled state of the component and active state of the GameObject, if required by
|
||||
/// <see cref="IUxrStateSave.SerializeActiveAndEnabledState" />.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// The transform, using <see cref="IUxrStateSave.RequiresTransformSerialization" /> and
|
||||
/// <see cref="IUxrStateSave.TransformStateSaveSpace" />.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// After that, it hands over the serialization to <paramref name="customSerializeStateHandler" /> to handle the custom
|
||||
/// component serialization.
|
||||
/// </summary>
|
||||
/// <param name="serializer">The serializer</param>
|
||||
/// <param name="level">The amount of data to serialize</param>
|
||||
/// <param name="options">Options</param>
|
||||
/// <param name="customSerializeStateHandler">The handler that will serialize the remaining custom data</param>
|
||||
public void SerializeState(IUxrSerializer serializer, UxrStateSaveLevel level, UxrStateSaveOptions options, SerializeStateHandler customSerializeStateHandler)
|
||||
{
|
||||
if (_targetComponent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnStateSerializing(_targetComponent, GetStateSaveEventArgs(serializer, level, options));
|
||||
|
||||
// Enabled/Active states
|
||||
|
||||
if (_targetComponent.SerializeActiveAndEnabledState)
|
||||
{
|
||||
Behaviour behaviour = _targetComponent.Component;
|
||||
bool enabled = behaviour.enabled;
|
||||
bool active = _targetComponent.GameObject.activeSelf;
|
||||
bool enabledBefore = enabled;
|
||||
bool activeBefore = active;
|
||||
|
||||
SerializeStateValue(serializer, level, options, NameIsEnabled, ref enabled);
|
||||
SerializeStateValue(serializer, level, options, NameIsActive, ref active);
|
||||
|
||||
if (serializer.IsReading)
|
||||
{
|
||||
if (enabled != enabledBefore && behaviour != null)
|
||||
{
|
||||
behaviour.enabled = enabled;
|
||||
}
|
||||
|
||||
if (active != activeBefore)
|
||||
{
|
||||
_targetComponent.gameObject.SetActive(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transform
|
||||
|
||||
if (_targetComponent.RequiresTransformSerialization(level))
|
||||
{
|
||||
SerializeStateTransform(serializer, level, options, SelfTransformVarName, _targetComponent.TransformStateSaveSpace, _targetComponent.transform);
|
||||
}
|
||||
|
||||
// User custom serialization
|
||||
|
||||
customSerializeStateHandler?.Invoke(serializer.IsReading, _targetComponent.StateSerializationVersion, level, options);
|
||||
|
||||
OnStateSerialized(_targetComponent, GetStateSaveEventArgs(serializer, level, options));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a value only when necessary, depending on <paramref name="level" />, <paramref name="options" /> and if
|
||||
/// the value changed.<br />
|
||||
/// </summary>
|
||||
/// <param name="serializer">Serializer</param>
|
||||
/// <param name="level">The amount of data to serialize</param>
|
||||
/// <param name="options">Options</param>
|
||||
/// <param name="varName">
|
||||
/// The parameter name. It will be used to track value changes over time. If it is null or empty,
|
||||
/// it will be serialized without checking for value changes. The name must be unique to any other transform or value
|
||||
/// serialized for the target component using <see cref="SerializeStateValue{TV}" /> or
|
||||
/// <see cref="SerializeStateTransform" />.
|
||||
/// </param>
|
||||
/// <param name="value">A reference to the value being loaded/saved</param>
|
||||
[SuppressMessage("ReSharper", "InvokeAsExtensionMethod")]
|
||||
public void SerializeStateValue<TV>(IUxrSerializer serializer, UxrStateSaveLevel level, UxrStateSaveOptions options, string varName, ref TV value)
|
||||
{
|
||||
// Initialize dictionaries if necessary. We store a deep copy to be able to check for changes later by comparing values.
|
||||
|
||||
if (!string.IsNullOrEmpty(varName))
|
||||
{
|
||||
if (!_initialValues.ContainsKey(varName))
|
||||
{
|
||||
_initialValues.Add(varName, ObjectExt.DeepCopy(value));
|
||||
}
|
||||
else if (options.HasFlag(UxrStateSaveOptions.ResetChangesCache))
|
||||
{
|
||||
_initialValues[varName] = ObjectExt.DeepCopy(value);
|
||||
}
|
||||
|
||||
if (!_lastValues.ContainsKey(varName))
|
||||
{
|
||||
_lastValues.Add(varName, ObjectExt.DeepCopy(value));
|
||||
}
|
||||
else if (options.HasFlag(UxrStateSaveOptions.ResetChangesCache))
|
||||
{
|
||||
_lastValues[varName] = ObjectExt.DeepCopy(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Here we need to write read/write parts separately to implement comparison logic
|
||||
|
||||
bool serialize = false;
|
||||
|
||||
if (!serializer.IsReading)
|
||||
{
|
||||
// When writing, check if the data changed to export changes only.
|
||||
// We use a floating point precision threshold for specific types to help avoiding redundant writes.
|
||||
|
||||
if (options.HasFlag(UxrStateSaveOptions.DontCheckCache))
|
||||
{
|
||||
serialize = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
|
||||
case UxrStateSaveLevel.None: return;
|
||||
|
||||
case UxrStateSaveLevel.ChangesSinceBeginning:
|
||||
|
||||
serialize = varName == null || !ObjectExt.ValuesEqual(value, _initialValues[varName], UxrConstants.Math.DefaultPrecisionThreshold);
|
||||
break;
|
||||
|
||||
case UxrStateSaveLevel.ChangesSincePreviousSave:
|
||||
|
||||
serialize = varName == null || !ObjectExt.ValuesEqual(value, _lastValues[varName], UxrConstants.Math.DefaultPrecisionThreshold);
|
||||
break;
|
||||
|
||||
case UxrStateSaveLevel.Complete:
|
||||
|
||||
serialize = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.HasFlag(UxrStateSaveOptions.DontSerialize))
|
||||
{
|
||||
// When reading, will deserialize a boolean telling whether there is data to be deserialized. If a false value is deserialized as a result, it means that the previous value didn't change and no new data needs to be read.
|
||||
// When writing, will serialize a boolean telling whether any value will be written. If a false value is serialized, it means that the previous value didn't change and no data needs to be written.
|
||||
serializer.Serialize(ref serialize);
|
||||
}
|
||||
|
||||
if (serialize)
|
||||
{
|
||||
object oldValue = !string.IsNullOrEmpty(varName) ? ObjectExt.DeepCopy(value) : null;
|
||||
|
||||
OnVarSerializing(_targetComponent, GetStateSaveEventArgs(serializer, level, options, varName, value, oldValue));
|
||||
|
||||
if (!options.HasFlag(UxrStateSaveOptions.DontSerialize))
|
||||
{
|
||||
serializer.SerializeAnyVar(ref value);
|
||||
}
|
||||
|
||||
if (!options.HasFlag(UxrStateSaveOptions.DontCacheChanges) && !string.IsNullOrEmpty(varName))
|
||||
{
|
||||
_lastValues[varName] = ObjectExt.DeepCopy(value);
|
||||
}
|
||||
|
||||
SerializeCounter++;
|
||||
|
||||
OnVarSerialized(_targetComponent, GetStateSaveEventArgs(serializer, level, options, varName, value, oldValue));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes transform data.
|
||||
/// The Transform can be for the target component or any other component tracked by it, normally children in the
|
||||
/// hierarchy. For example, an avatar serializes the position of the head and hands.
|
||||
/// </summary>
|
||||
/// <param name="serializer">Serializer</param>
|
||||
/// <param name="level">The amount of data to serialize</param>
|
||||
/// <param name="options">Options</param>
|
||||
/// <param name="transformVarName">
|
||||
/// A name to identify the transform. It will be used to track value changes over time. If it is null or empty,
|
||||
/// it will be serialized without checking for value changes. The name must be unique to any other transform or
|
||||
/// value serialized for the target component using <see cref="SerializeStateValue{TV}" /> or
|
||||
/// <see cref="SerializeStateTransform" />.
|
||||
/// </param>
|
||||
/// <param name="space">
|
||||
/// The space the transform data is specified in, when writing. Scale will always be stored in local
|
||||
/// space.
|
||||
/// </param>
|
||||
/// <param name="transform">
|
||||
/// The transform to serialize. It can be the target component's Transform or any other transform serialized by the
|
||||
/// component.
|
||||
/// </param>
|
||||
public void SerializeStateTransform(IUxrSerializer serializer, UxrStateSaveLevel level, UxrStateSaveOptions options, string transformVarName, UxrTransformSpace space, Transform transform)
|
||||
{
|
||||
// Can't use ref with Transform property directly, so we need to implement read/write paths separately
|
||||
|
||||
if (serializer.IsReading)
|
||||
{
|
||||
// Read parent
|
||||
|
||||
IUxrUniqueId newUniqueParent = transform.parent != null ? transform.parent.GetComponent<IUxrUniqueId>() : null;
|
||||
IUxrUniqueId currentUniqueParent = newUniqueParent;
|
||||
SerializeStateValue(serializer, level, options, GetTransformVarName(transformVarName, NameTransformParent), ref newUniqueParent);
|
||||
|
||||
if (currentUniqueParent != newUniqueParent)
|
||||
{
|
||||
Debug.Log($"{transform.name} parent changed from {currentUniqueParent} to {newUniqueParent}");
|
||||
|
||||
if (newUniqueParent != null)
|
||||
{
|
||||
Component newParentComponent = newUniqueParent as Component;
|
||||
Transform newParentTransform = newParentComponent != null ? newParentComponent.transform : null;
|
||||
|
||||
if (newParentTransform != null)
|
||||
{
|
||||
if (transform.parent != null)
|
||||
{
|
||||
// If there is a current parent, only switch parent if the new parent has also a UniqueID
|
||||
|
||||
Component currentParentComponent = currentUniqueParent as Component;
|
||||
|
||||
if (currentParentComponent != null && newParentComponent != null && transform.parent != newParentComponent.transform)
|
||||
{
|
||||
transform.SetParent(newParentTransform);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is no current parent, switch parent to the new UniqueID
|
||||
|
||||
transform.SetParent(newParentTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentUniqueParent != null)
|
||||
{
|
||||
// If the new parent is null, switch only if the current parent has a UniqueID
|
||||
transform.SetParent(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read space
|
||||
|
||||
object boxedSpace = space;
|
||||
SerializeStateValue(serializer, level, options, GetTransformVarName(transformVarName, NameTransformSpace), ref boxedSpace);
|
||||
space = (UxrTransformSpace)boxedSpace;
|
||||
|
||||
object boxedPos = GetPosition(transform, space);
|
||||
object boxedRot = GetRotation(transform, space);
|
||||
object boxedScale = transform.localScale;
|
||||
|
||||
// Read position
|
||||
|
||||
int counterBefore = SerializeCounter;
|
||||
SerializeStateValue(serializer, level, options, GetTransformVarName(transformVarName, NameTransformPos), ref boxedPos);
|
||||
|
||||
if (counterBefore != SerializeCounter && !options.HasFlag(UxrStateSaveOptions.DontSerialize))
|
||||
{
|
||||
SetPosition(transform, space, (Vector3)boxedPos);
|
||||
}
|
||||
|
||||
// Read rotation
|
||||
|
||||
counterBefore = SerializeCounter;
|
||||
SerializeStateValue(serializer, level, options, GetTransformVarName(transformVarName, NameTransformRot), ref boxedRot);
|
||||
|
||||
if (counterBefore != SerializeCounter && !options.HasFlag(UxrStateSaveOptions.DontSerialize))
|
||||
{
|
||||
SetRotation(transform, space, (Quaternion)boxedRot);
|
||||
}
|
||||
|
||||
// Read scale
|
||||
|
||||
counterBefore = SerializeCounter;
|
||||
SerializeStateValue(serializer, level, options, GetTransformVarName(transformVarName, NameTransformScale), ref boxedScale);
|
||||
|
||||
if (counterBefore != SerializeCounter && !options.HasFlag(UxrStateSaveOptions.DontSerialize))
|
||||
{
|
||||
transform.localScale = (Vector3)boxedScale;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write parent
|
||||
|
||||
int counterBeforeParent = SerializeCounter;
|
||||
bool parentSerialized = false;
|
||||
|
||||
IUxrUniqueId uniqueParent = transform.parent != null ? transform.parent.GetComponent<IUxrUniqueId>() : null;
|
||||
SerializeStateValue(serializer, level, options, GetTransformVarName(transformVarName, NameTransformParent), ref uniqueParent);
|
||||
|
||||
if (counterBeforeParent != SerializeCounter)
|
||||
{
|
||||
parentSerialized = true;
|
||||
}
|
||||
|
||||
// Write space
|
||||
|
||||
object boxedSpace = space;
|
||||
SerializeStateValue(serializer, level, options, GetTransformVarName(transformVarName, NameTransformSpace), ref boxedSpace);
|
||||
|
||||
// Compute values
|
||||
|
||||
Vector3 position = GetPosition(transform, space);
|
||||
Quaternion rotation = GetRotation(transform, space);
|
||||
|
||||
// Write values
|
||||
|
||||
object boxedPos = position;
|
||||
object boxedRot = rotation;
|
||||
object boxedScale = transform.localScale;
|
||||
|
||||
if (parentSerialized)
|
||||
{
|
||||
// If the parent was serialized, it means that it changed or was forced to be serialized.
|
||||
// In this case we need to make sure that the transform components are serialized too.
|
||||
options |= UxrStateSaveOptions.DontCheckCache;
|
||||
}
|
||||
|
||||
SerializeStateValue(serializer, level, options, GetTransformVarName(transformVarName, NameTransformPos), ref boxedPos);
|
||||
SerializeStateValue(serializer, level, options, GetTransformVarName(transformVarName, NameTransformRot), ref boxedRot);
|
||||
SerializeStateValue(serializer, level, options, GetTransformVarName(transformVarName, NameTransformScale), ref boxedScale);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates state variables.
|
||||
/// </summary>
|
||||
/// <param name="vars">Contains the variables that can be interpolated for the target component</param>
|
||||
/// <param name="t">Interpolation value [0.0, 1.0]</param>
|
||||
/// <param name="customInterpolateStateHandler">The user-defined interpolate state handler for the component</param>
|
||||
/// <param name="getInterpolator">A function that gets the interpolator for a given serialized var</param>
|
||||
public void InterpolateState(in UxrStateInterpolationVars vars, float t, InterpolateStateHandler customInterpolateStateHandler, Func<string, UxrVarInterpolator> getInterpolator)
|
||||
{
|
||||
if (_targetComponent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InterpolateStateTransform(vars, t, SelfTransformVarName, _targetComponent.transform, _targetComponent.TransformStateSaveSpace, getInterpolator);
|
||||
|
||||
customInterpolateStateHandler?.Invoke(vars, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates a transform.
|
||||
/// </summary>
|
||||
/// <param name="vars">Contains the variables that can be interpolated for the target component</param>
|
||||
/// <param name="t">Interpolation value [0.0, 1.0]</param>
|
||||
/// <param name="transformVarName">
|
||||
/// The name assigned to the transform when serializing it using
|
||||
/// <see cref="SerializeStateTransform" />
|
||||
/// </param>
|
||||
/// <param name="targetTransform">The target transform</param>
|
||||
/// <param name="space">
|
||||
/// The space in which the transform data was serialized using <see cref="SerializeStateTransform" />
|
||||
/// </param>
|
||||
/// <param name="getInterpolator">A function that gets the interpolator for a given serialized var</param>
|
||||
public void InterpolateStateTransform(in UxrStateInterpolationVars vars, float t, string transformVarName, Transform targetTransform, UxrTransformSpace space, Func<string, UxrVarInterpolator> getInterpolator)
|
||||
{
|
||||
if (getInterpolator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string posVarName = GetTransformVarName(transformVarName, NameTransformPos);
|
||||
string rotVarName = GetTransformVarName(transformVarName, NameTransformRot);
|
||||
string scaleVarName = GetTransformVarName(transformVarName, NameTransformScale);
|
||||
|
||||
if (vars.Values.ContainsKey(posVarName))
|
||||
{
|
||||
if (getInterpolator(posVarName) is UxrVector3Interpolator positionInterpolator)
|
||||
{
|
||||
SetPosition(targetTransform, space, positionInterpolator.Interpolate((Vector3)vars.Values[posVarName].OldValue, (Vector3)vars.Values[posVarName].NewValue, t));
|
||||
}
|
||||
}
|
||||
|
||||
if (vars.Values.ContainsKey(rotVarName))
|
||||
{
|
||||
if (getInterpolator(rotVarName) is UxrQuaternionInterpolator rotationInterpolator)
|
||||
{
|
||||
SetRotation(targetTransform, space, rotationInterpolator.Interpolate((Quaternion)vars.Values[rotVarName].OldValue, (Quaternion)vars.Values[rotVarName].NewValue, t));
|
||||
}
|
||||
}
|
||||
|
||||
if (vars.Values.ContainsKey(scaleVarName))
|
||||
{
|
||||
if (getInterpolator(scaleVarName) is UxrVector3Interpolator scaleInterpolator)
|
||||
{
|
||||
targetTransform.localScale = scaleInterpolator.Interpolate((Vector3)vars.Values[scaleVarName].OldValue, (Vector3)vars.Values[scaleVarName].NewValue, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default interpolator for the given variable.
|
||||
/// </summary>
|
||||
/// <param name="varName">The variable name</param>
|
||||
/// <returns>Interpolator or null to not interpolate</returns>
|
||||
public UxrVarInterpolator GetDefaultInterpolator(string varName)
|
||||
{
|
||||
if (!_initialValues.ContainsKey(varName) || _initialValues[varName] == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Type type = _initialValues[varName].GetType();
|
||||
|
||||
if (type == typeof(Vector3))
|
||||
{
|
||||
return UxrVector3Interpolator.DefaultInterpolator;
|
||||
}
|
||||
if (type == typeof(Quaternion))
|
||||
{
|
||||
return UxrQuaternionInterpolator.DefaultInterpolator;
|
||||
}
|
||||
if (type == typeof(float))
|
||||
{
|
||||
return UxrFloatInterpolator.DefaultInterpolator;
|
||||
}
|
||||
if (type == typeof(int))
|
||||
{
|
||||
return UxrIntInterpolator.DefaultInterpolator;
|
||||
}
|
||||
if (type == typeof(Vector2))
|
||||
{
|
||||
return UxrVector2Interpolator.DefaultInterpolator;
|
||||
}
|
||||
if (type == typeof(Vector4))
|
||||
{
|
||||
return UxrVector4Interpolator.DefaultInterpolator;
|
||||
}
|
||||
if (type == typeof(Color))
|
||||
{
|
||||
return UxrColorInterpolator.DefaultInterpolator;
|
||||
}
|
||||
if (type == typeof(Color32))
|
||||
{
|
||||
return UxrColor32Interpolator.DefaultInterpolator;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a serialized var name is the name given to the position component of a given transform serialized
|
||||
/// using <see cref="SerializeStateTransform" />.
|
||||
/// </summary>
|
||||
/// <param name="varName">The variable name to check</param>
|
||||
/// <param name="transformVarName">The name given to the transform using <see cref="SerializeStateTransform" /></param>
|
||||
/// <returns>Whether <paramref name="varName" /> is the name assigned to the position component of the given transform</returns>
|
||||
public bool IsTransformPositionVarName(string varName, string transformVarName)
|
||||
{
|
||||
if (!_transformVarNameCache.TryGetValue(transformVarName, out Dictionary<string, string> innerDict))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return innerDict.TryGetValue(NameTransformPos, out string cachedValue) && cachedValue == varName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a serialized var name is the name given to the rotation component of a given transform serialized
|
||||
/// using <see cref="SerializeStateTransform" />.
|
||||
/// </summary>
|
||||
/// <param name="varName">The variable name to check</param>
|
||||
/// <param name="transformVarName">The name given to the transform using <see cref="SerializeStateTransform" /></param>
|
||||
/// <returns>Whether <paramref name="varName" /> is the name assigned to the rotation component of the given transform</returns>
|
||||
public bool IsTransformRotationVarName(string varName, string transformVarName)
|
||||
{
|
||||
if (!_transformVarNameCache.TryGetValue(transformVarName, out Dictionary<string, string> innerDict))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return innerDict.TryGetValue(NameTransformRot, out string cachedValue) && cachedValue == varName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a serialized var name is the name given to the scale component of a given transform serialized using
|
||||
/// <see cref="SerializeStateTransform" />.
|
||||
/// </summary>
|
||||
/// <param name="varName">The variable name to check</param>
|
||||
/// <param name="transformVarName">The name given to the transform using <see cref="SerializeStateTransform" /></param>
|
||||
/// <returns>Whether <paramref name="varName" /> is the name assigned to the scale component of the given transform</returns>
|
||||
public bool IsTransformScaleVarName(string varName, string transformVarName)
|
||||
{
|
||||
if (!_transformVarNameCache.TryGetValue(transformVarName, out Dictionary<string, string> innerDict))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return innerDict.TryGetValue(NameTransformScale, out string cachedValue) && cachedValue == varName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnStateSerializing(IUxrStateSave stateSave, UxrStateSaveEventArgs e)
|
||||
{
|
||||
base.OnStateSerializing(stateSave, e);
|
||||
Monitor.RaiseStateSerializing(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnStateSerialized(IUxrStateSave stateSave, UxrStateSaveEventArgs e)
|
||||
{
|
||||
base.OnStateSerialized(stateSave, e);
|
||||
Monitor.RaiseStateSerialized(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnVarSerializing(IUxrStateSave stateSave, UxrStateSaveEventArgs e)
|
||||
{
|
||||
base.OnVarSerializing(stateSave, e);
|
||||
Monitor.RaiseVarSerializing(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnVarSerialized(IUxrStateSave stateSave, UxrStateSaveEventArgs e)
|
||||
{
|
||||
base.OnVarSerialized(stateSave, e);
|
||||
Monitor.RaiseVarSerialized(e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrStateSaveImplementer
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void StoreInitialState()
|
||||
{
|
||||
if (_targetComponent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the initial state after the first frame. We use a dummy serializer in write mode to initialize the changes cache without saving any data.
|
||||
_targetComponent.SerializeState(UxrDummySerializer.WriteModeSerializer,
|
||||
_targetComponent.StateSerializationVersion,
|
||||
UxrStateSaveLevel.ChangesSinceBeginning,
|
||||
UxrStateSaveOptions.DontSerialize | UxrStateSaveOptions.ResetChangesCache | UxrStateSaveOptions.FirstFrame);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Fills the internal state event args object with the given parameters.
|
||||
/// </summary>
|
||||
/// <param name="serializer">The serializer</param>
|
||||
/// <param name="level">Serialization level</param>
|
||||
/// <param name="options">Serialization options</param>
|
||||
/// <param name="varName">Var name or null when not serializing any var</param>
|
||||
/// <param name="value">Var value or null when not serializing any var</param>
|
||||
/// <param name="oldValue">Old var value or null when not reading any var</param>
|
||||
/// <returns>The state event args</returns>
|
||||
private UxrStateSaveEventArgs GetStateSaveEventArgs(IUxrSerializer serializer, UxrStateSaveLevel level, UxrStateSaveOptions options, string varName = null, object value = null, object oldValue = null)
|
||||
{
|
||||
_stateSaveArgs.Set(serializer, level, options, varName, value, oldValue);
|
||||
return _stateSaveArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a transform's position in a given space.
|
||||
/// </summary>
|
||||
/// <param name="transform">Transform to get the position of</param>
|
||||
/// <param name="space">Coordinates to get the position in</param>
|
||||
/// <returns>Position in the given coordinates</returns>
|
||||
private Vector3 GetPosition(Transform transform, UxrTransformSpace space)
|
||||
{
|
||||
switch (space)
|
||||
{
|
||||
case UxrTransformSpace.World: return transform.position;
|
||||
case UxrTransformSpace.Local: return transform.localPosition;
|
||||
|
||||
case UxrTransformSpace.Avatar:
|
||||
|
||||
UxrAvatar avatar = GetAvatar();
|
||||
|
||||
if (avatar)
|
||||
{
|
||||
return avatar.transform.InverseTransformPoint(transform.position);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default: throw new ArgumentOutOfRangeException(nameof(space), space, "Transform space not implemented");
|
||||
}
|
||||
|
||||
return transform.position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a transform's rotation in a given space.
|
||||
/// </summary>
|
||||
/// <param name="transform">Transform to get the rotation of</param>
|
||||
/// <param name="space">Coordinates to get the rotation in</param>
|
||||
/// <returns>Rotation in the given coordinates</returns>
|
||||
private Quaternion GetRotation(Transform transform, UxrTransformSpace space)
|
||||
{
|
||||
switch (space)
|
||||
{
|
||||
case UxrTransformSpace.World: return transform.rotation;
|
||||
case UxrTransformSpace.Local: return transform.localRotation;
|
||||
|
||||
case UxrTransformSpace.Avatar:
|
||||
|
||||
UxrAvatar avatar = GetAvatar();
|
||||
|
||||
if (avatar)
|
||||
{
|
||||
return Quaternion.Inverse(avatar.transform.rotation) * transform.rotation;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default: throw new ArgumentOutOfRangeException(nameof(space), space, "Transform space not implemented");
|
||||
}
|
||||
|
||||
return transform.rotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a transform's position in a given space.
|
||||
/// </summary>
|
||||
/// <param name="transform">Transform to set the position of</param>
|
||||
/// <param name="space">Coordinates to set the position in</param>
|
||||
/// <param name="position">Position value</param>
|
||||
private void SetPosition(Transform transform, UxrTransformSpace space, Vector3 position)
|
||||
{
|
||||
switch (space)
|
||||
{
|
||||
case UxrTransformSpace.World:
|
||||
transform.position = position;
|
||||
break;
|
||||
|
||||
case UxrTransformSpace.Local:
|
||||
transform.localPosition = position;
|
||||
break;
|
||||
|
||||
case UxrTransformSpace.Avatar:
|
||||
|
||||
UxrAvatar avatar = GetAvatar();
|
||||
|
||||
if (avatar)
|
||||
{
|
||||
transform.position = avatar.transform.TransformPoint(position);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a transform's rotation in a given space.
|
||||
/// </summary>
|
||||
/// <param name="transform">Transform to set the rotation of</param>
|
||||
/// <param name="space">Coordinates to set the rotation in</param>
|
||||
/// <param name="rotation">Rotation value</param>
|
||||
private void SetRotation(Transform transform, UxrTransformSpace space, Quaternion rotation)
|
||||
{
|
||||
switch (space)
|
||||
{
|
||||
case UxrTransformSpace.World:
|
||||
transform.rotation = rotation;
|
||||
break;
|
||||
|
||||
case UxrTransformSpace.Local:
|
||||
transform.localRotation = rotation;
|
||||
break;
|
||||
|
||||
case UxrTransformSpace.Avatar:
|
||||
|
||||
UxrAvatar avatar = GetAvatar();
|
||||
|
||||
if (avatar)
|
||||
{
|
||||
transform.rotation = avatar.transform.rotation * rotation;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the avatar the target component belongs to.
|
||||
/// </summary>
|
||||
/// <returns>Avatar component or null if the component doesn't belong to an avatar</returns>
|
||||
private UxrAvatar GetAvatar()
|
||||
{
|
||||
if (_avatar != null)
|
||||
{
|
||||
return _avatar;
|
||||
}
|
||||
|
||||
_avatar = _targetComponent.GetComponentInParent<UxrAvatar>();
|
||||
return _avatar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name used to identify a serialized transform variable if it's cached, or caches it if it's not yet stored.
|
||||
/// This is used to avoid many string manipulation calls.
|
||||
/// </summary>
|
||||
/// <param name="transformVarName">The name assigned to the Transform</param>
|
||||
/// <param name="subName">The name to identify which part of the Transform is serialized (pos, rot, scale, parent or space)</param>
|
||||
/// <returns>The name</returns>
|
||||
private string GetTransformVarName(string transformVarName, string subName)
|
||||
{
|
||||
if (_transformVarNameCache.TryGetValue(transformVarName, out Dictionary<string, string> innerDict))
|
||||
{
|
||||
if (innerDict.TryGetValue(subName, out string cachedValue))
|
||||
{
|
||||
return cachedValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
innerDict = new Dictionary<string, string>();
|
||||
_transformVarNameCache[transformVarName] = innerDict;
|
||||
}
|
||||
|
||||
// If not in cache, compute the value and cache it
|
||||
string combinedValue = string.Concat(transformVarName, subName);
|
||||
innerDict[subName] = combinedValue;
|
||||
|
||||
return combinedValue;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private const string NameIsEnabled = "__enabled";
|
||||
private const string NameIsActive = "__active";
|
||||
private const string NameTransformParent = ".tf.parent";
|
||||
private const string NameTransformSpace = ".tf.space";
|
||||
private const string NameTransformPos = ".tf.pos";
|
||||
private const string NameTransformRot = ".tf.rot";
|
||||
private const string NameTransformScale = ".tf.scl";
|
||||
|
||||
private readonly T _targetComponent;
|
||||
|
||||
private readonly Dictionary<string, object> _initialValues = new Dictionary<string, object>();
|
||||
private readonly Dictionary<string, object> _lastValues = new Dictionary<string, object>();
|
||||
private readonly UxrStateSaveEventArgs _stateSaveArgs = new UxrStateSaveEventArgs();
|
||||
private readonly Dictionary<string, Dictionary<string, string>> _transformVarNameCache = new Dictionary<string, Dictionary<string, string>>();
|
||||
private bool _registered;
|
||||
|
||||
private UxrAvatar _avatar;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb166c78bcb9c0545b278804895381bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateSaveLevel.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Core.StateSave
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different levels of state saving.
|
||||
/// </summary>
|
||||
public enum UxrStateSaveLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't save anything.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Save a snapshot with only the changes since the previous save using <see cref="ChangesSincePreviousSave" />,
|
||||
/// <see cref="ChangesSinceBeginning" /> or <see cref="Complete" />.
|
||||
/// This can be used in replay systems to save incremental changes only.
|
||||
/// </summary>
|
||||
ChangesSincePreviousSave,
|
||||
|
||||
/// <summary>
|
||||
/// Save a snapshot with all changes since the end of the first frame when the object is first enabled. The end of the
|
||||
/// first frame is used because it represents the initial state of the scene when it's loaded, including logic that
|
||||
/// might be executed during Awake() or Start().
|
||||
/// This can be used to save space/bandwidth by avoiding sending redundant information.
|
||||
/// It can be utilized in a networking environment to send an up-to-date scene state to new users upon joining.
|
||||
/// Additionally, it can be employed in replay systems to save a keyframe.
|
||||
/// </summary>
|
||||
ChangesSinceBeginning,
|
||||
|
||||
/// <summary>
|
||||
/// Save complete data. It will serialize the state of all objects, regardless of whether any values changed since
|
||||
/// the beginning. This can be used to ensure that the state of all objects is restored correctly, even if changes are
|
||||
/// made in later versions where objects are moved to a different initial location or have a different initial state.
|
||||
/// </summary>
|
||||
Complete
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 148f09259d5173e40b422e7589aa42f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,101 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateSaveMonitor.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace UltimateXR.Core.StateSave
|
||||
{
|
||||
/// <summary>
|
||||
/// Monitor that helps intercepting relevant state serialization events.
|
||||
/// </summary>
|
||||
public class UxrStateSaveMonitor
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Event called right before the state of an object with the <see cref="IUxrStateSave" /> interface is about to be
|
||||
/// serialized using <see cref="IUxrStateSave.SerializeState" />.
|
||||
/// </summary>
|
||||
public static event EventHandler<UxrStateSaveEventArgs> StateSerializing;
|
||||
|
||||
/// <summary>
|
||||
/// Event called right after the state of an object with the <see cref="IUxrStateSave" /> interface was serialized
|
||||
/// using <see cref="IUxrStateSave.SerializeState" />.
|
||||
/// </summary>
|
||||
public static event EventHandler<UxrStateSaveEventArgs> StateSerialized;
|
||||
|
||||
/// <summary>
|
||||
/// Event called right before a state variable is about to be serialized in a
|
||||
/// <see cref="IUxrStateSave.SerializeState" /> call.
|
||||
/// </summary>
|
||||
public static event EventHandler<UxrStateSaveEventArgs> VarSerializing;
|
||||
|
||||
/// <summary>
|
||||
/// Event called right after a state variable was serialized in a <see cref="IUxrStateSave.SerializeState" /> call.
|
||||
/// </summary>
|
||||
public static event EventHandler<UxrStateSaveEventArgs> VarSerialized;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="target">The <see cref="IUxrStateSave" /> that is being monitored</param>
|
||||
public UxrStateSaveMonitor(IUxrStateSave target)
|
||||
{
|
||||
_target = target;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="StateSerializing" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
internal void RaiseStateSerializing(UxrStateSaveEventArgs e)
|
||||
{
|
||||
StateSerializing?.Invoke(_target, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="StateSerialized" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
internal void RaiseStateSerialized(UxrStateSaveEventArgs e)
|
||||
{
|
||||
StateSerialized?.Invoke(_target, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="VarSerializing" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
internal void RaiseVarSerializing(UxrStateSaveEventArgs e)
|
||||
{
|
||||
VarSerializing?.Invoke(_target, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="VarSerialized" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
internal void RaiseVarSerialized(UxrStateSaveEventArgs e)
|
||||
{
|
||||
VarSerialized?.Invoke(_target, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private readonly IUxrStateSave _target;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b92723a89483edd4f970c1ec9f1123d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateSaveOptions.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace UltimateXR.Core.StateSave
|
||||
{
|
||||
/// <summary>
|
||||
/// Flags for state saving options.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum UxrStateSaveOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// No options.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Simulate the process, but do not read or write any data. The serialization counter
|
||||
/// <see cref="UxrStateSaveImplementer{T}.SerializeCounter" /> will still be updated to know how many vars would be
|
||||
/// serialized. The changes cache will be updated unless <see cref="DontCacheChanges" /> is also used.
|
||||
/// </summary>
|
||||
DontSerialize = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Do not update the changes cache. The changes cache stores the last values that were serialized to make
|
||||
/// sure to serialize changes only in incremental serializations (see <see cref="UxrStateSaveLevel" />).
|
||||
/// </summary>
|
||||
DontCacheChanges = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Do not check the changes cache when writing, which means that the values will be written whether they changed
|
||||
/// or not.
|
||||
/// </summary>
|
||||
DontCheckCache = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Resets the changes cache, which will set the serialized values as the initial (
|
||||
/// <see cref="UxrStateSaveLevel.ChangesSinceBeginning" />) and latest (
|
||||
/// <see cref="UxrStateSaveLevel.ChangesSincePreviousSave" />) states.
|
||||
/// </summary>
|
||||
ResetChangesCache = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies that it is gathering the first initial state.
|
||||
/// </summary>
|
||||
FirstFrame = 1 << 10,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d83ab819e544593b22ca55e601d88fe
|
||||
timeCreated: 1705684047
|
||||
8
Assets/UltimateXR/Runtime/Scripts/Core/StateSync.meta
Normal file
8
Assets/UltimateXR/Runtime/Scripts/Core/StateSync.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e488170cacf297842bbd2c6e691a50d0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="IUxrStateSync.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core.Unique;
|
||||
|
||||
namespace UltimateXR.Core.StateSync
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Interface for components to synchronize their state changes. State changes can be intercepted,
|
||||
/// serialized, deserialized and be reproduced back in a different environment. This can be used to
|
||||
/// synchronize state changes in a network session or save state changes to disk.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Relevant internal state changes are notified through a <see cref="StateChanged" /> event. The state
|
||||
/// change is described by a <see cref="UxrSyncEventArgs" /> object. Each <see cref="UxrSyncEventArgs" />
|
||||
/// can be reproduced back using the <see cref="SyncState" /> method. This architecture can be used to
|
||||
/// listen for changes and reproduce them on the other clients, since <see cref="UxrSyncEventArgs" />
|
||||
/// objects can be serialized.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// To leverage the implementation of this interface, consider using <see cref="UxrStateSyncImplementer{T}" />.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public interface IUxrStateSync : IUxrUniqueId
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a relevant state of a component changed and requires synchronization.
|
||||
/// </summary>
|
||||
event EventHandler<UxrSyncEventArgs> StateChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Executes the state change described by <see cref="e" />.
|
||||
/// </summary>
|
||||
/// <param name="e">State change information</param>
|
||||
void SyncState(UxrSyncEventArgs e);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f1e439607f93f8429810b27bd58f32c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,109 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrMethodInvokedSyncEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UltimateXR.Core.Serialization;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.StateSync
|
||||
{
|
||||
/// <summary>
|
||||
/// Event args for the state sync of a method that was called. It supports sending the same parameters.
|
||||
/// </summary>
|
||||
public class UxrMethodInvokedSyncEventArgs : UxrSyncEventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method name.
|
||||
/// </summary>
|
||||
public string MethodName
|
||||
{
|
||||
get => _methodName;
|
||||
private set => _methodName = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the call parameters.
|
||||
/// </summary>
|
||||
public object[] Parameters
|
||||
{
|
||||
get => _parameters;
|
||||
private set => _parameters = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="methodName">The name of the method that was called</param>
|
||||
/// <param name="parameters">The parameters used in the call</param>
|
||||
public UxrMethodInvokedSyncEventArgs(string methodName, params object[] parameters)
|
||||
{
|
||||
MethodName = methodName;
|
||||
Parameters = parameters ?? new object[] { };
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
if (MethodName == null && Parameters == null)
|
||||
{
|
||||
return "Unknown method call";
|
||||
}
|
||||
|
||||
if (Parameters == null || Parameters.Length == 0)
|
||||
{
|
||||
return $"Method call {MethodName}()";
|
||||
}
|
||||
|
||||
return $"Method call {MethodName ?? "unknown"}({string.Join(", ", Parameters.Select(p => p == null ? "null" : p.ToString()))})";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrSyncEventArgs
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeEventInternal(IUxrSerializer serializer)
|
||||
{
|
||||
bool isMethodKnown = false;
|
||||
|
||||
try
|
||||
{
|
||||
serializer.Serialize(ref _methodName);
|
||||
isMethodKnown = true;
|
||||
serializer.Serialize(ref _parameters);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors && serializer.IsReading && isMethodKnown)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} Error deserializing invoked method {_methodName}(). Exception below.");
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private string _methodName;
|
||||
private object[] _parameters;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb960f54581c96d4991f67ac281dd26f
|
||||
timeCreated: 1650792910
|
||||
@@ -0,0 +1,81 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrPropertyChangedSyncEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Serialization;
|
||||
|
||||
namespace UltimateXR.Core.StateSync
|
||||
{
|
||||
/// <summary>
|
||||
/// Event args for the state sync of a property whose value was changed.
|
||||
/// </summary>
|
||||
public class UxrPropertyChangedSyncEventArgs : UxrSyncEventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property name.
|
||||
/// </summary>
|
||||
public string PropertyName
|
||||
{
|
||||
get => _propertyName;
|
||||
private set => _propertyName = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new property value.
|
||||
/// </summary>
|
||||
public object Value
|
||||
{
|
||||
get => _value;
|
||||
private set => _value = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property</param>
|
||||
/// <param name="value">The new value of the property</param>
|
||||
public UxrPropertyChangedSyncEventArgs(string propertyName, object value)
|
||||
{
|
||||
PropertyName = propertyName;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
string newValue = Value == null ? "null" : Value.ToString();
|
||||
return $"Property change {PropertyName ?? "Unknown"} = {newValue ?? "null/unknown"}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrSyncEventArgs
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeEventInternal(IUxrSerializer serializer)
|
||||
{
|
||||
serializer.Serialize(ref _propertyName);
|
||||
serializer.SerializeAnyVar(ref _value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private string _propertyName;
|
||||
private object _value;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 126849ebc072f2243851202a432b423e
|
||||
timeCreated: 1650792910
|
||||
@@ -0,0 +1,59 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateSyncImplementer.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Core.StateSync
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for <see cref="UxrStateSyncImplementer{T}" />.
|
||||
/// </summary>
|
||||
public class UxrStateSyncImplementer
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Gets the current call depth of BeginSync/EndSync calls, which are responsible for helping synchronize calls
|
||||
/// over the network.
|
||||
/// To avoid redundant synchronization, nested calls (where <see cref="SyncCallDepth" /> is greater than 1),
|
||||
/// need to be ignored.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// State synchronization, for networking or other functionality like saving gameplay replays, can be done
|
||||
/// by subscribing to <see cref="UxrManager.ComponentStateChanged" />. By default, only top level calls will
|
||||
/// trigger the event. This can be changed using <see cref="UxrManager.UseTopLevelStateChangesOnly" />.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// In the following code, only PlayerShoot() needs to be synchronized. This will not only save bandwidth, but also
|
||||
/// make sure that only a single particle system gets instantiated and the shot audio doesn't get played twice.
|
||||
/// </para>
|
||||
/// <code>
|
||||
/// void PlayerShoot(int parameter1, bool parameter2)
|
||||
/// {
|
||||
/// BeginSync();
|
||||
/// ShowParticles(parameter1);
|
||||
/// PlayAudioShot(parameter2);
|
||||
/// EndSyncMethod(new object[] {parameter1, parameter2});
|
||||
/// }
|
||||
///
|
||||
/// void ShowParticles(int parameter);
|
||||
/// {
|
||||
/// BeginSync();
|
||||
/// Instantiate(ParticleSystem);
|
||||
/// EndSyncMethod(new object[] {parameter});
|
||||
/// }
|
||||
///
|
||||
/// void PlayAudioShot(bool parameter);
|
||||
/// {
|
||||
/// BeginSync();
|
||||
/// _audio.Play();
|
||||
/// EndSyncMethod(new object[] {parameter});
|
||||
/// }
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public static int SyncCallDepth { get; protected set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c8beee399964389a19123b9deef1a36
|
||||
timeCreated: 1705596679
|
||||
@@ -0,0 +1,308 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateSyncImplementer_1.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Core.StateSync
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class simplifying the implementation of the <see cref="IUxrStateSync" /> interface.
|
||||
/// This class includes functionality for automatic synchronization of property changes and method calls
|
||||
/// through reflection, using a convenient BeginSync/EndSync pattern.
|
||||
/// It is utilized by <see cref="UxrComponent" /> to implement <see cref="IUxrStateSync" />.
|
||||
/// In scenarios where custom classes cannot inherit from <see cref="UxrComponent" /> for automatic sync capabilities,
|
||||
/// this class is designed to implement the interface.
|
||||
/// </summary>
|
||||
public class UxrStateSyncImplementer<T> : UxrStateSyncImplementer where T : Component, IUxrStateSync
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="targetComponent">Target component for all the methods called on this object</param>
|
||||
public UxrStateSyncImplementer(T targetComponent)
|
||||
{
|
||||
_targetComponent = targetComponent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor is private to use public constructor with target component.
|
||||
/// </summary>
|
||||
private UxrStateSyncImplementer()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Executes the state change described by <see cref="e" /> on the component.<br />
|
||||
/// First it will check if they are built-in state sync events such as property change (
|
||||
/// <see cref="UxrPropertyChangedSyncEventArgs" />, using <see cref="BeginSync" />/<see cref="EndSyncProperty" />) and
|
||||
/// method call (<see cref="UxrMethodInvokedSyncEventArgs" />, using <see cref="BeginSync" />/
|
||||
/// <see cref="EndSyncMethod" />).
|
||||
/// If it's a different, custom event, it will be handled using the provided
|
||||
/// <paramref name="fallbackSyncStateHandler" />.
|
||||
/// </summary>
|
||||
/// <param name="e">State change</param>
|
||||
/// <param name="fallbackSyncStateHandler">Fallback event handler</param>
|
||||
public void SyncState(UxrSyncEventArgs e, Action<UxrSyncEventArgs> fallbackSyncStateHandler)
|
||||
{
|
||||
// First check if it's a synchronization that can be solved at the base level
|
||||
|
||||
if (e is UxrPropertyChangedSyncEventArgs propertyChangedEventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Set new property value using reflection
|
||||
_targetComponent.GetType().GetProperty(propertyChangedEventArgs.PropertyName, PropertyFlags).SetValue(_targetComponent, propertyChangedEventArgs.Value);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} Error trying to sync property {propertyChangedEventArgs.PropertyName} to value {propertyChangedEventArgs.Value} . Component: {_targetComponent.GetPathUnderScene()}. Exception: {exception}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (e is UxrMethodInvokedSyncEventArgs methodInvokedEventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (methodInvokedEventArgs.Parameters == null || !methodInvokedEventArgs.Parameters.Any())
|
||||
{
|
||||
// Invoke without arguments
|
||||
_targetComponent.GetType().GetMethod(methodInvokedEventArgs.MethodName, MethodFlags).Invoke(_targetComponent, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invoke method using same parameters using reflection. Make sure we select the correct overload.
|
||||
|
||||
bool anyIsNull = methodInvokedEventArgs.Parameters.Any(p => p == null);
|
||||
|
||||
if (_targetComponent.GetType().GetMethods(MethodFlags).Count(m => m.Name.Equals(methodInvokedEventArgs.MethodName)) == 1)
|
||||
{
|
||||
// There are no overloads
|
||||
_targetComponent.GetType().GetMethod(methodInvokedEventArgs.MethodName, MethodFlags).Invoke(_targetComponent, methodInvokedEventArgs.Parameters);
|
||||
}
|
||||
else if (!anyIsNull)
|
||||
{
|
||||
// We can look for a method specifying the parameter types.
|
||||
_targetComponent.GetType().GetMethod(methodInvokedEventArgs.MethodName, MethodFlags, null, methodInvokedEventArgs.Parameters.Select(p => p.GetType()).ToArray(), null).Invoke(_targetComponent, methodInvokedEventArgs.Parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have a call where a parameter is null, so we can't infer the parameter type. Try to find a method with the same parameter count.
|
||||
|
||||
MethodInfo method = _targetComponent.GetType().GetMethods(MethodFlags).FirstOrDefault(x => x.Name.Equals(methodInvokedEventArgs.MethodName) && x.GetParameters().Length == methodInvokedEventArgs.Parameters.Length);
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(_targetComponent, methodInvokedEventArgs.Parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Could not find a method with the given name and parameter count");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (AmbiguousMatchException ambiguousMatchException)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} Trying to sync a method that has ambiguous call. {e}. Component: {_targetComponent.GetPathUnderScene()}. Exception: {ambiguousMatchException}");
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} Error trying to sync method. It could be that an exception inside the method was thrown, that {nameof(EndSyncMethod)} was used with the wrong parameters or it has an overload that could not be resolved. {e}. Component: {_targetComponent.GetPathUnderScene()}. Exception: {exception}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's a synchronization using a custom UxrSyncEventArgs object. Pass it to the fallback handler.
|
||||
|
||||
try
|
||||
{
|
||||
fallbackSyncStateHandler?.Invoke(e);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} Error trying to sync state. {e}. Component: {_targetComponent.GetPathUnderScene()}. Exception: {exception}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Starts a synchronization block that will end with an EndSync method like <see cref="EndSyncProperty" />,
|
||||
/// <see cref="EndSyncMethod" /> or <see cref="EndSyncState" />, which causes the
|
||||
/// <see cref="IUxrStateSync.StateChanged" /> event to be triggered.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// See <see cref="UxrComponent.BeginSync" />.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="options">Options. It's saved/used in all environments by default.</param>
|
||||
public void BeginSync(UxrStateSyncOptions options = UxrStateSyncOptions.Default)
|
||||
{
|
||||
SyncCallDepth++;
|
||||
_optionStack.Push(options);
|
||||
|
||||
if (SyncCallDepth > StateSyncCallDepthErrorThreshold)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} BeginSync/EndSync mismatch when calling BeginSync. Did you forget an EndSync call? Component: {_targetComponent.GetPathUnderScene()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Cancels a <see cref="BeginSync" /> to escape when a condition is found that makes it not require to sync.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// See <see cref="UxrComponent.CancelSync" />.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public void CancelSync()
|
||||
{
|
||||
if (SyncCallDepth > 0)
|
||||
{
|
||||
SyncCallDepth--;
|
||||
_optionStack.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} BeginSync/CancelSync mismatch when calling CancelSync. Did you forget a BeginSync call? State call depth is < 1. Component: {_targetComponent.GetPathUnderScene()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Ends synchronization for a property change. It notifies that a property was changed in a component that
|
||||
/// requires network/state synchronization, ensuring that the change is performed in all other clients too.
|
||||
/// The synchronization should begin using <see cref="BeginSync" />.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// See <see cref="UxrComponent.EndSyncProperty" />.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="raiseChangedEvent">Delegate that will call the <see cref="IUxrStateSync.StateChanged" /> event</param>
|
||||
/// <param name="value">New property value</param>
|
||||
/// <param name="propertyName">Property name</param>
|
||||
public void EndSyncProperty(Action<UxrSyncEventArgs> raiseChangedEvent, in object value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
EndSyncState(raiseChangedEvent, new UxrPropertyChangedSyncEventArgs(propertyName, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Ends synchronization for a method call. It notifies that a method was invoked in a component that requires
|
||||
/// network/state synchronization, ensuring that the call is performed in all other clients too.
|
||||
/// The synchronization should begin using <see cref="BeginSync" />.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// See <see cref="UxrComponent.EndSyncMethod" />.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public void EndSyncMethod(Action<UxrSyncEventArgs> raiseChangedEvent, object[] parameters = null, [CallerMemberName] string methodName = null)
|
||||
{
|
||||
EndSyncState(raiseChangedEvent, new UxrMethodInvokedSyncEventArgs(methodName, parameters));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Ends a synchronization block for a custom event. The synchronization block should begin using
|
||||
/// <see cref="BeginSync" />. The event ensures that the code is executed in all other receivers too.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// See <see cref="UxrComponent.EndSyncState" />.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="raiseChangedEvent">
|
||||
/// The delegate to call to raise the <see cref="IUxrStateSync.StateChanged" /> event. The
|
||||
/// delegate will receive a <see cref="UxrSyncEventArgs" /> as parameter describing the state change.
|
||||
/// </param>
|
||||
/// <param name="e">The state change</param>
|
||||
public void EndSyncState(Action<UxrSyncEventArgs> raiseChangedEvent, UxrSyncEventArgs e)
|
||||
{
|
||||
if (SyncCallDepth > 0)
|
||||
{
|
||||
e.Options = _optionStack.Pop();
|
||||
raiseChangedEvent?.Invoke(e);
|
||||
SyncCallDepth--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} BeginSync/EndSync mismatch when calling EndSync. Did you forget a BeginSync call? State call depth is < 1. Component: {_targetComponent.GetPathUnderScene()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the component if it hasn't been registered already.
|
||||
/// </summary>
|
||||
public void RegisterIfNecessary()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_registered)
|
||||
{
|
||||
UxrManager.Instance.RegisterStateSyncComponent<T>(_targetComponent);
|
||||
_registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the component.
|
||||
/// </summary>
|
||||
public void Unregister()
|
||||
{
|
||||
UxrManager.Instance.UnregisterStateSyncComponent<T>(_targetComponent);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private const int StateSyncCallDepthErrorThreshold = 100;
|
||||
private const BindingFlags EventFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
private const BindingFlags MethodFlags = EventFlags | BindingFlags.InvokeMethod | BindingFlags.FlattenHierarchy;
|
||||
private const BindingFlags PropertyFlags = EventFlags | BindingFlags.SetProperty;
|
||||
|
||||
private readonly T _targetComponent;
|
||||
private readonly Stack<UxrStateSyncOptions> _optionStack = new Stack<UxrStateSyncOptions>();
|
||||
private bool _registered;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 759872880e47518449e2348177609a7b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateSyncOptions.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace UltimateXR.Core.StateSync
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different options when using a <see cref="UxrSyncEventArgs" /> with BeginSync().
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum UxrStateSyncOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// No save/use.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the event should be synchronized over the network.
|
||||
/// This feature helps save bandwidth on events that do not need network synchronization.
|
||||
/// For instance, a Transform update might not be needed in a networking scenario because it has already been
|
||||
/// synchronized using a NetworkTransform component more efficiently. For replays, where the networking component is
|
||||
/// not working, the transform update will still be needed.
|
||||
/// </summary>
|
||||
Network = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the event should be saved in replays.
|
||||
/// For example, UxrGrabbableObject uses a coroutine that syncs grabbable objects with rigidbodies so that the
|
||||
/// position/rotation and speed keep in sync in all devices every x seconds. This is used only in objects
|
||||
/// that do not have specific networking components such as NetworkRigidbody or NetworkTransform.
|
||||
/// In replays, the position and rotation are already sampled so there is no need to save these events.
|
||||
/// </summary>
|
||||
Replay = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Forces to output a new sampling frame before and after the sync event when recording a replay. This can be used
|
||||
/// to avoid interpolation errors when a certain event affects how values are interpolated.
|
||||
/// For example re-parenting an object between two frames will create a jump if the position is recorded
|
||||
/// in local space. Forcing to output a new frame will avoid this.
|
||||
/// </summary>
|
||||
GenerateNewFrame = 1 << 8,
|
||||
|
||||
/// <summary>
|
||||
/// Ignores nesting checks, which will generate <see cref="IUxrStateSync.StateChanged"/> events even when
|
||||
/// the BeginSync/EndSync block is nested.
|
||||
/// </summary>
|
||||
IgnoreNestingCheck = 1 << 9,
|
||||
|
||||
/// <summary>
|
||||
/// Save/use in all environments.
|
||||
/// </summary>
|
||||
Default = Network | Replay
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 786fe7232748b434dad29e2c924229c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStateSyncResult.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Core.StateSync
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the result of executing a state change synchronization.
|
||||
/// </summary>
|
||||
public class UxrStateSyncResult
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether there was an error executing the state change.
|
||||
/// </summary>
|
||||
public bool IsError => ErrorMessage != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target of the state change.
|
||||
/// </summary>
|
||||
public IUxrStateSync Target { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state change data.
|
||||
/// </summary>
|
||||
public UxrSyncEventArgs EventArgs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message if there was an error executing the state change. Otherwise it returns null.
|
||||
/// </summary>
|
||||
public string ErrorMessage { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="target">Target of the state change</param>
|
||||
/// <param name="eventArgs">Event data containing the state change</param>
|
||||
/// <param name="errorMessage">Error message if there was an error executing the state change, otherwise null</param>
|
||||
public UxrStateSyncResult(IUxrStateSync target, UxrSyncEventArgs eventArgs, string errorMessage)
|
||||
{
|
||||
Target = target;
|
||||
EventArgs = eventArgs;
|
||||
ErrorMessage = errorMessage;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
string error = !string.IsNullOrEmpty(ErrorMessage) ? $" Error is: {ErrorMessage}" : string.Empty;
|
||||
|
||||
if (IsValid)
|
||||
{
|
||||
string result = !string.IsNullOrEmpty(ErrorMessage) ? "Could not execute state change" : "Successful state change";
|
||||
return $"{result} event for {Target.Component.name}, {EventArgs}.{error}";
|
||||
}
|
||||
|
||||
if (Target != null)
|
||||
{
|
||||
return $"Unknown event for {Target.Component.name}.{error}";
|
||||
}
|
||||
|
||||
if (EventArgs != null)
|
||||
{
|
||||
return $"Event for unresolved target: {EventArgs}.{error}";
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the result contains valid data.
|
||||
/// </summary>
|
||||
private bool IsValid => Target != null && EventArgs != null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5413f228685ae7f429bfa593f430c076
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user