VR4RoboticArm2/VR4RoboticArm/Library/PackageCache/com.meta.xr.sdk.interaction/Runtime/Scripts/Input/DataSource.cs
IonutMocanu d7aba243a2 Main
2025-09-08 11:04:02 +03:00

293 lines
12 KiB
C#

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using UnityEngine;
namespace Oculus.Interaction.Input
{
/// <summary>
/// Interface expressing that an instance is an integration point from which data can be retrieved. Any type
/// that is a provider of a certain type of data (forwarding <see cref="HandDataAsset"/> data from the system as done by
/// <see cref="Hand"/>, altering and re-forwarding <see cref="HandDataAsset"/> data based on scene state as done by
/// <see cref="SyntheticHand"/>, etc.) should be a DataSource for that kind of data.
/// </summary>
/// <remarks>
/// This is a base interface for many different types supplying many different types of data throughout the Interaction SDK.
/// For example, <see cref="Hand"/>s are sources for <see cref="HandDataAsset"/>s, while <see cref="Controller"/>s are sources
/// for <see cref="ControllerDataAsset"/>.
/// </remarks>
public interface IDataSource
{
/// <summary>
/// Indicator which is incremented every time new data becomes available.
/// </summary>
/// <remarks>
/// Exceptionally high-frequency data in exceptionally long-running experiences can potentially overflow this value,
/// leading to undefined behavior. While this is not expected to be a factor in typical cases, wildly high-frequency
/// scenarios may need to destroy and recreate their IDataSources periodically to avoid hitting this limit.
/// </remarks>
int CurrentDataVersion { get; }
/// <summary>
/// Marks the data asset (for example, <see cref="HandDataAsset"/>) stored as outdated, which means it should be
/// re-processed the next time data is retrieved from this source.
/// </summary>
void MarkInputDataRequiresUpdate();
/// <summary>
/// Notifies observers that new data is available from this source. Data is typically retrieved by calling
/// <see cref="IDataSource{TData}.GetData"/>.
/// </summary>
/// <remarks>
/// If observing a <see cref="Hand"/> instance, use <see cref="Hand.WhenHandUpdated"/> instead of this event.
/// </remarks>
event Action InputDataAvailable;
}
/// <summary>
/// Interface expressing that an instance is an integration point from which data can be retrieved. This is a conceptual
/// increment on <see cref="IDataSource"/>, adding only a specification of the type of data provided and an accessor to
/// retrieve that data.
/// </summary>
public interface IDataSource<TData> : IDataSource
{
/// <summary>
/// Retrieves the <typeparamref name="TData"/> currently contained by this source. If
/// <see cref="IDataSource.MarkInputDataRequiresUpdate"/> has been called since the last invocation of this method,
/// all required work to lazily update the contained data will be executed within this call.
/// </summary>
/// <returns>The <typeparamref name="TData"/> currently contained by this source</returns>
TData GetData();
}
/// <summary>
/// Base class for most concrete <see cref="IDataSource"/> types. Conceptually, any type which produces data can be an
/// <see cref="IDataSource"/> for that data type, but in practice usage is more constrained (though still pervasive throughout
/// the Interaction SDK). Descendent types of this DataSource implementation specifically are also MonoBehaviours; thus, the
/// expectation for descendent types is that they will be long-lived instances which persistently provide data over a period of
/// time, on a schedule specified by <see cref="UpdateModeFlags"/> but typically tied in some way into MonoBehaviour's built-in
/// update capabilites.
/// </summary>
/// <typeparam name="TData"></typeparam>
public abstract class DataSource<TData> : MonoBehaviour, IDataSource<TData>
where TData : class, ICopyFrom<TData>, new()
{
/// <summary>
/// Indicates whether or not the MonoBehaviour start-up process has completed for this instance.
/// </summary>
public bool Started => _started;
protected bool _started = false;
private bool _requiresUpdate = true;
/// <summary>
/// Enumeration controlling when this <see cref="IDataSource"/> updates its data. This enum is intended to be used as a bit
/// mask, meaning multiple different update modes can be enabled at the same time.
/// </summary>
[Flags]
public enum UpdateModeFlags
{
Manual = 0,
UnityUpdate = 1 << 0,
UnityFixedUpdate = 1 << 1,
UnityLateUpdate = 1 << 2,
AfterPreviousStep = 1 << 3
}
[Header("Update")]
[SerializeField]
private UpdateModeFlags _updateMode;
/// <summary>
/// Returns the <see cref="UpdateModeFlags"/> specifying the circumstances under which the data contained in this instance
/// will be updated.
/// </summary>
public UpdateModeFlags UpdateMode => _updateMode;
[SerializeField, Interface(typeof(IDataSource))]
[Optional(OptionalAttribute.Flag.DontHide)]
private UnityEngine.Object _updateAfter;
private IDataSource UpdateAfter;
private int _currentDataVersion;
protected bool UpdateModeAfterPrevious => (_updateMode & UpdateModeFlags.AfterPreviousStep) != 0;
/// <summary>
/// Implementation of <see cref="IDataSource.InputDataAvailable"/>; for details, please refer to the related
/// documentation provided for that interface.
/// </summary>
public event Action InputDataAvailable = delegate { };
/// <summary>
/// Implementation of <see cref="IDataSource.CurrentDataVersion"/>; for details, please refer to the related
/// documentation provided for that interface.
/// </summary>
public virtual int CurrentDataVersion => _currentDataVersion;
#region Unity Lifecycle
protected virtual void Start()
{
this.BeginStart(ref _started);
if (_updateAfter != null)
{
UpdateAfter = _updateAfter as IDataSource;
this.AssertField(UpdateAfter, nameof(UpdateAfter));
}
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
if (UpdateModeAfterPrevious && UpdateAfter != null)
{
UpdateAfter.InputDataAvailable += MarkInputDataRequiresUpdate;
}
}
}
protected virtual void OnDisable()
{
if (_started)
{
if (UpdateAfter != null)
{
UpdateAfter.InputDataAvailable -= MarkInputDataRequiresUpdate;
}
}
}
protected virtual void Update()
{
if ((_updateMode & UpdateModeFlags.UnityUpdate) != 0)
{
MarkInputDataRequiresUpdate();
}
}
protected virtual void FixedUpdate()
{
if ((_updateMode & UpdateModeFlags.UnityFixedUpdate) != 0)
{
MarkInputDataRequiresUpdate();
}
}
protected virtual void LateUpdate()
{
if ((_updateMode & UpdateModeFlags.UnityLateUpdate) != 0)
{
MarkInputDataRequiresUpdate();
}
}
#endregion
protected void ResetUpdateAfter(IDataSource updateAfter, UpdateModeFlags updateMode)
{
bool wasActive = isActiveAndEnabled;
if (isActiveAndEnabled) { OnDisable(); }
_updateMode = updateMode;
UpdateAfter = updateAfter;
_requiresUpdate = true;
_currentDataVersion += 1;
if (wasActive) { OnEnable(); }
}
/// <summary>
/// Implementation of <see cref="IDataSource{TData}.GetData"/>; for details, please refer to the related
/// documentation provided for that interface.
/// </summary>
public TData GetData()
{
if (RequiresUpdate())
{
UpdateData();
_requiresUpdate = false;
}
return DataAsset;
}
protected bool RequiresUpdate()
{
return _requiresUpdate;
}
/// <summary>
/// Implementation of <see cref="IDataSource.MarkInputDataRequiresUpdate"/>; for details, please refer to the related
/// documentation provided for that interface.
/// </summary>
public virtual void MarkInputDataRequiresUpdate()
{
_requiresUpdate = true;
_currentDataVersion += 1;
InputDataAvailable();
}
protected abstract void UpdateData();
/// <summary>
/// Returns the current DataAsset, without performing any updates.
/// </summary>
/// <returns>
/// Null if no call to GetData has been made since this data source was initialized.
/// </returns>
protected abstract TData DataAsset { get; }
#region Inject
/// <summary>
/// Injects all required dependencies for a dynamically instantiated DataSource; effectively wraps
/// <see cref="InjectUpdateMode(UpdateModeFlags)"/> and <see cref="InjectUpdateAfter(IDataSource)"/>.
/// This method exists to support Interaction SDK's dependency injection pattern and is not needed for typical Unity
/// Editor-based usage.
/// </summary>
public void InjectAllDataSource(UpdateModeFlags updateMode, IDataSource updateAfter)
{
InjectUpdateMode(updateMode);
InjectUpdateAfter(updateAfter);
}
/// <summary>
/// Sets the <see cref="UpdateModeFlags"/> for a dynamically instantiated DataSource. This method exists to support
/// Interaction SDK's dependency injection pattern and is not needed for typical Unity Editor-based usage.
/// </summary>
/// <param name="updateMode"></param>
public void InjectUpdateMode(UpdateModeFlags updateMode)
{
_updateMode = updateMode;
}
/// <summary>
/// Sets the <see cref="IDataSource"/> to update after for a dynamically instantiated DataSource. This method exists to
/// support Interaction SDK's dependency injection pattern and is not needed for typical Unity Editor-based usage.
/// </summary>
public void InjectUpdateAfter(IDataSource updateAfter)
{
_updateAfter = updateAfter as UnityEngine.Object;
UpdateAfter = updateAfter;
}
#endregion
}
}