Initialer Upload neues Unity-Projekt

This commit is contained in:
Daniel Ocks
2025-07-21 09:11:14 +02:00
commit eeca72985b
14558 changed files with 1508140 additions and 0 deletions

View File

@ -0,0 +1,126 @@
/*
* 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 Oculus.Interaction.Input;
using System;
using UnityEngine;
namespace Oculus.Interaction
{
/// <summary>
/// An Axis1D that switches between two Axis1D based on an ActiveState
/// </summary>
public class Axis1DPrioritySelector : MonoBehaviour, IAxis1D
{
[Serializable]
public class AxisData
{
[SerializeField, Interface(typeof(IActiveState))]
private UnityEngine.Object _activeState;
public IActiveState ActiveState;
[SerializeField, Interface(typeof(IAxis1D))]
private UnityEngine.Object _axis;
public IAxis1D Axis;
public void Initialize()
{
ActiveState = _activeState as IActiveState;
Axis = _axis as IAxis1D;
}
public void Validate (Component context)
{
context.AssertField(ActiveState, nameof(ActiveState));
context.AssertField(Axis, nameof(Axis));
}
}
[SerializeField] private AxisData[] _axisData;
[SerializeField, Interface(typeof(IAxis1D))]
private UnityEngine.Object _fallbackIfNoMatchAxis;
private IAxis1D FallbackIfNoMatchAxis;
private AxisData ActiveAxis;
protected IAxis1D Current => GetActiveAxis();
protected virtual void Awake()
{
foreach (var axisDatum in _axisData)
{
axisDatum.Initialize();
}
FallbackIfNoMatchAxis = _fallbackIfNoMatchAxis as IAxis1D;
}
protected virtual void Start()
{
foreach (var axisDatum in _axisData)
{
axisDatum.Validate(this);
}
this.AssertField(FallbackIfNoMatchAxis, nameof(FallbackIfNoMatchAxis));
}
public float Value()
{
return Current.Value();
}
private IAxis1D GetActiveAxis()
{
if ((ActiveAxis != null) && ActiveAxis.ActiveState.Active)
{
return ActiveAxis.Axis;
}
foreach (var axisDatum in _axisData)
{
if (axisDatum.ActiveState.Active)
{
ActiveAxis = axisDatum;
return ActiveAxis.Axis;
}
}
return FallbackIfNoMatchAxis;
}
#region Inject
public void InjectAll(AxisData[] axisData, IAxis1D fallbackIfNoMatchAxis)
{
_axisData = axisData;
foreach (var axisDatum in axisData)
{
axisDatum.Validate(this);
}
FallbackIfNoMatchAxis = fallbackIfNoMatchAxis;
_fallbackIfNoMatchAxis = fallbackIfNoMatchAxis as UnityEngine.Object;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 462d07f0e530df04491907e3d9383e25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,94 @@
/*
* 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 Oculus.Interaction.Input;
using UnityEngine;
namespace Oculus.Interaction
{
/// <summary>
/// An Axis1D that switches between two Axis1D based on an ActiveState
/// </summary>
public class Axis1DSwitch : MonoBehaviour, IAxis1D
{
[SerializeField, Interface(typeof(IActiveState))]
private UnityEngine.Object _activeState;
private IActiveState ActiveState;
[SerializeField, Interface(typeof(IAxis1D))]
private UnityEngine.Object _axisWhenActive;
[SerializeField, Interface(typeof(IAxis1D))]
private UnityEngine.Object _axisWhenInactive;
protected IAxis1D AxisWhenActive;
protected IAxis1D AxisWhenInactive;
protected IAxis1D Current => ActiveState.Active ? AxisWhenActive : AxisWhenInactive;
protected virtual void Awake()
{
ActiveState = _activeState as IActiveState;
AxisWhenActive = _axisWhenActive as IAxis1D;
AxisWhenInactive = _axisWhenInactive as IAxis1D;
}
protected virtual void Start()
{
this.AssertField(ActiveState, nameof(ActiveState));
this.AssertField(AxisWhenActive, nameof(AxisWhenActive));
this.AssertField(AxisWhenInactive, nameof(AxisWhenInactive));
}
public float Value()
{
return Current.Value();
}
#region Inject
public void InjectAllAxis1DSwitch(IActiveState activeState, IAxis1D axisWhenActive, IAxis1D axisWhenInactive)
{
InjectActiveState(activeState);
InjectAxisWhenActive(axisWhenActive);
InjectAxisWhenInactive(axisWhenInactive);
}
public void InjectActiveState(IActiveState activeState)
{
_activeState = activeState as UnityEngine.Object;
ActiveState = activeState;
}
private void InjectAxisWhenActive(IAxis1D axisWhenActive)
{
AxisWhenActive = axisWhenActive;
_axisWhenActive = axisWhenActive as UnityEngine.Object;
}
private void InjectAxisWhenInactive(IAxis1D axisWhenInactive)
{
AxisWhenInactive = axisWhenInactive;
_axisWhenInactive = axisWhenInactive as UnityEngine.Object;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a05b4d817add3f44c996755c74db002b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 96dc2ac9879a2ae409623a72b39f8340
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,138 @@
/*
* 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
{
public class Controller :
DataModifier<ControllerDataAsset>,
IController
{
public Handedness Handedness => GetData().Config.Handedness;
public bool IsConnected
{
get
{
var currentData = GetData();
return currentData.IsDataValid && currentData.IsConnected;
}
}
public bool IsPoseValid
{
get
{
var currentData = GetData();
return currentData.IsDataValid &&
currentData.RootPoseOrigin != PoseOrigin.None;
}
}
public bool IsPointerPoseValid
{
get
{
var currentData = GetData();
return currentData.IsDataValid &&
currentData.PointerPoseOrigin != PoseOrigin.None;
}
}
public event Action WhenUpdated = delegate { };
private ITrackingToWorldTransformer TrackingToWorldTransformer =>
GetData().Config.TrackingToWorldTransformer;
public float Scale => TrackingToWorldTransformer != null
? TrackingToWorldTransformer.Transform.lossyScale.x
: 1;
public bool IsButtonUsageAnyActive(ControllerButtonUsage buttonUsage)
{
var currentData = GetData();
return
currentData.IsDataValid &&
(buttonUsage & currentData.ButtonUsageMask) != 0;
}
public bool IsButtonUsageAllActive(ControllerButtonUsage buttonUsage)
{
var currentData = GetData();
return currentData.IsDataValid &&
(buttonUsage & currentData.ButtonUsageMask) == buttonUsage;
}
/// <summary>
/// Retrieves the current controller pose, in world space.
/// </summary>
/// <param name="pose">Set to current pose if `IsPoseValid`; Pose.identity otherwise</param>
/// <returns>Value of `IsPoseValid`</returns>
public bool TryGetPose(out Pose pose)
{
if (!IsPoseValid)
{
pose = Pose.identity;
return false;
}
pose = GetData().Config.TrackingToWorldTransformer.ToWorldPose(GetData().RootPose);
return true;
}
/// <summary>
/// Retrieves the current controller pointer pose, in world space.
/// </summary>
/// <param name="pose">Set to current pose if `IsPoseValid`; Pose.identity otherwise</param>
/// <returns>Value of `IsPoseValid`</returns>
public bool TryGetPointerPose(out Pose pose)
{
if (!IsPointerPoseValid)
{
pose = Pose.identity;
return false;
}
pose = GetData().Config.TrackingToWorldTransformer.ToWorldPose(GetData().PointerPose);
return true;
}
public override void MarkInputDataRequiresUpdate()
{
base.MarkInputDataRequiresUpdate();
if (Started)
{
WhenUpdated();
}
}
protected override void Apply(ControllerDataAsset data)
{
// Default implementation does nothing, to allow instantiation of this modifier directly
}
#region Inject
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4ae60cfa43388e449a7518f36418f81c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,68 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input {
public class ControllerButtonUsageActiveState : MonoBehaviour, IActiveState
{
[SerializeField, Interface(typeof(IController))]
UnityEngine.Object _controller;
private IController Controller;
[SerializeField]
private ControllerButtonUsage _controllerButtonUsage;
public bool Active => Controller.IsButtonUsageAnyActive(_controllerButtonUsage);
protected virtual void Awake()
{
Controller = _controller as IController;
}
protected virtual void Start()
{
this.AssertField(Controller, nameof(Controller));
}
#region Inject
public void InjectAllControllerButtonUsageActiveState(IController controller,
ControllerButtonUsage controllerButtonUsage)
{
InjectController(controller);
InjectControllerButtonUsage(controllerButtonUsage);
}
public void InjectController(IController controller)
{
_controller = controller as UnityEngine.Object;
Controller = controller;
}
public void InjectControllerButtonUsage(ControllerButtonUsage controllerButtonUsage)
{
_controllerButtonUsage = controllerButtonUsage;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ad374a43a946e7d4496818c6b194809f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,57 @@
/*
* 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
{
[Serializable]
public class ControllerDataAsset : ICopyFrom<ControllerDataAsset>
{
public bool IsDataValid;
public bool IsConnected;
public bool IsTracked;
public ControllerButtonUsage ButtonUsageMask;
public Pose RootPose;
public PoseOrigin RootPoseOrigin;
public Pose PointerPose;
public PoseOrigin PointerPoseOrigin;
public ControllerDataSourceConfig Config;
public void CopyFrom(ControllerDataAsset source)
{
IsDataValid = source.IsDataValid;
IsConnected = source.IsConnected;
IsTracked = source.IsTracked;
Config = source.Config;
CopyPosesAndStateFrom(source);
}
public void CopyPosesAndStateFrom(ControllerDataAsset source)
{
ButtonUsageMask = source.ButtonUsageMask;
RootPose = source.RootPose;
RootPoseOrigin = source.RootPoseOrigin;
PointerPose = source.PointerPose;
PointerPoseOrigin = source.PointerPoseOrigin;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe4ca944e80140841985b0f273793641
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
/*
* 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.
*/
namespace Oculus.Interaction.Input
{
/// <summary>
/// A set of constants that are passed to each child of a Controller modifier tree from the root DataSource.
/// </summary>
public class ControllerDataSourceConfig
{
public Handedness Handedness { get; set; }
public ITrackingToWorldTransformer TrackingToWorldTransformer { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1675de914d203a4a910d701ade05ce3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,41 @@
/*
* 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;
namespace Oculus.Interaction.Input
{
// Enum containing all values of Unity.XR.CommonUsage.
[Flags]
public enum ControllerButtonUsage
{
None = 0,
PrimaryButton = 1 << 0,
PrimaryTouch = 1 << 1,
SecondaryButton = 1 << 2,
SecondaryTouch = 1 << 3,
GripButton = 1 << 4,
TriggerButton = 1 << 5,
MenuButton = 1 << 6,
Primary2DAxisClick = 1 << 7,
Primary2DAxisTouch = 1 << 8,
Thumbrest = 1 << 9,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8489868b0a7582e43890f869877224e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,97 @@
/*
* 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;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Input
{
/// <summary>
/// ControllerRef is a utility component that delegates all of its IController implementation
/// to the provided Controller object.
/// </summary>
public class ControllerRef : MonoBehaviour, IController, IActiveState
{
[SerializeField, Interface(typeof(IController))]
private UnityEngine.Object _controller;
private IController Controller;
protected virtual void Awake()
{
Controller = _controller as IController;
}
protected virtual void Start()
{
this.AssertField(Controller, nameof(Controller));
}
public Handedness Handedness => Controller.Handedness;
public bool IsConnected => Controller.IsConnected;
public bool IsPoseValid => Controller.IsPoseValid;
public event Action WhenUpdated
{
add => Controller.WhenUpdated += value;
remove => Controller.WhenUpdated -= value;
}
public bool Active => IsConnected;
public bool TryGetPose(out Pose pose)
{
return Controller.TryGetPose(out pose);
}
public bool TryGetPointerPose(out Pose pose)
{
return Controller.TryGetPointerPose(out pose);
}
public float Scale => Controller.Scale;
public bool IsButtonUsageAnyActive(ControllerButtonUsage buttonUsage)
{
return Controller.IsButtonUsageAnyActive(buttonUsage);
}
public bool IsButtonUsageAllActive(ControllerButtonUsage buttonUsage)
{
return Controller.IsButtonUsageAllActive(buttonUsage);
}
#region Inject
public void InjectAllControllerRef(IController controller)
{
InjectController(controller);
}
public void InjectController(IController controller)
{
_controller = controller as UnityEngine.Object;
Controller = controller;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3673df66324d8f34d9433049c54de631
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
/*
* 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
{
public interface IController
{
Handedness Handedness { get; }
bool IsConnected { get; }
bool IsPoseValid { get; }
bool TryGetPose(out Pose pose);
bool TryGetPointerPose(out Pose pose);
float Scale { get; }
bool IsButtonUsageAnyActive(ControllerButtonUsage buttonUsage);
bool IsButtonUsageAllActive(ControllerButtonUsage buttonUsage);
event Action WhenUpdated;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77db11dffd48abd4cb96a6076a2a2545
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
/*
* 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.
*/
namespace Oculus.Interaction.Input
{
public interface IControllerDataModifier
{
void Apply(ControllerDataAsset controllerDataAsset, Handedness handedness);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b392af6b92984f2887f80975b07141ba
timeCreated: 1629344165

View File

@ -0,0 +1,115 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
public abstract class
DataModifier<TData> : DataSource<TData>
where TData : class, ICopyFrom<TData>, new()
{
[Header("Data Modifier")]
[SerializeField, Interface(nameof(_modifyDataFromSource))]
protected UnityEngine.Object _iModifyDataFromSourceMono;
private IDataSource<TData> _modifyDataFromSource;
[SerializeField]
[Tooltip("If this is false, then this modifier will simply pass through " +
"data without performing any modification. This saves on memory " +
"and computation")]
private bool _applyModifier = true;
private static TData InvalidAsset { get; } = new TData();
private TData _thisDataAsset;
private TData _currentDataAsset = InvalidAsset;
protected override TData DataAsset => _currentDataAsset;
public virtual IDataSource<TData> ModifyDataFromSource => _modifyDataFromSource == null
? (_modifyDataFromSource = _iModifyDataFromSourceMono as IDataSource<TData>)
: _modifyDataFromSource;
public override int CurrentDataVersion
{
get
{
return _applyModifier
? base.CurrentDataVersion
: ModifyDataFromSource.CurrentDataVersion;
}
}
public void ResetSources(IDataSource<TData> modifyDataFromSource, IDataSource updateAfter, UpdateModeFlags updateMode)
{
ResetUpdateAfter(updateAfter, updateMode);
_modifyDataFromSource = modifyDataFromSource;
_currentDataAsset = InvalidAsset;
}
protected override void UpdateData()
{
if (_applyModifier)
{
if (_thisDataAsset == null)
{
_thisDataAsset = new TData();
}
_thisDataAsset.CopyFrom(ModifyDataFromSource.GetData());
_currentDataAsset = _thisDataAsset;
Apply(_currentDataAsset);
}
else
{
_currentDataAsset = ModifyDataFromSource.GetData();
}
}
protected abstract void Apply(TData data);
protected override void Start()
{
this.BeginStart(ref _started, ()=>base.Start());
this.AssertField(ModifyDataFromSource, nameof(ModifyDataFromSource));
this.EndStart(ref _started);
}
#region Inject
public void InjectAllDataModifier(UpdateModeFlags updateMode, IDataSource updateAfter, IDataSource<TData> modifyDataFromSource, bool applyModifier)
{
base.InjectAllDataSource(updateMode, updateAfter);
InjectModifyDataFromSource(modifyDataFromSource);
InjectApplyModifier(applyModifier);
}
public void InjectModifyDataFromSource(IDataSource<TData> modifyDataFromSource)
{
_modifyDataFromSource = modifyDataFromSource;
_iModifyDataFromSourceMono = modifyDataFromSource as Object;
}
public void InjectApplyModifier(bool applyModifier)
{
_applyModifier = applyModifier;
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 12efd6a9760b4bcaafd9cefbe1db0bad
timeCreated: 1630526111

View File

@ -0,0 +1,204 @@
/*
* 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
{
public interface IDataSource
{
int CurrentDataVersion { get; }
void MarkInputDataRequiresUpdate();
event Action InputDataAvailable;
}
public interface IDataSource<TData> : IDataSource
{
TData GetData();
}
public abstract class DataSource<TData> : MonoBehaviour, IDataSource<TData>
where TData : class, ICopyFrom<TData>, new()
{
public bool Started => _started;
protected bool _started = false;
private bool _requiresUpdate = true;
[Flags]
public enum UpdateModeFlags
{
Manual = 0,
UnityUpdate = 1 << 0,
UnityFixedUpdate = 1 << 1,
UnityLateUpdate = 1 << 2,
AfterPreviousStep = 1 << 3
}
[Header("Update")]
[SerializeField]
private UpdateModeFlags _updateMode;
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;
// Notifies that new data is available for query via GetData() method.
// Do not use this event if you are reading data from a `Oculus.Interaction.Input.Hand` object,
// instead, use the `Updated` event on that class.
public event Action InputDataAvailable = delegate { };
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(); }
}
public TData GetData()
{
if (RequiresUpdate())
{
UpdateData();
_requiresUpdate = false;
}
return DataAsset;
}
protected bool RequiresUpdate()
{
return _requiresUpdate;
}
/// <summary>
/// Marks the DataAsset stored as outdated, which means it will be
/// re-processed JIT during the next call to GetData.
/// </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
public void InjectAllDataSource(UpdateModeFlags updateMode, IDataSource updateAfter)
{
InjectUpdateMode(updateMode);
InjectUpdateAfter(updateAfter);
}
public void InjectUpdateMode(UpdateModeFlags updateMode)
{
_updateMode = updateMode;
}
public void InjectUpdateAfter(IDataSource updateAfter)
{
_updateAfter = updateAfter as UnityEngine.Object;
UpdateAfter = updateAfter;
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e3eaa7f7e074674929b88928616f063
timeCreated: 1630527498

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b30e59c0241c28d46b6f42afca0d444d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,63 @@
/*
* 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
{
public class Hmd : DataModifier<HmdDataAsset>, IHmd
{
public event Action WhenUpdated = delegate { };
protected override void Apply(HmdDataAsset data)
{
// Default implementation does nothing, to allow instantiation of this modifier directly
}
public override void MarkInputDataRequiresUpdate()
{
base.MarkInputDataRequiresUpdate();
if (Started)
{
WhenUpdated();
}
}
public bool TryGetRootPose(out Pose pose)
{
var currentData = GetData();
if (!currentData.IsTracked)
{
pose = Pose.identity;
return false;
}
ITrackingToWorldTransformer transformer = GetData().Config.TrackingToWorldTransformer;
pose = transformer.ToWorldPose(currentData.Root);
return true;
}
#region Inject
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2606bf2f0c914a7aba4390f29ba2eb6e
timeCreated: 1629334585

View File

@ -0,0 +1,42 @@
/*
* 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
{
[Serializable]
public class HmdDataAsset : ICopyFrom<HmdDataAsset>
{
public Pose Root;
public bool IsTracked;
public int FrameId;
public HmdDataSourceConfig Config;
public void CopyFrom(HmdDataAsset source)
{
Root = source.Root;
IsTracked = source.IsTracked;
FrameId = source.FrameId;
Config = source.Config;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 332eb9fb3feb493aa2632f2739bf3d41
timeCreated: 1629321466

View File

@ -0,0 +1,30 @@
/*
* 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.
*/
namespace Oculus.Interaction.Input
{
/// <summary>
/// A set of constants that are passed to each child of a Hand modifier tree from the root DataSource.
/// </summary>
public class HmdDataSourceConfig
{
public ITrackingToWorldTransformer TrackingToWorldTransformer { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5e6945eb8bd2e9941a48a55b14cda4d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,70 @@
/*
* 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;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Input
{
/// <summary>
/// A set of constants that are passed to each child of a Hand modifier tree from the root DataSource.
/// </summary>
public class HmdRef : MonoBehaviour, IHmd
{
[SerializeField, Interface(typeof(IHmd))]
private UnityEngine.Object _hmd;
private IHmd Hmd;
public event Action WhenUpdated
{
add => Hmd.WhenUpdated += value;
remove => Hmd.WhenUpdated -= value;
}
protected virtual void Awake()
{
Hmd = _hmd as IHmd;
}
protected virtual void Start()
{
this.AssertField(Hmd, nameof(Hmd));
}
public bool TryGetRootPose(out Pose pose)
{
return Hmd.TryGetRootPose(out pose);
}
#region Inject
public void InjectAllHmdRef(IHmd hmd)
{
InjectHmd(hmd);
}
public void InjectHmd(IHmd hmd)
{
_hmd = hmd as UnityEngine.Object;
Hmd = hmd;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 484167e684014224c9a8bff92b293c6a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
/*
* 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
{
public interface IHmd
{
bool TryGetRootPose(out Pose pose);
event Action WhenUpdated;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d92925b93eb01cf4ba528518efe2705a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c02ebbf428f995b45a218658ebd541d6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4b1422466b455b248ad5e0e27e1e043e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,53 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
public class FixedScaleHand : Hand
{
[SerializeField]
private float _scale = 1f;
protected override void Apply(HandDataAsset data)
{
Pose rootToPointer = PoseUtils.Delta(data.Root, data.PointerPose);
rootToPointer.position = (rootToPointer.position / data.HandScale) * _scale;
PoseUtils.Multiply(data.Root, rootToPointer, ref data.PointerPose);
data.HandScale = _scale;
}
#region Inject
public void InjectAllFixedScaleDataModifier(UpdateModeFlags updateMode, IDataSource updateAfter,
DataModifier<HandDataAsset> modifyDataFromSource, bool applyModifier, float scale)
{
base.InjectAllHand(updateMode, updateAfter, modifyDataFromSource, applyModifier);
InjectScale(scale);
}
public void InjectScale(float scale)
{
_scale = scale;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b927808915b4eba41b76168390b99c24
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,311 @@
/*
* 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 UnityEngine;
using System.Runtime.InteropServices;
using UnityEngine.Assertions;
using System.Collections.Generic;
namespace Oculus.Interaction.Input.Filter
{
// Temporary structure used to pass data to and from native components
[StructLayout(LayoutKind.Sequential)]
public struct HandData
{
private const int NumHandJoints = 24;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = NumHandJoints * 4, ArraySubType = UnmanagedType.R4)]
private float[] jointValues;
private float _rootRotX;
private float _rootRotY;
private float _rootRotZ;
private float _rootRotW;
private float _rootPosX;
private float _rootPosY;
private float _rootPosZ;
public void Init()
{
jointValues = new float[NumHandJoints * 4];
}
public void SetData(Quaternion[] joints, Pose root)
{
Assert.AreEqual(NumHandJoints, joints.Length);
for (int jointIndex = 0; jointIndex < NumHandJoints; jointIndex++)
{
Quaternion joint = joints[jointIndex];
int jointValueIndex = jointIndex * 4;
jointValues[jointValueIndex + 0] = joint.x;
jointValues[jointValueIndex + 1] = joint.y;
jointValues[jointValueIndex + 2] = joint.z;
jointValues[jointValueIndex + 3] = joint.w;
}
this._rootRotX = root.rotation.x;
this._rootRotY = root.rotation.y;
this._rootRotZ = root.rotation.z;
this._rootRotW = root.rotation.w;
this._rootPosX = root.position.x;
this._rootPosY = root.position.y;
this._rootPosZ = root.position.z;
}
public void GetData(ref Quaternion[] joints, out Pose root)
{
Assert.AreEqual(NumHandJoints, joints.Length);
for (int jointIndex = 0; jointIndex < NumHandJoints; jointIndex++)
{
int jointValueIndex = jointIndex * 4;
joints[jointIndex].x = jointValues[jointValueIndex + 0];
joints[jointIndex].y = jointValues[jointValueIndex + 1];
joints[jointIndex].z = jointValues[jointValueIndex + 2];
joints[jointIndex].w = jointValues[jointValueIndex + 3];
}
root = new Pose(new Vector3(_rootPosX, _rootPosY, _rootPosZ),
new Quaternion(_rootRotX, _rootRotY, _rootRotZ, _rootRotW));
}
}
public class HandFilter : Hand
{
#region Oculus Library Methods and Constants
[DllImport("InteractionSdk")]
private static extern int isdk_DataSource_Create(int id);
[DllImport("InteractionSdk")]
private static extern int isdk_DataSource_Destroy(int handle);
[DllImport("InteractionSdk")]
private static extern int isdk_DataModifier_Create(int id, int handle);
[DllImport("InteractionSdk")]
private static extern int isdk_DataSource_Update(int handle);
[DllImport("InteractionSdk")]
private static extern int isdk_DataSource_GetData(int handle, ref HandData data);
[DllImport("InteractionSdk")]
private static extern int isdk_ExternalHandSource_SetData(int handle, in HandData data);
[DllImport("InteractionSdk")]
private static extern int isdk_DataSource_SetAttributeFloat(int handle, int attrId, float value);
enum AttributeId
{
Unknown = 0,
WristPosBeta,
WristPosMinCutOff,
WristRotBeta,
WristRotMinCutOff,
FingerRotBeta,
FingerRotMinCutOff,
Frequency,
WristPosDeltaCutOff,
WristRotDeltaCutOff,
FingerRotDeltaCutOff,
};
private const int _isdkExternalHandSourceId = 2;
private const int _isdkOneEuroHandModifierId = 1;
private const int _isdkSuccess = 0;
#endregion Oculus Library Methods and Constants
#region Tuneable Values
[Header("Settings", order =-1)]
[Tooltip("Applies a One Euro Filter when filter parameters are provided")]
[SerializeField, Optional]
private HandFilterParameterBlock _filterParameters = null;
#endregion Tuneable Values
private int _dataSourceHandle = -1;
private int _handModifierHandle = -1;
private const string _logPrefix = "[Oculus.Interaction]";
private bool _hasFlaggedError = false;
private HandData _handData = new HandData();
protected virtual void Awake()
{
_handData.Init();
_dataSourceHandle = isdk_DataSource_Create(_isdkExternalHandSourceId);
this.AssertIsTrue(_dataSourceHandle >= 0, $"{_logPrefix} Unable to allocate external hand data source!");
_handModifierHandle = isdk_DataModifier_Create(_isdkOneEuroHandModifierId, _dataSourceHandle);
this.AssertIsTrue(_handModifierHandle >= 0, $"{_logPrefix} Unable to allocate one euro hand data modifier!");
}
protected virtual void OnDestroy()
{
int result = -1;
//Release the filter and source
result = isdk_DataSource_Destroy(_handModifierHandle);
this.AssertIsTrue(_isdkSuccess == result, $"{nameof(_handModifierHandle)} destroy was unsuccessful. ");
result = isdk_DataSource_Destroy(_dataSourceHandle);
this.AssertIsTrue(_isdkSuccess == result, $"{nameof(_dataSourceHandle)} destroy was unsuccessful. ");
}
protected override void Apply(HandDataAsset handDataAsset)
{
base.Apply(handDataAsset);
if (!handDataAsset.IsTracked)
{
return;
}
if (UpdateFilterParameters() && UpdateHandData(handDataAsset))
{
return;
}
if (_hasFlaggedError)
return;
_hasFlaggedError = true;
Debug.LogError("Unable to send value to filter, InteractionSDK plugin may be missing or corrupted");
}
protected bool UpdateFilterParameters()
{
if (_filterParameters == null)
return true;
int result = -1;
// wrist position
result = isdk_DataSource_SetAttributeFloat(
_handModifierHandle, (int)AttributeId.WristPosBeta,
_filterParameters.wristPositionParameters.Beta);
if (result != _isdkSuccess)
{
return false;
}
result = isdk_DataSource_SetAttributeFloat(
_handModifierHandle, (int)AttributeId.WristPosMinCutOff,
_filterParameters.wristPositionParameters.MinCutoff);
if (result != _isdkSuccess)
{
return false;
}
result = isdk_DataSource_SetAttributeFloat(
_handModifierHandle, (int)AttributeId.WristPosDeltaCutOff,
_filterParameters.wristPositionParameters.DCutoff);
if (result != _isdkSuccess)
{
return false;
}
// wrist rotation
result = isdk_DataSource_SetAttributeFloat(
_handModifierHandle, (int)AttributeId.WristRotBeta,
_filterParameters.wristRotationParameters.Beta);
if (result != _isdkSuccess)
{
return false;
}
result = isdk_DataSource_SetAttributeFloat(
_handModifierHandle, (int)AttributeId.WristRotMinCutOff,
_filterParameters.wristRotationParameters.MinCutoff);
if (result != _isdkSuccess)
{
return false;
}
result = isdk_DataSource_SetAttributeFloat(
_handModifierHandle, (int)AttributeId.WristRotDeltaCutOff,
_filterParameters.wristRotationParameters.DCutoff);
if (result != _isdkSuccess)
{
return false;
}
// finger rotation
result = isdk_DataSource_SetAttributeFloat(
_handModifierHandle, (int)AttributeId.FingerRotBeta,
_filterParameters.fingerRotationParameters.Beta);
if (result != _isdkSuccess)
{
return false;
}
result = isdk_DataSource_SetAttributeFloat(
_handModifierHandle, (int)AttributeId.FingerRotMinCutOff,
_filterParameters.fingerRotationParameters.MinCutoff);
if (result != _isdkSuccess)
{
return false;
}
result = isdk_DataSource_SetAttributeFloat(
_handModifierHandle, (int)AttributeId.FingerRotDeltaCutOff,
_filterParameters.fingerRotationParameters.DCutoff);
if (result != _isdkSuccess)
{
return false;
}
// frequency
result = isdk_DataSource_SetAttributeFloat(
_handModifierHandle, (int)AttributeId.Frequency,
_filterParameters.frequency);
if (result != _isdkSuccess)
{
return false;
}
return true;
}
protected bool UpdateHandData(HandDataAsset handDataAsset)
{
// null parameters implies don't filter
if (_filterParameters == null)
return true;
// pipe data asset into temp struct
_handData.SetData(handDataAsset.Joints,handDataAsset.Root);
// Send it
int result = isdk_ExternalHandSource_SetData(_dataSourceHandle, _handData);
if (result != _isdkSuccess)
{
return false;
}
// Update
result = isdk_DataSource_Update(_handModifierHandle);
if (result != _isdkSuccess)
{
return false;
}
// Get result
result = isdk_DataSource_GetData(_handModifierHandle, ref _handData);
if (result != _isdkSuccess)
{
return false;
}
// Copy results into our hand data asset
_handData.GetData(ref handDataAsset.Joints, out handDataAsset.Root);
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fcebab82b3c5dd4fac023079b7fed29
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,109 @@
/*
* 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 Oculus.Interaction.Throw;
using UnityEngine;
using UnityEngine.Serialization;
namespace Oculus.Interaction.Input
{
/// <summary>
/// Tracks the history of finger rotations and can be set to use the joint
/// rotations from some number of frames ago.
/// </summary>
public class JointRotationHistoryHand : Hand
{
[SerializeField]
private int _historyLength = 60;
[SerializeField]
private int _historyOffset = 5;
private Quaternion[][] _jointHistory = new Quaternion[(int)HandJointId.HandMaxSkinnable][];
private int _historyIndex = 0;
private int _capturedDataVersion;
protected override void Start()
{
base.Start();
for (int i = 0; i < _jointHistory.Length; i++)
{
_jointHistory[i] = new Quaternion[_historyLength];
for (int j = 0; j < _historyLength; j++)
{
_jointHistory[i][j] = Quaternion.identity;
}
}
}
#region DataModifier Implementation
protected override void Apply(HandDataAsset data)
{
if (!data.IsDataValid)
{
return;
}
if (_capturedDataVersion != ModifyDataFromSource.CurrentDataVersion)
{
_capturedDataVersion = ModifyDataFromSource.CurrentDataVersion;
_historyIndex = (_historyIndex + 1) % _historyLength;
for (int i = 0; i < _jointHistory.Length; i++)
{
_jointHistory[i][_historyIndex] = data.Joints[i];
}
}
_historyOffset = Mathf.Clamp(_historyOffset, 0, _historyLength);
int index = (_historyIndex + _historyLength - _historyOffset) % _historyLength;
for (int i = 0; i < _jointHistory.Length; i++)
{
data.Joints[i] = _jointHistory[i][index];
}
}
#endregion
public void SetHistoryOffset(int offset)
{
_historyOffset = offset;
MarkInputDataRequiresUpdate();
}
#region Inject
public void InjectAllJointHistoryHand(UpdateModeFlags updateMode, IDataSource updateAfter,
DataModifier<HandDataAsset> modifyDataFromSource, bool applyModifier,
int historyLength, int historyOffset)
{
base.InjectAllHand(updateMode, updateAfter, modifyDataFromSource, applyModifier);
InjectHistoryLength(historyLength);
SetHistoryOffset(historyOffset);
}
public void InjectHistoryLength(int historyLength)
{
_historyLength = historyLength;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b5d61cebc8bc2644b45d6034694ea8f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,55 @@
/*
* 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.
*/
namespace Oculus.Interaction.Input
{
public class LastKnownGoodHand : Hand
{
private readonly HandDataAsset _lastState = new HandDataAsset();
protected override void Apply(HandDataAsset data)
{
bool shouldUseData = data.IsHighConfidence ||
data.RootPoseOrigin == PoseOrigin.FilteredTrackedPose ||
data.RootPoseOrigin == PoseOrigin.SyntheticPose;
if (data.IsDataValid && data.IsTracked && shouldUseData)
{
_lastState.CopyFrom(data);
}
else if (_lastState.IsDataValid && data.IsConnected)
{
// No high confidence data, use last known good.
// Only copy pose data, not confidence/tracked flags.
data.CopyPosesFrom(_lastState);
data.RootPoseOrigin = PoseOrigin.SyntheticPose;
data.IsDataValid = true;
data.IsTracked = true;
data.IsHighConfidence = true;
}
else
{
// This hand is not connected, or has never seen valid data.
data.IsTracked = false;
data.IsHighConfidence = false;
data.RootPoseOrigin = PoseOrigin.None;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7daff26e66096cd49a253ca3ab592c03
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,522 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
/// <summary>
/// Alters hand data piped into this modifier to lock and unlock joints (wrist position and rotation,
/// finger joint rotations) When switching between locked and unlocked states, additionally smooths
/// out transitions by easing between source hand data and target hand data.
/// </summary>
public class SyntheticHand : Hand
{
[System.Flags]
public enum WristLockMode
{
Position = 1 << 0,
Rotation = 1 << 1,
Full = (1 << 2) - 1
}
[SerializeField]
private ProgressCurve _wristPositionLockCurve;
[SerializeField]
private ProgressCurve _wristPositionUnlockCurve;
[SerializeField]
private ProgressCurve _wristRotationLockCurve;
[SerializeField]
private ProgressCurve _wristRotationUnlockCurve;
[SerializeField]
private ProgressCurve _jointLockCurve;
[SerializeField]
private ProgressCurve _jointUnlockCurve;
/// <summary>
/// Use this factor to control how much the fingers can spread when nearby a constrained pose.
/// </summary>
[SerializeField]
[Tooltip("Use this factor to control how much the fingers can spread when nearby a constrained pose.")]
private float _spreadAllowance = 5f;
public System.Action UpdateRequired = delegate { };
private readonly HandDataAsset _lastStates = new HandDataAsset();
private float _wristPositionOverrideFactor;
private float _wristRotationOverrideFactor;
private float[] _jointsOverrideFactor = new float[FingersMetadata.HAND_JOINT_IDS.Length];
private ProgressCurve[] _jointLockProgressCurves = new ProgressCurve[FingersMetadata.HAND_JOINT_IDS.Length];
private ProgressCurve[] _jointUnlockProgressCurves = new ProgressCurve[FingersMetadata.HAND_JOINT_IDS.Length];
private Pose _desiredWristPose;
private bool _wristPositionLocked;
private bool _wristRotationLocked;
private Pose _constrainedWristPose;
private Pose _lastWristPose;
private Quaternion[] _desiredJointsRotation = new Quaternion[FingersMetadata.HAND_JOINT_IDS.Length];
private Quaternion[] _constrainedJointRotations = new Quaternion[FingersMetadata.HAND_JOINT_IDS.Length];
private Quaternion[] _lastSyntheticRotation = new Quaternion[FingersMetadata.HAND_JOINT_IDS.Length];
private JointFreedom[] _jointsFreedomLevels = new JointFreedom[FingersMetadata.HAND_JOINT_IDS.Length];
private bool _hasConnectedData;
protected override void Start()
{
this.BeginStart(ref _started, () => base.Start());
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; i++)
{
_jointLockProgressCurves[i] = new ProgressCurve(_jointLockCurve);
_jointUnlockProgressCurves[i] = new ProgressCurve(_jointUnlockCurve);
}
this.EndStart(ref _started);
}
protected override void Apply(HandDataAsset data)
{
if (!Started || !data.IsDataValid || !data.IsTracked || !data.IsHighConfidence)
{
data.IsConnected = false;
data.RootPoseOrigin = PoseOrigin.None;
_hasConnectedData = false;
return;
}
UpdateRequired.Invoke();
_lastStates.CopyFrom(data);
if (!_hasConnectedData)
{
_constrainedWristPose.CopyFrom(data.Root);
_hasConnectedData = true;
}
UpdateJointsRotation(data);
UpdateRootPose(ref data.Root);
data.RootPoseOrigin = PoseOrigin.SyntheticPose;
}
/// <summary>
/// Updates the pose of the root of the hand
/// using the visual provided values. Sometimes this
/// might require lerping between the tracked pose
/// and the provided one to improve the movement of the hand
/// without worrying about when the overwrite value was written.
///
/// During this update, the modifier also ensures the unlocking
/// animations are executed.
/// </summary>
/// <param name="root">The tracked root value to modify</param>
private void UpdateRootPose(ref Pose root)
{
float smoothPositionFactor = _wristPositionLocked ? _wristPositionLockCurve.Progress() : _wristPositionUnlockCurve.Progress();
Vector3 position = Vector3.Lerp(root.position, _desiredWristPose.position, _wristPositionOverrideFactor);
root.position = Vector3.Lerp(_constrainedWristPose.position, position, smoothPositionFactor);
float smoothRotationFactor = _wristRotationLocked ? _wristRotationLockCurve.Progress() : _wristRotationUnlockCurve.Progress();
Quaternion rotation = Quaternion.Lerp(root.rotation, _desiredWristPose.rotation, _wristRotationOverrideFactor);
root.rotation = Quaternion.Lerp(_constrainedWristPose.rotation, rotation, smoothRotationFactor);
_lastWristPose.CopyFrom(root);
}
/// <summary>
/// Updates the rotation of the joints in the hand
/// using the visual provided values. Sometimes this
/// might require lerping between the tracked pose
/// and the provided ones to improve the movement of the fingers
/// without worrying about when the overwrite values were written.
///
/// During this update the modifier also ensures that fingers that disallow
/// some movement (locked or constrained) have their values properly set, and
/// when there is an unlock event the finger values are smoothly animated back to
/// their tracked rotations.
/// </summary>
/// <param name="data">The entire hand data structure to read and write the joints rotations from</param>
private void UpdateJointsRotation(HandDataAsset data)
{
float extraRotationAllowance = 0f;
Quaternion[] jointRotations = data.Joints;
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; ++i)
{
JointFreedom freedomLevel = _jointsFreedomLevels[i];
Quaternion desiredRotation = _desiredJointsRotation[i];
float overrideFactor = _jointsOverrideFactor[i];
int rawJointIndex = (int)FingersMetadata.HAND_JOINT_IDS[i];
if (freedomLevel == JointFreedom.Free)
{
//nothing to do, we move the finger freely
}
else if (freedomLevel == JointFreedom.Locked)
{
jointRotations[rawJointIndex] = Quaternion.Slerp(
jointRotations[rawJointIndex],
desiredRotation,
overrideFactor);
}
else if (freedomLevel == JointFreedom.Constrained)
{
bool jointCanSpread = false;
if (FingersMetadata.HAND_JOINT_CAN_SPREAD[i])
{
jointCanSpread = true;
extraRotationAllowance = 0f;
}
Quaternion maxRotation = desiredRotation * Quaternion.Euler(0f, 0f, -90f * extraRotationAllowance);
float overRotation = OverFlex(jointRotations[rawJointIndex], maxRotation);
extraRotationAllowance = Mathf.Max(extraRotationAllowance, overRotation);
if (overRotation < 0f)
{
jointRotations[rawJointIndex] = Quaternion.Slerp(
jointRotations[rawJointIndex],
maxRotation,
overrideFactor);
}
else if (jointCanSpread)
{
Quaternion trackedRotation = jointRotations[rawJointIndex];
float spreadAngle = Vector3.SignedAngle(
trackedRotation * Vector3.forward,
maxRotation * Vector3.forward,
trackedRotation * Vector3.up);
float spreadFactor = 1f - Mathf.Clamp01(overRotation * _spreadAllowance);
trackedRotation = trackedRotation * Quaternion.Euler(0f, spreadAngle * spreadFactor, 0f);
jointRotations[rawJointIndex] = trackedRotation;
}
}
float smoothFactor = _jointsFreedomLevels[i] == JointFreedom.Free ?
_jointUnlockProgressCurves[i].Progress()
: _jointLockProgressCurves[i].Progress();
jointRotations[rawJointIndex] = Quaternion.Slerp(
_constrainedJointRotations[i],
jointRotations[rawJointIndex],
smoothFactor);
_lastSyntheticRotation[i] = jointRotations[rawJointIndex];
}
}
/// <summary>
/// Stores the rotation data for all joints in the hand, to be applied during the ApplyHand event.
/// </summary>
/// <param name="jointRotations">The joint rotations following the FingersMetadata.HAND_JOINT_IDS format.</param>
/// <param name="overrideFactor">How much to lerp the fingers from the tracked (raw) state to the provided one.</param>
public void OverrideAllJoints(in Quaternion[] jointRotations, float overrideFactor)
{
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; ++i)
{
_desiredJointsRotation[i] = jointRotations[i];
_jointsOverrideFactor[i] = overrideFactor;
}
}
/// <summary>
/// Stores the rotation data for all joints for the given finger, to be applied during the ApplyHand event.
/// </summary>
/// <param name="finger">The finger for which to lock joints.</param>
/// <param name="rotations">The joint rotations for each joint on the finger</param>
/// <param name="overrideFactor">How much to lerp the fingers from the tracked (raw) state to the provided one.</param>
public void OverrideFingerRotations(HandFinger finger, Quaternion[] rotations, float overrideFactor)
{
int[] jointIndices = FingersMetadata.FINGER_TO_JOINT_INDEX[(int)finger];
for (int i = 0; i < jointIndices.Length; i++)
{
OverrideJointRotationAtIndex(jointIndices[i], rotations[i], overrideFactor);
}
}
public void OverrideJointRotation(HandJointId jointId, Quaternion rotation, float overrideFactor)
{
int jointIndex = FingersMetadata.HandJointIdToIndex(jointId);
OverrideJointRotationAtIndex(jointIndex, rotation, overrideFactor);
}
private void OverrideJointRotationAtIndex(int jointIndex, Quaternion rotation, float overrideFactor)
{
_desiredJointsRotation[jointIndex] = rotation;
_jointsOverrideFactor[jointIndex] = overrideFactor;
}
/// <summary>
/// Immediately locks an individual finger (all its internal joints) at the last known value.
/// </summary>
/// <param name="finger">The finger for which to lock joints.</param>
public void LockFingerAtCurrent(in HandFinger finger)
{
SetFingerFreedom(finger, JointFreedom.Locked);
int fingerIndex = (int)finger;
int[] jointIndexes = FingersMetadata.FINGER_TO_JOINT_INDEX[fingerIndex];
for (int i = 0; i < jointIndexes.Length; ++i)
{
int jointIndex = jointIndexes[i];
int rawJointIndex = (int)FingersMetadata.HAND_JOINT_IDS[jointIndex];
_desiredJointsRotation[jointIndex] = _lastStates.Joints[rawJointIndex];
_jointsOverrideFactor[jointIndex] = 1f;
}
}
public void LockJoint(in HandJointId jointId, Quaternion rotation, float overrideFactor = 1f)
{
int jointIndex = FingersMetadata.HandJointIdToIndex(jointId);
_desiredJointsRotation[jointIndex] = rotation;
_jointsOverrideFactor[jointIndex] = 1f;
SetJointFreedomAtIndex(jointIndex, JointFreedom.Locked);
}
/// <summary>
/// To use in conjunction with OverrideAllJoints, it sets the freedom state for a provided finger.
/// Opposite to LockFingerAtCurrent, this method uses the data provided in OverrideAllJoints instead
/// of the last known state.
/// </summary>
/// <param name="freedomLevel">The freedom level for the finger</param>
public void SetFingerFreedom(in HandFinger finger, in JointFreedom freedomLevel, bool skipAnimation = false)
{
int[] jointIndexes = FingersMetadata.FINGER_TO_JOINT_INDEX[(int)finger];
for (int i = 0; i < jointIndexes.Length; ++i)
{
SetJointFreedomAtIndex(jointIndexes[i], freedomLevel, skipAnimation);
}
}
public void SetJointFreedom(in HandJointId jointId, in JointFreedom freedomLevel, bool skipAnimation = false)
{
int jointIndex = FingersMetadata.HandJointIdToIndex(jointId);
SetJointFreedomAtIndex(jointIndex, freedomLevel, skipAnimation);
}
public JointFreedom GetJointFreedom(in HandJointId jointId)
{
int jointIndex = FingersMetadata.HandJointIdToIndex(jointId);
return _jointsFreedomLevels[jointIndex];
}
/// <summary>
/// Short-hand method for setting the freedom level of all fingers in a hand to Free.
/// Similar to calling SetFingerFreedom for each single finger in the hand
/// with a value of FingerFreedom.Free for the freedomLevel
/// </summary>
public void FreeAllJoints()
{
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; ++i)
{
SetJointFreedomAtIndex(i, JointFreedom.Free);
}
}
private void SetJointFreedomAtIndex(int jointId, in JointFreedom freedomLevel, bool skipAnimation = false)
{
JointFreedom currentFreedom = _jointsFreedomLevels[jointId];
if (currentFreedom != freedomLevel)
{
bool locked = freedomLevel == JointFreedom.Locked
|| freedomLevel == JointFreedom.Constrained;
UpdateProgressCurve(ref _jointLockProgressCurves[jointId],
ref _jointUnlockProgressCurves[jointId],
locked, skipAnimation);
_constrainedJointRotations[jointId] = _lastSyntheticRotation[jointId];
}
_jointsFreedomLevels[jointId] = freedomLevel;
}
/// <summary>
/// Stores the desired pose to set the wrist of the hand to.
/// This is not necessarily the final pose of the hand, as it allows
/// lerping between the tracked and provided one during the ApplyHand phase.
///
/// To ensure the hand is locked at the desired pose, pass a value of 1 in the overrideFactor
/// </summary>
/// <param name="wristPose">The final pose desired for the wrist</param>
/// <param name="lockMode">Either lock the position, rotation or both (default)</param>
/// <param name="overrideFactor">How much to lerp between the tracked and the provided pose</param>
/// <param name="skipAnimation">Whether to skip the animation curve for this override.</param>
public void LockWristPose(Pose wristPose, float overrideFactor = 1f, WristLockMode lockMode = WristLockMode.Full, bool worldPose = false, bool skipAnimation = false)
{
Pose desiredWristPose = (worldPose && TrackingToWorldTransformer != null ) ?
TrackingToWorldTransformer.ToTrackingPose(wristPose): wristPose;
if ((lockMode & WristLockMode.Position) != 0)
{
LockWristPosition(desiredWristPose.position, overrideFactor, skipAnimation);
}
if ((lockMode & WristLockMode.Rotation) != 0)
{
LockWristRotation(desiredWristPose.rotation, overrideFactor, skipAnimation);
}
}
public void LockWristPosition(Vector3 position, float overrideFactor = 1f, bool skipAnimation = false)
{
_wristPositionOverrideFactor = overrideFactor;
_desiredWristPose.position = position;
if (!_wristPositionLocked)
{
_wristPositionLocked = true;
SyntheticWristLockChangedState(WristLockMode.Position, skipAnimation);
}
}
public void LockWristRotation(Quaternion rotation, float overrideFactor = 1f, bool skipAnimation = false)
{
_wristRotationOverrideFactor = overrideFactor;
_desiredWristPose.rotation = rotation;
if (!_wristRotationLocked)
{
_wristRotationLocked = true;
SyntheticWristLockChangedState(WristLockMode.Rotation, skipAnimation);
}
}
/// <summary>
/// Unlocks the hand (locked at the OverrideWristPose method) starting
/// a timer for the smooth release animation.
/// </summary>
public void FreeWrist(WristLockMode lockMode = WristLockMode.Full)
{
if ((lockMode & WristLockMode.Position) != 0
&& _wristPositionLocked)
{
_wristPositionOverrideFactor = 0f;
_wristPositionLocked = false;
SyntheticWristLockChangedState(WristLockMode.Position);
}
if ((lockMode & WristLockMode.Rotation) != 0
&& _wristRotationLocked)
{
_wristRotationOverrideFactor = 0f;
_wristRotationLocked = false;
SyntheticWristLockChangedState(WristLockMode.Rotation);
}
}
private void SyntheticWristLockChangedState(WristLockMode lockMode, bool skipAnimation = false)
{
if ((lockMode & WristLockMode.Position) != 0)
{
UpdateProgressCurve(ref _wristPositionLockCurve, ref _wristPositionUnlockCurve,
_wristPositionLocked, skipAnimation);
_constrainedWristPose.position = _lastWristPose.position;
}
if ((lockMode & WristLockMode.Rotation) != 0)
{
UpdateProgressCurve(ref _wristRotationLockCurve, ref _wristRotationUnlockCurve,
_wristRotationLocked, skipAnimation);
_constrainedWristPose.rotation = _lastWristPose.rotation;
}
}
/// <summary>
/// Indicates whether a joint's tracked rotation is past a given rotation.
/// Works in local Unity Joint coordinates.
/// This is useful for blocking fingers past the snapping point.
/// </summary>
/// <param name="desiredLocalRot">The known local rotation of the joint. </param>
/// <param name="maxLocalRot">The desired max local rotation of the joint.</param>
/// <returns>A negative scalar proportional to how much the rotation is over the max one, a proportional positive scalar if under.</returns>
private static float OverFlex(in Quaternion desiredLocalRot, in Quaternion maxLocalRot)
{
Vector3 jointDir = desiredLocalRot * Vector3.right;
Vector3 jointTan = desiredLocalRot * Vector3.back;
Vector3 maxDir = maxLocalRot * Vector3.right;
Vector3 difference = Vector3.Cross(jointDir, maxDir);
return Vector3.Dot(jointTan, difference);
}
private static void UpdateProgressCurve(ref ProgressCurve lockProgress, ref ProgressCurve unlockProgress, bool locked, bool skipAnimation)
{
ProgressCurve progress = locked ? lockProgress : unlockProgress;
if (skipAnimation)
{
progress.End();
}
else
{
progress.Start();
}
}
#region Inject
public void InjectAllSyntheticHandModifier(UpdateModeFlags updateMode, IDataSource updateAfter,
DataModifier<HandDataAsset> modifyDataFromSource, bool applyModifier,
ProgressCurve wristPositionLockCurve, ProgressCurve wristPositionUnlockCurve,
ProgressCurve wristRotationLockCurve, ProgressCurve wristRotationUnlockCurve,
ProgressCurve jointLockCurve, ProgressCurve jointUnlockCurve,
float spreadAllowance)
{
base.InjectAllHand(updateMode, updateAfter, modifyDataFromSource, applyModifier);
InjectWristPositionLockCurve(wristPositionLockCurve);
InjectWristPositionUnlockCurve(wristPositionUnlockCurve);
InjectWristRotationLockCurve(wristRotationLockCurve);
InjectWristRotationUnlockCurve(wristRotationUnlockCurve);
InjectJointLockCurve(jointLockCurve);
InjectJointUnlockCurve(jointUnlockCurve);
InjectSpreadAllowance(spreadAllowance);
}
public void InjectWristPositionLockCurve(ProgressCurve wristPositionLockCurve) {
_wristPositionLockCurve = wristPositionLockCurve;
}
public void InjectWristPositionUnlockCurve(ProgressCurve wristPositionUnlockCurve) {
_wristPositionUnlockCurve = wristPositionUnlockCurve;
}
public void InjectWristRotationLockCurve(ProgressCurve wristRotationLockCurve) {
_wristRotationLockCurve = wristRotationLockCurve;
}
public void InjectWristRotationUnlockCurve(ProgressCurve wristRotationUnlockCurve) {
_wristRotationUnlockCurve = wristRotationUnlockCurve;
}
public void InjectJointLockCurve(ProgressCurve jointLockCurve) {
_jointLockCurve = jointLockCurve;
}
public void InjectJointUnlockCurve(ProgressCurve jointUnlockCurve) {
_jointUnlockCurve = jointUnlockCurve;
}
public void InjectSpreadAllowance(float spreadAllowance) {
_spreadAllowance = spreadAllowance;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c67033f580359c4581dff1ccffcca91
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,195 @@
/*
* 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.
*/
namespace Oculus.Interaction.Input
{
public enum JointFreedom
{
Free,
Constrained,
Locked
}
/// <summary>
/// This class contains a series of useful fingers-related data structures
/// to be used for optimal calculations without relying in dictionaries.
///
/// Since we always assume the hand pose information to be sorted in
/// the HAND_JOINT_IDS order, we can align multiple data structures
/// that follow that convention.
/// </summary>
public class FingersMetadata
{
public static JointFreedom[] DefaultFingersFreedom()
{
return new JointFreedom[Constants.NUM_FINGERS]
{
JointFreedom.Locked,
JointFreedom.Locked,
JointFreedom.Constrained,
JointFreedom.Constrained,
JointFreedom.Free
};
}
public static int HandJointIdToIndex(HandJointId id)
{
return (int)id - (int)HandJointId.HandThumb0;
}
/// <summary>
/// Valid identifiers for the i-bone of a hand.
/// </summary>
public static readonly HandJointId[] HAND_JOINT_IDS = new HandJointId[]
{
HandJointId.HandThumb0,
HandJointId.HandThumb1,
HandJointId.HandThumb2,
HandJointId.HandThumb3,
HandJointId.HandIndex1,
HandJointId.HandIndex2,
HandJointId.HandIndex3,
HandJointId.HandMiddle1,
HandJointId.HandMiddle2,
HandJointId.HandMiddle3,
HandJointId.HandRing1,
HandJointId.HandRing2,
HandJointId.HandRing3,
HandJointId.HandPinky0,
HandJointId.HandPinky1,
HandJointId.HandPinky2,
HandJointId.HandPinky3
};
/// <summary>
/// This array is used to convert from Finger id to the list indices
/// of its joint in the HAND_JOINT_IDS list.
/// </summary>
public static readonly int[][] FINGER_TO_JOINT_INDEX = new int[][]
{
new[] {0,1,2,3},
new[] {4,5,6},
new[] {7,8,9},
new[] {10,11,12},
new[] {13,14,15,16}
};
public static readonly HandJointId[][] FINGER_TO_JOINTS = new[]
{
new HandJointId[]
{
HandJointId.HandThumb0,
HandJointId.HandThumb1,
HandJointId.HandThumb2,
HandJointId.HandThumb3
},
new HandJointId[]
{
HandJointId.HandIndex1,
HandJointId.HandIndex2,
HandJointId.HandIndex3
},
new HandJointId[]
{
HandJointId.HandMiddle1,
HandJointId.HandMiddle2,
HandJointId.HandMiddle3
},
new HandJointId[]
{
HandJointId.HandRing1,
HandJointId.HandRing2,
HandJointId.HandRing3
},
new HandJointId[]
{
HandJointId.HandPinky0,
HandJointId.HandPinky1,
HandJointId.HandPinky2,
HandJointId.HandPinky3
}
};
/// <summary>
/// Array order following HAND_JOINT_IDS that indicates if the i joint
/// can spread (rotate around Y). Should be true for the root of the fingers
/// but Pink and Thumb are special cases
/// </summary>
public static readonly bool[] HAND_JOINT_CAN_SPREAD = new bool[]
{
true, //HandJointId.HandThumb0
true, //HandJointId.HandThumb1
false,//HandJointId.HandThumb2
false,//HandJointId.HandThumb3
true, //HandJointId.HandIndex1
false,//HandJointId.HandIndex2
false,//HandJointId.HandIndex3
true, //HandJointId.HandMiddle1
false,//HandJointId.HandMiddle2
false,//HandJointId.HandMiddle3
true, //HandJointId.HandRing1
false,//HandJointId.HandRing2
false,//HandJointId.HandRing3
true, //HandJointId.HandPinky0
true, //HandJointId.HandPinky1
false,//HandJointId.HandPinky2
false //HandJointId.HandPinky3
};
/// <summary>
/// Map HandJointId to HandFinger
/// </summary>
public static readonly HandFinger[] JOINT_TO_FINGER = new HandFinger[]
{
HandFinger.Invalid,
HandFinger.Invalid,
HandFinger.Thumb,
HandFinger.Thumb,
HandFinger.Thumb,
HandFinger.Thumb,
HandFinger.Index,
HandFinger.Index,
HandFinger.Index,
HandFinger.Middle,
HandFinger.Middle,
HandFinger.Middle,
HandFinger.Ring,
HandFinger.Ring,
HandFinger.Ring,
HandFinger.Pinky,
HandFinger.Pinky,
HandFinger.Pinky,
HandFinger.Pinky,
HandFinger.Thumb,
HandFinger.Index,
HandFinger.Middle,
HandFinger.Ring,
HandFinger.Pinky
};
/// <summary>
/// Map HandJointId to HandFinger
/// </summary>
public static readonly int[] JOINT_TO_FINGER_INDEX = new int[]
{
-1, -1, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 3, 4, 3, 3, 3, 4
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c86919be0f18a54b8706542dc38dede
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,111 @@
/*
* 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.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Input
{
public class FromHandPrefabDataSource : DataSource<HandDataAsset>
{
private readonly HandDataAsset _handDataAsset = new HandDataAsset();
protected override HandDataAsset DataAsset => _handDataAsset;
[SerializeField]
private Handedness _handedness;
public Handedness Handedness => _handedness;
[SerializeField]
private bool _hidePrefabOnStart = true;
[HideInInspector]
[SerializeField]
private List<Transform> _jointTransforms = new List<Transform>();
public List<Transform> JointTransforms => _jointTransforms;
[SerializeField, Interface(typeof(IHandSkeletonProvider))]
private UnityEngine.Object _handSkeletonProvider;
private IHandSkeletonProvider HandSkeletonProvider;
[SerializeField, Interface(typeof(ITrackingToWorldTransformer)), Optional]
private UnityEngine.Object _trackingToWorldTransformer;
private ITrackingToWorldTransformer TrackingToWorldTransformer;
protected virtual void Awake()
{
HandSkeletonProvider = _handSkeletonProvider as IHandSkeletonProvider;
if (_trackingToWorldTransformer != null)
{
TrackingToWorldTransformer =
_trackingToWorldTransformer as ITrackingToWorldTransformer;
}
HandDataSourceConfig Config = _handDataAsset.Config;
Config.Handedness = _handedness;
}
protected override void Start()
{
base.Start();
HandDataSourceConfig Config = _handDataAsset.Config;
Config.TrackingToWorldTransformer = TrackingToWorldTransformer;
Config.HandSkeleton = HandSkeletonProvider[_handedness];
if (!_hidePrefabOnStart)
{
return;
}
foreach (Renderer r in gameObject.GetComponentsInChildren<Renderer>())
{
r.enabled = false;
}
}
protected override void UpdateData()
{
this.AssertField(HandSkeletonProvider, nameof(HandSkeletonProvider));
_handDataAsset.IsDataValid = true;
_handDataAsset.IsConnected = true;
_handDataAsset.IsTracked = true;
_handDataAsset.IsHighConfidence = true;
_handDataAsset.RootPoseOrigin = PoseOrigin.SyntheticPose;
_handDataAsset.Root = transform.GetPose();
_handDataAsset.HandScale = 1;
for (var i = 0; i < Constants.NUM_HAND_JOINTS; ++i)
{
Transform joint = _jointTransforms[i];
if (_jointTransforms[i] == null)
{
continue;
}
_handDataAsset.Joints[i] = joint.transform.localRotation;
}
}
public Transform GetTransformFor(HandJointId jointId)
{
return _jointTransforms[(int)jointId];
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 151f9be47337d9e4daef0b8725e30eab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,237 @@
/*
* 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
{
// A top level component that provides hand pose data, pinch states, and more.
// Rather than sourcing data directly from the runtime layer, provides one
// level of abstraction so that the aforementioned data can be injected
// from other sources.
public class Hand : DataModifier<HandDataAsset>, IHand
{
public Handedness Handedness => GetData().Config.Handedness;
public ITrackingToWorldTransformer TrackingToWorldTransformer =>
GetData().Config.TrackingToWorldTransformer;
public HandSkeleton HandSkeleton => GetData().Config.HandSkeleton;
private HandJointCache _jointPosesCache;
public event Action WhenHandUpdated = delegate { };
public bool IsConnected => GetData().IsDataValidAndConnected;
public bool IsHighConfidence => GetData().IsHighConfidence;
public bool IsDominantHand => GetData().IsDominantHand;
public float Scale => (TrackingToWorldTransformer != null
? TrackingToWorldTransformer.Transform.lossyScale.x
: 1) * GetData().HandScale;
private static readonly Vector3 PALM_LOCAL_OFFSET = new Vector3(0.08f, -0.01f, 0.0f);
protected override void Apply(HandDataAsset data)
{
// Default implementation does nothing, to allow instantiation of this modifier directly
}
public override void MarkInputDataRequiresUpdate()
{
base.MarkInputDataRequiresUpdate();
if (Started)
{
InitializeJointPosesCache();
WhenHandUpdated.Invoke();
}
}
private void InitializeJointPosesCache()
{
if (_jointPosesCache == null && GetData().IsDataValidAndConnected)
{
_jointPosesCache = new HandJointCache(HandSkeleton);
}
}
private void CheckJointPosesCacheUpdate()
{
if (_jointPosesCache != null
&& CurrentDataVersion != _jointPosesCache.LocalDataVersion)
{
_jointPosesCache.Update(GetData(), CurrentDataVersion);
}
}
#region IHandState implementation
public bool GetFingerIsPinching(HandFinger finger)
{
HandDataAsset currentData = GetData();
return currentData.IsConnected && currentData.IsFingerPinching[(int)finger];
}
public bool GetIndexFingerIsPinching()
{
return GetFingerIsPinching(HandFinger.Index);
}
public bool IsPointerPoseValid => IsPoseOriginAllowed(GetData().PointerPoseOrigin);
public bool GetPointerPose(out Pose pose)
{
HandDataAsset currentData = GetData();
return ValidatePose(currentData.PointerPose, currentData.PointerPoseOrigin,
out pose);
}
public bool GetJointPose(HandJointId handJointId, out Pose pose)
{
pose = Pose.identity;
if (!IsTrackedDataValid
|| _jointPosesCache == null
|| !GetRootPose(out Pose rootPose))
{
return false;
}
CheckJointPosesCacheUpdate();
pose = _jointPosesCache.WorldJointPose(handJointId, rootPose, Scale);
return true;
}
public bool GetJointPoseLocal(HandJointId handJointId, out Pose pose)
{
pose = Pose.identity;
if (!GetJointPosesLocal(out ReadOnlyHandJointPoses localJointPoses))
{
return false;
}
pose = localJointPoses[(int)handJointId];
return true;
}
public bool GetJointPosesLocal(out ReadOnlyHandJointPoses localJointPoses)
{
if (!IsTrackedDataValid || _jointPosesCache == null)
{
localJointPoses = ReadOnlyHandJointPoses.Empty;
return false;
}
CheckJointPosesCacheUpdate();
return _jointPosesCache.GetAllLocalPoses(out localJointPoses);
}
public bool GetJointPoseFromWrist(HandJointId handJointId, out Pose pose)
{
pose = Pose.identity;
if (!GetJointPosesFromWrist(out ReadOnlyHandJointPoses jointPosesFromWrist))
{
return false;
}
pose = jointPosesFromWrist[(int)handJointId];
return true;
}
public bool GetJointPosesFromWrist(out ReadOnlyHandJointPoses jointPosesFromWrist)
{
if (!IsTrackedDataValid || _jointPosesCache == null)
{
jointPosesFromWrist = ReadOnlyHandJointPoses.Empty;
return false;
}
CheckJointPosesCacheUpdate();
return _jointPosesCache.GetAllPosesFromWrist(out jointPosesFromWrist);
}
public bool GetPalmPoseLocal(out Pose pose)
{
Quaternion rotationQuat = Quaternion.identity;
Vector3 offset = PALM_LOCAL_OFFSET;
if (Handedness == Handedness.Left)
{
offset = -offset;
}
pose = new Pose(offset * Scale, rotationQuat);
return true;
}
public bool GetFingerIsHighConfidence(HandFinger finger)
{
return GetData().IsFingerHighConfidence[(int)finger];
}
public float GetFingerPinchStrength(HandFinger finger)
{
return GetData().FingerPinchStrength[(int)finger];
}
public bool IsTrackedDataValid => IsPoseOriginAllowed(GetData().RootPoseOrigin);
public bool GetRootPose(out Pose pose)
{
HandDataAsset currentData = GetData();
return ValidatePose(currentData.Root, currentData.RootPoseOrigin, out pose);
}
#endregion
private bool ValidatePose(in Pose sourcePose, PoseOrigin sourcePoseOrigin, out Pose pose)
{
if (IsPoseOriginDisallowed(sourcePoseOrigin))
{
pose = Pose.identity;
return false;
}
pose = TrackingToWorldTransformer != null
? TrackingToWorldTransformer.ToWorldPose(sourcePose)
: sourcePose;
return true;
}
private bool IsPoseOriginAllowed(PoseOrigin poseOrigin)
{
return poseOrigin != PoseOrigin.None;
}
private bool IsPoseOriginDisallowed(PoseOrigin poseOrigin)
{
return poseOrigin == PoseOrigin.None;
}
#region Inject
public void InjectAllHand(UpdateModeFlags updateMode, IDataSource updateAfter,
DataModifier<HandDataAsset> modifyDataFromSource, bool applyModifier)
{
base.InjectAllDataModifier(updateMode, updateAfter, modifyDataFromSource, applyModifier);
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a379f34d4f4f2e408d34f14bfb753ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,73 @@
/*
* 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
{
[Serializable]
public class HandDataAsset : ICopyFrom<HandDataAsset>
{
public bool IsDataValid;
public bool IsConnected;
public bool IsTracked;
public Pose Root;
public PoseOrigin RootPoseOrigin;
public Quaternion[] Joints = new Quaternion[Constants.NUM_HAND_JOINTS];
public bool IsHighConfidence;
public bool[] IsFingerPinching = new bool[Constants.NUM_FINGERS];
public bool[] IsFingerHighConfidence = new bool[Constants.NUM_FINGERS];
public float[] FingerPinchStrength = new float[Constants.NUM_FINGERS];
public float HandScale;
public Pose PointerPose;
public PoseOrigin PointerPoseOrigin;
public bool IsDominantHand;
public HandDataSourceConfig Config = new HandDataSourceConfig();
public bool IsDataValidAndConnected => IsDataValid && IsConnected;
public void CopyFrom(HandDataAsset source)
{
IsDataValid = source.IsDataValid;
IsConnected = source.IsConnected;
IsTracked = source.IsTracked;
IsHighConfidence = source.IsHighConfidence;
IsDominantHand = source.IsDominantHand;
Config = source.Config;
CopyPosesFrom(source);
}
public void CopyPosesFrom(HandDataAsset source)
{
Root = source.Root;
RootPoseOrigin = source.RootPoseOrigin;
Array.Copy(source.Joints, Joints, Constants.NUM_HAND_JOINTS);
Array.Copy(source.IsFingerPinching, IsFingerPinching, IsFingerPinching.Length);
Array.Copy(source.IsFingerHighConfidence, IsFingerHighConfidence,
IsFingerHighConfidence.Length);
Array.Copy(source.FingerPinchStrength, FingerPinchStrength, FingerPinchStrength.Length);
HandScale = source.HandScale;
PointerPose = source.PointerPose;
PointerPoseOrigin = source.PointerPoseOrigin;
Config = source.Config;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 24ccc8fce03318d4fb4fbd8bb782ded2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
/*
* 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.
*/
namespace Oculus.Interaction.Input
{
/// <summary>
/// A set of constants that are passed to each child of a Hand modifier tree from the root DataSource.
/// </summary>
public class HandDataSourceConfig
{
public Handedness Handedness { get; set; }
public ITrackingToWorldTransformer TrackingToWorldTransformer { get; set; }
public HandSkeleton HandSkeleton { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf1f3c380e869844ba59339cad4a325c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,153 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
public class HandJointCache
{
private Pose[] _localPoses = new Pose[Constants.NUM_HAND_JOINTS];
private Pose[] _posesFromWrist = new Pose[Constants.NUM_HAND_JOINTS];
private Pose[] _worldPoses = new Pose[Constants.NUM_HAND_JOINTS];
private ReadOnlyHandJointPoses _posesFromWristCollection;
private ReadOnlyHandJointPoses _localPosesCollection;
private IReadOnlyHandSkeletonJointList _originalJoints;
private int _dirtyWorldJoints = 0;
private int _dirtyWristJoints = 0;
public int LocalDataVersion { get; private set; } = -1;
public HandJointCache(IReadOnlyHandSkeleton handSkeleton)
{
LocalDataVersion = -1;
_posesFromWrist[0] = Pose.identity;
_posesFromWristCollection = new ReadOnlyHandJointPoses(_posesFromWrist);
_localPosesCollection = new ReadOnlyHandJointPoses(_localPoses);
_originalJoints = handSkeleton.Joints;
}
public void Update(HandDataAsset data, int dataVersion)
{
_dirtyWorldJoints = _dirtyWristJoints = (1 << (int)HandJointId.HandEnd) - 1; //set all dirty
if (!data.IsDataValidAndConnected)
{
return;
}
LocalDataVersion = dataVersion;
UpdateAllLocalPoses(data);
}
public bool GetAllLocalPoses(out ReadOnlyHandJointPoses localJointPoses)
{
localJointPoses = _localPosesCollection;
return _posesFromWristCollection.Count > 0;
}
public bool GetAllPosesFromWrist(out ReadOnlyHandJointPoses jointPosesFromWrist)
{
UpdateAllPosesFromWrist();
jointPosesFromWrist = _posesFromWristCollection;
return _posesFromWristCollection.Count > 0;
}
public Pose LocalJointPose(HandJointId jointid)
{
return _localPoses[(int)jointid];
}
public Pose PoseFromWrist(HandJointId jointid)
{
Pose pose = _posesFromWrist[(int)jointid];
UpdateWristJoint(jointid, ref pose);
return pose;
}
public Pose WorldJointPose(HandJointId jointid, Pose rootPose, float handScale)
{
int jointIndex = (int)jointid;
if ((_dirtyWorldJoints & (1 << jointIndex)) != 0) //its dirty
{
Pose wristPose = Pose.identity;
UpdateWristJoint(jointid, ref wristPose);
PoseUtils.Multiply(_localPoses[0], wristPose, ref _worldPoses[jointIndex]);
_worldPoses[jointIndex].position *= handScale;
_worldPoses[jointIndex].Postmultiply(rootPose);
_dirtyWorldJoints = _dirtyWorldJoints & ~(1 << jointIndex); //set clean
}
return _worldPoses[jointIndex];
}
private void UpdateAllLocalPoses(HandDataAsset data)
{
for (int i = 0; i < Constants.NUM_HAND_JOINTS; ++i)
{
HandSkeletonJoint originalJoint = _originalJoints[i];
_localPoses[i].position = originalJoint.pose.position;
_localPoses[i].rotation = data.Joints[i];
}
}
private void UpdateAllPosesFromWrist()
{
if (_dirtyWristJoints == 0) //its completely clean
{
return;
}
for (int jointIndex = 0; jointIndex < Constants.NUM_HAND_JOINTS; ++jointIndex)
{
if ((_dirtyWristJoints & (1 << jointIndex)) == 0) //its clean
{
continue;
}
HandSkeletonJoint originalJoint = _originalJoints[jointIndex];
if (originalJoint.parent >= 0)
{
PoseUtils.Multiply(_posesFromWrist[originalJoint.parent],
_localPoses[jointIndex], ref _posesFromWrist[jointIndex]);
}
}
_dirtyWristJoints = 0; //set all clean
}
private void UpdateWristJoint(HandJointId jointid, ref Pose pose)
{
int jointIndex = (int)jointid;
if ((_dirtyWristJoints & (1 << jointIndex)) != 0)// its dirty
{
if (jointid > HandJointId.HandWristRoot)
{
UpdateWristJoint((HandJointId)_originalJoints[jointIndex].parent, ref pose);
PoseUtils.Multiply(pose, _localPoses[jointIndex], ref _posesFromWrist[jointIndex]);
}
_dirtyWristJoints = _dirtyWristJoints & ~(1 << jointIndex); //set clean
}
pose.CopyFrom(_posesFromWrist[jointIndex]);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f06ae507a1989e04daeeedc9859327ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,340 @@
/*
* 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 System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Input
{
public class HandPhysicsCapsules : MonoBehaviour
{
[SerializeField, Interface(typeof(IHandVisual))]
private UnityEngine.Object _handVisual;
private IHandVisual HandVisual;
[SerializeField]
private JointsRadiusFeature _jointsRadiusFeature;
[Space]
[SerializeField]
[Tooltip("Capsules will be generated as triggers")]
private bool _asTriggers = false;
[SerializeField]
[Tooltip("Capsules will be generated in this Layer")]
private int _useLayer = 0;
[SerializeField]
[Tooltip("Capsules reaching this joint will not be generated")]
private HandFingerJointFlags _mask = HandFingerJointFlags.All;
private Action _whenCapsulesGenerated = delegate { };
public event Action WhenCapsulesGenerated
{
add
{
_whenCapsulesGenerated += value;
if (_capsulesGenerated)
{
value.Invoke();
}
}
remove
{
_whenCapsulesGenerated -= value;
}
}
private Transform _rootTransform;
public Transform RootTransform => _rootTransform;
private List<BoneCapsule> _capsules;
public IList<BoneCapsule> Capsules { get; private set; }
private Rigidbody[] _rigidbodies;
private bool _capsulesAreActive;
private bool _capsulesGenerated;
protected bool _started;
#region Editor events
protected virtual void Reset()
{
_useLayer = this.gameObject.layer;
this.TryGetComponent(out HandVisual);
}
#endregion
protected virtual void Awake()
{
HandVisual = _handVisual as IHandVisual;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
this.AssertField(HandVisual, nameof(HandVisual));
this.AssertField(_jointsRadiusFeature, nameof(_jointsRadiusFeature));
GenerateCapsules();
this.EndStart(ref _started);
}
private void GenerateCapsules()
{
_rigidbodies = new Rigidbody[(int)HandJointId.HandMaxSkinnable];
Transform _holder = new GameObject("Capsules").transform;
_holder.SetParent(transform, false);
_holder.localPosition = Vector3.zero;
_holder.localRotation = Quaternion.identity;
_holder.gameObject.layer = _useLayer;
int capsulesCount = Constants.NUM_HAND_JOINTS;
_capsules = new List<BoneCapsule>(capsulesCount);
Capsules = _capsules.AsReadOnly();
for (int i = (int)HandJointId.HandThumb0; i < (int)HandJointId.HandEnd; ++i)
{
HandJointId currentJoint = (HandJointId)i;
HandJointId parentJoint = HandJointUtils.JointParentList[i];
if (parentJoint == HandJointId.Invalid
|| ((1 << (int)currentJoint) & (int)_mask) == 0)
{
continue;
}
Vector3 boneEnd = HandVisual.GetJointPose(currentJoint, Space.World).position;
if (!TryGetJointRigidbody(parentJoint, out Rigidbody body))
{
Pose parentPose = HandVisual.GetJointPose(parentJoint, Space.World);
body = CreateJointRigidbody(parentJoint, _holder, parentPose);
}
string boneName = $"{parentJoint}-{currentJoint} CapsuleCollider";
float boneRadius = _jointsRadiusFeature.GetJointRadius(parentJoint);
float offset = currentJoint >= HandJointId.HandMaxSkinnable ? -boneRadius
: parentJoint == HandJointId.HandStart ? boneRadius
: 0f;
CapsuleCollider collider = CreateCollider(boneName,
body.transform, boneEnd, boneRadius, offset);
BoneCapsule capsule = new BoneCapsule(parentJoint, currentJoint, body, collider);
_capsules.Add(capsule);
}
IgnoreSelfCollisions();
_capsulesAreActive = false;
_capsulesGenerated = true;
_whenCapsulesGenerated.Invoke();
}
private void IgnoreSelfCollisions()
{
for (int i = 0; i < _capsules.Count; i++)
{
for (int j = i + 1; j < _capsules.Count; j++)
{
Physics.IgnoreCollision(_capsules[i].CapsuleCollider, _capsules[j].CapsuleCollider);
}
}
}
private bool TryGetJointRigidbody(HandJointId joint, out Rigidbody body)
{
body = _rigidbodies[(int)joint];
return body != null;
}
private Rigidbody CreateJointRigidbody(HandJointId joint,
Transform holder, Pose pose)
{
string name = $"{joint} Rigidbody";
Rigidbody rigidbody = new GameObject(name)
.AddComponent<Rigidbody>();
rigidbody.mass = 1.0f;
rigidbody.isKinematic = true;
rigidbody.useGravity = false;
rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
rigidbody.transform.SetParent(holder, false);
rigidbody.transform.SetPose(pose);
rigidbody.gameObject.SetActive(false);
rigidbody.gameObject.layer = _useLayer;
_rigidbodies[(int)joint] = rigidbody;
return rigidbody;
}
private CapsuleCollider CreateCollider(string name,
Transform from, Vector3 to, float radius, float offset)
{
CapsuleCollider collider = new GameObject(name)
.AddComponent<CapsuleCollider>();
collider.isTrigger = _asTriggers;
Vector3 boneDirection = to - from.position;
Quaternion boneRotation = Quaternion.LookRotation(boneDirection);
float boneLength = boneDirection.magnitude;
boneLength -= Mathf.Abs(offset);
collider.radius = radius;
collider.height = boneLength + radius * 2.0f;
collider.direction = 2;
collider.center = Vector3.forward * (boneLength * 0.5f + Mathf.Max(0f, offset));
Transform capsuleTransform = collider.transform;
capsuleTransform.SetParent(from, false);
capsuleTransform.SetPositionAndRotation(from.position, boneRotation);
collider.gameObject.layer = _useLayer;
return collider;
}
protected virtual void OnEnable()
{
if (_started)
{
HandVisual.WhenHandVisualUpdated += HandleHandVisualUpdated;
}
}
protected virtual void OnDisable()
{
if (_started)
{
HandVisual.WhenHandVisualUpdated -= HandleHandVisualUpdated;
EnableRigidbodies(false);
}
}
protected virtual void LateUpdate()
{
if (_capsulesAreActive && !HandVisual.IsVisible)
{
EnableRigidbodies(false);
}
}
private void EnableRigidbodies(bool enable)
{
if (_rigidbodies != null
|| enable == _capsulesAreActive)
{
return;
}
foreach (Rigidbody body in _rigidbodies)
{
body.gameObject.SetActive(enable);
}
_capsulesAreActive = enable;
}
private void HandleHandVisualUpdated()
{
_capsulesAreActive = HandVisual.IsVisible;
for (int i = 0; i < (int)HandJointId.HandMaxSkinnable; ++i)
{
Rigidbody jointbody = _rigidbodies[i];
if (jointbody == null)
{
continue;
}
GameObject jointGO = jointbody.gameObject;
if (_capsulesAreActive)
{
Pose bonePose = HandVisual.GetJointPose((HandJointId)i, Space.World);
bool justActivated = false;
if (!jointGO.activeSelf)
{
jointGO.SetActive(true);
justActivated = true;
}
if (_asTriggers)
{
jointbody.transform.SetPositionAndRotation(bonePose.position, bonePose.rotation);
}
else if (justActivated)
{
jointbody.position = bonePose.position;
jointbody.rotation = bonePose.rotation;
}
else
{
jointbody.MovePosition(bonePose.position);
jointbody.MoveRotation(bonePose.rotation);
}
}
else if (jointGO.activeSelf)
{
jointGO.SetActive(false);
}
}
}
#region Inject
public void InjectAllOVRHandPhysicsCapsules(IHandVisual handVisual,
bool asTriggers, int useLayer)
{
InjectHandVisual(handVisual);
InjectAsTriggers(asTriggers);
InjectUseLayer(useLayer);
}
public void InjectHandVisual(IHandVisual handVisual)
{
_handVisual = handVisual as UnityEngine.Object;
HandVisual = handVisual;
}
public void InjectAsTriggers(bool asTriggers)
{
_asTriggers = asTriggers;
}
public void InjectUseLayer(int useLayer)
{
_useLayer = useLayer;
}
#endregion
}
public class BoneCapsule
{
public HandJointId StartJoint { get; private set; }
public HandJointId EndJoint { get; private set; }
public Rigidbody CapsuleRigidbody { get; private set; }
public CapsuleCollider CapsuleCollider { get; private set; }
public BoneCapsule(HandJointId fromJoint, HandJointId toJoint, Rigidbody body, CapsuleCollider collider)
{
StartJoint = fromJoint;
EndJoint = toJoint;
CapsuleRigidbody = body;
CapsuleCollider = collider;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1e477b940c8a084ab4f2dda703b49ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,391 @@
/*
* 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 System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/// <summary>
/// Primitive type serialization
/// </summary>
namespace Oculus.Interaction.Input
{
public static class Constants
{
public const int NUM_HAND_JOINTS = (int)HandJointId.HandEnd;
public const int NUM_FINGERS = 5;
}
public enum Handedness
{
Left = 0,
Right = 1,
}
public enum HandFinger
{
Invalid = -1,
Thumb = 0,
Index = 1,
Middle = 2,
Ring = 3,
Pinky = 4,
Max = 4
}
[Flags]
public enum HandFingerFlags
{
None = 0,
Thumb = 1 << 0,
Index = 1 << 1,
Middle = 1 << 2,
Ring = 1 << 3,
Pinky = 1 << 4,
All = (1 << 5) - 1
}
public enum PinchGrabParam
{
PinchDistanceStart = 0,
PinchDistanceStopMax,
PinchDistanceStopOffset,
PinchHqDistanceStart,
PinchHqDistanceStopMax,
PinchHqDistanceStopOffset,
PinchHqViewAngleThreshold,
ThumbDistanceStart,
ThumbDistanceStopMax,
ThumbDistanceStopOffset,
ThumbMaxDot,
}
[Flags]
public enum HandFingerJointFlags
{
None = 0,
Wrist = 1 << HandJointId.HandWristRoot,
ForearmStub = 1 << HandJointId.HandForearmStub,
Thumb0 = 1 << HandJointId.HandThumb0,
Thumb1 = 1 << HandJointId.HandThumb1,
Thumb2 = 1 << HandJointId.HandThumb2,
Thumb3 = 1 << HandJointId.HandThumb3,
Index1 = 1 << HandJointId.HandIndex1,
Index2 = 1 << HandJointId.HandIndex2,
Index3 = 1 << HandJointId.HandIndex3,
Middle1 = 1 << HandJointId.HandMiddle1,
Middle2 = 1 << HandJointId.HandMiddle2,
Middle3 = 1 << HandJointId.HandMiddle3,
Ring1 = 1 << HandJointId.HandRing1,
Ring2 = 1 << HandJointId.HandRing2,
Ring3 = 1 << HandJointId.HandRing3,
Pinky0 = 1 << HandJointId.HandPinky0,
Pinky1 = 1 << HandJointId.HandPinky1,
Pinky2 = 1 << HandJointId.HandPinky2,
Pinky3 = 1 << HandJointId.HandPinky3,
ThumbTip = 1 << HandJointId.HandThumbTip,
IndexTip = 1 << HandJointId.HandIndexTip,
MiddleTip = 1 << HandJointId.HandMiddleTip,
RingTip = 1 << HandJointId.HandRingTip,
PinkyTip = 1 << HandJointId.HandPinkyTip,
All = (1 << HandJointId.HandEnd)-1
}
public static class HandFingerUtils
{
public static HandFingerFlags ToFlags(HandFinger handFinger)
{
return (HandFingerFlags)(1 << (int)handFinger);
}
}
public enum HandJointId
{
Invalid = -1,
// hand bones
HandStart = 0,
HandWristRoot = HandStart + 0, // root frame of the hand, where the wrist is located
HandForearmStub = HandStart + 1, // frame for user's forearm
HandThumb0 = HandStart + 2, // thumb trapezium bone
HandThumb1 = HandStart + 3, // thumb metacarpal bone
HandThumb2 = HandStart + 4, // thumb proximal phalange bone
HandThumb3 = HandStart + 5, // thumb distal phalange bone
HandIndex1 = HandStart + 6, // index proximal phalange bone
HandIndex2 = HandStart + 7, // index intermediate phalange bone
HandIndex3 = HandStart + 8, // index distal phalange bone
HandMiddle1 = HandStart + 9, // middle proximal phalange bone
HandMiddle2 = HandStart + 10, // middle intermediate phalange bone
HandMiddle3 = HandStart + 11, // middle distal phalange bone
HandRing1 = HandStart + 12, // ring proximal phalange bone
HandRing2 = HandStart + 13, // ring intermediate phalange bone
HandRing3 = HandStart + 14, // ring distal phalange bone
HandPinky0 = HandStart + 15, // pinky metacarpal bone
HandPinky1 = HandStart + 16, // pinky proximal phalange bone
HandPinky2 = HandStart + 17, // pinky intermediate phalange bone
HandPinky3 = HandStart + 18, // pinky distal phalange bone
HandMaxSkinnable = HandStart + 19,
// Bone tips are position only.
// They are not used for skinning but are useful for hit-testing.
// NOTE: HandThumbTip == HandMaxSkinnable since the extended tips need to be contiguous
HandThumbTip = HandMaxSkinnable + 0, // tip of the thumb
HandIndexTip = HandMaxSkinnable + 1, // tip of the index finger
HandMiddleTip = HandMaxSkinnable + 2, // tip of the middle finger
HandRingTip = HandMaxSkinnable + 3, // tip of the ring finger
HandPinkyTip = HandMaxSkinnable + 4, // tip of the pinky
HandEnd = HandMaxSkinnable + 5,
}
public class HandJointUtils
{
public static List<HandJointId[]> FingerToJointList = new List<HandJointId[]>()
{
new[] {HandJointId.HandThumb0,HandJointId.HandThumb1,HandJointId.HandThumb2,HandJointId.HandThumb3},
new[] {HandJointId.HandIndex1, HandJointId.HandIndex2, HandJointId.HandIndex3},
new[] {HandJointId.HandMiddle1, HandJointId.HandMiddle2, HandJointId.HandMiddle3},
new[] {HandJointId.HandRing1,HandJointId.HandRing2,HandJointId.HandRing3},
new[] {HandJointId.HandPinky0, HandJointId.HandPinky1, HandJointId.HandPinky2, HandJointId.HandPinky3}
};
public static HandJointId[] JointParentList = new[]
{
HandJointId.Invalid,
HandJointId.HandStart,
HandJointId.HandStart,
HandJointId.HandThumb0,
HandJointId.HandThumb1,
HandJointId.HandThumb2,
HandJointId.HandStart,
HandJointId.HandIndex1,
HandJointId.HandIndex2,
HandJointId.HandStart,
HandJointId.HandMiddle1,
HandJointId.HandMiddle2,
HandJointId.HandStart,
HandJointId.HandRing1,
HandJointId.HandRing2,
HandJointId.HandStart,
HandJointId.HandPinky0,
HandJointId.HandPinky1,
HandJointId.HandPinky2,
HandJointId.HandThumb3,
HandJointId.HandIndex3,
HandJointId.HandMiddle3,
HandJointId.HandRing3,
HandJointId.HandPinky3
};
public static HandJointId[][] JointChildrenList = new[]
{
new []
{
HandJointId.HandThumb0,
HandJointId.HandIndex1,
HandJointId.HandMiddle1,
HandJointId.HandRing1,
HandJointId.HandPinky0
},
new HandJointId[0],
new []{ HandJointId.HandThumb1 },
new []{ HandJointId.HandThumb2 },
new []{ HandJointId.HandThumb3 },
new []{ HandJointId.HandThumbTip },
new []{ HandJointId.HandIndex2 },
new []{ HandJointId.HandIndex3 },
new []{ HandJointId.HandIndexTip },
new []{ HandJointId.HandMiddle2 },
new []{ HandJointId.HandMiddle3 },
new []{ HandJointId.HandMiddleTip },
new []{ HandJointId.HandRing2 },
new []{ HandJointId.HandRing3 },
new []{ HandJointId.HandRingTip },
new []{ HandJointId.HandPinky1 },
new []{ HandJointId.HandPinky2 },
new []{ HandJointId.HandPinky3 },
new []{ HandJointId.HandPinkyTip },
new HandJointId[0],
new HandJointId[0],
new HandJointId[0],
new HandJointId[0],
new HandJointId[0]
};
public static List<HandJointId> JointIds = new List<HandJointId>()
{
HandJointId.HandIndex1,
HandJointId.HandIndex2,
HandJointId.HandIndex3,
HandJointId.HandMiddle1,
HandJointId.HandMiddle2,
HandJointId.HandMiddle3,
HandJointId.HandRing1,
HandJointId.HandRing2,
HandJointId.HandRing3,
HandJointId.HandPinky0,
HandJointId.HandPinky1,
HandJointId.HandPinky2,
HandJointId.HandPinky3,
HandJointId.HandThumb0,
HandJointId.HandThumb1,
HandJointId.HandThumb2,
HandJointId.HandThumb3
};
private static readonly HandJointId[] _handFingerProximals =
{
HandJointId.HandThumb2, HandJointId.HandIndex1, HandJointId.HandMiddle1,
HandJointId.HandRing1, HandJointId.HandPinky1
};
public static HandJointId GetHandFingerTip(HandFinger finger)
{
return HandJointId.HandMaxSkinnable + (int)finger;
}
/// <summary>
/// Returns the "proximal" JointId for the given finger.
/// This is commonly known as the Knuckle.
/// For fingers, proximal is the join with index 1; eg HandIndex1.
/// For thumb, proximal is the joint with index 2; eg HandThumb2.
/// </summary>
public static HandJointId GetHandFingerProximal(HandFinger finger)
{
return _handFingerProximals[(int)finger];
}
}
public struct HandSkeletonJoint
{
/// <summary>
/// Id of the parent joint in the skeleton hierarchy. Must always have a lower index than
/// this joint.
/// </summary>
public int parent;
/// <summary>
/// Stores the pose of the joint, in local space.
/// </summary>
public Pose pose;
/// <summary>
/// Radius of the bones starting at this joint
/// </summary>
public float radius;
}
public interface IReadOnlyHandSkeletonJointList
{
ref readonly HandSkeletonJoint this[int jointId] { get; }
}
public interface IReadOnlyHandSkeleton
{
IReadOnlyHandSkeletonJointList Joints { get; }
}
public interface ICopyFrom<in TSelfType>
{
void CopyFrom(TSelfType source);
}
public class ReadOnlyHandJointPoses : IReadOnlyList<Pose>
{
private Pose[] _poses;
public ReadOnlyHandJointPoses(Pose[] poses)
{
_poses = poses;
}
public IEnumerator<Pose> GetEnumerator()
{
foreach (var pose in _poses)
{
yield return pose;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public static ReadOnlyHandJointPoses Empty { get; } = new ReadOnlyHandJointPoses(Array.Empty<Pose>());
public int Count => _poses.Length;
public Pose this[int index] => _poses[index];
public ref readonly Pose this[HandJointId index] => ref _poses[(int)index];
}
public class HandSkeleton : IReadOnlyHandSkeleton, IReadOnlyHandSkeletonJointList
{
public HandSkeletonJoint[] joints = new HandSkeletonJoint[Constants.NUM_HAND_JOINTS];
public IReadOnlyHandSkeletonJointList Joints => this;
public ref readonly HandSkeletonJoint this[int jointId] => ref joints[jointId];
public static readonly HandSkeleton DefaultLeftSkeleton = new HandSkeleton()
{
joints = new HandSkeletonJoint[]
{
new HandSkeletonJoint(){parent = -1, pose = new Pose(new Vector3(0f,0f,0f), new Quaternion(0f,0f,0f,-1f))},
new HandSkeletonJoint(){parent = 0, pose = new Pose(new Vector3(0f,0f,0f), new Quaternion(0f,0f,0f,-1f))},
new HandSkeletonJoint(){parent = 0, pose = new Pose(new Vector3(-0.0200693f,0.0115541f,-0.01049652f), new Quaternion(-0.3753869f,0.4245841f,-0.007778856f,-0.8238644f))},
new HandSkeletonJoint(){parent = 2, pose = new Pose(new Vector3(-0.02485256f,-9.31E-10f,-1.863E-09f), new Quaternion(-0.2602303f,0.02433088f,0.125678f,-0.9570231f))},
new HandSkeletonJoint(){parent = 3, pose = new Pose(new Vector3(-0.03251291f,5.82E-10f,1.863E-09f), new Quaternion(0.08270377f,-0.0769617f,-0.08406223f,-0.9900357f))},
new HandSkeletonJoint(){parent = 4, pose = new Pose(new Vector3(-0.0337931f,3.26E-09f,1.863E-09f), new Quaternion(-0.08350593f,0.06501573f,-0.05827406f,-0.9926752f))},
new HandSkeletonJoint(){parent = 0, pose = new Pose(new Vector3(-0.09599624f,0.007316455f,-0.02355068f), new Quaternion(-0.03068309f,-0.01885559f,0.04328144f,-0.9984136f))},
new HandSkeletonJoint(){parent = 6, pose = new Pose(new Vector3(-0.0379273f,-5.82E-10f,-5.97E-10f), new Quaternion(0.02585241f,-0.007116061f,0.003292944f,-0.999635f))},
new HandSkeletonJoint(){parent = 7, pose = new Pose(new Vector3(-0.02430365f,-6.73E-10f,-6.75E-10f), new Quaternion(0.016056f,-0.02714872f,-0.072034f,-0.9969034f))},
new HandSkeletonJoint(){parent = 0, pose = new Pose(new Vector3(-0.09564661f,0.002543155f,-0.001725906f), new Quaternion(0.009066326f,-0.05146559f,0.05183575f,-0.9972874f))},
new HandSkeletonJoint(){parent = 9, pose = new Pose(new Vector3(-0.042927f,-8.51E-10f,-1.193E-09f), new Quaternion(0.01122823f,-0.004378874f,-0.001978267f,-0.9999254f))},
new HandSkeletonJoint(){parent = 10, pose = new Pose(new Vector3(-0.02754958f,3.09E-10f,1.128E-09f), new Quaternion(0.03431955f,-0.004611839f,-0.09300701f,-0.9950631f))},
new HandSkeletonJoint(){parent = 0, pose = new Pose(new Vector3(-0.0886938f,0.006529308f,0.01746524f), new Quaternion(0.05315936f,-0.1231034f,0.04981349f,-0.9897162f))},
new HandSkeletonJoint(){parent = 12, pose = new Pose(new Vector3(-0.0389961f,0f,5.24E-10f), new Quaternion(0.03363252f,-0.00278984f,0.00567602f,-0.9994143f))},
new HandSkeletonJoint(){parent = 13, pose = new Pose(new Vector3(-0.02657339f,1.281E-09f,1.63E-09f), new Quaternion(0.003477462f,0.02917945f,-0.02502854f,-0.9992548f))},
new HandSkeletonJoint(){parent = 0, pose = new Pose(new Vector3(-0.03407356f,0.009419836f,0.02299858f), new Quaternion(0.207036f,-0.1403428f,0.0183118f,-0.9680417f))},
new HandSkeletonJoint(){parent = 15, pose = new Pose(new Vector3(-0.04565055f,9.97679E-07f,-2.193963E-06f), new Quaternion(-0.09111304f,0.00407137f,0.02812923f,-0.9954349f))},
new HandSkeletonJoint(){parent = 16, pose = new Pose(new Vector3(-0.03072042f,1.048E-09f,-1.75E-10f), new Quaternion(0.03761665f,-0.04293772f,-0.01328605f,-0.9982809f))},
new HandSkeletonJoint(){parent = 17, pose = new Pose(new Vector3(-0.02031138f,-2.91E-10f,9.31E-10f), new Quaternion(-0.0006447434f,0.04917067f,-0.02401883f,-0.9985014f))},
new HandSkeletonJoint(){parent = 5, pose = new Pose(new Vector3(-0.02459077f,-0.001026974f,0.0006703701f), new Quaternion(0f,0f,0f,-1f))},
new HandSkeletonJoint(){parent = 8, pose = new Pose(new Vector3(-0.02236338f,-0.00102507f,0.0002956076f), new Quaternion(0f,0f,0f,-1f))},
new HandSkeletonJoint(){parent = 11, pose = new Pose(new Vector3(-0.02496492f,-0.001137299f,0.0003086528f), new Quaternion(0f,0f,0f,-1f))},
new HandSkeletonJoint(){parent = 14, pose = new Pose(new Vector3(-0.02432613f,-0.001608172f,0.000257905f), new Quaternion(0f,0f,0f,-1f))},
new HandSkeletonJoint(){parent = 18, pose = new Pose(new Vector3(-0.02192238f,-0.001216086f,-0.0002464796f), new Quaternion(0f,0f,0f,-1f)) }
}
};
public static readonly HandSkeleton DefaultRightSkeleton = new HandSkeleton()
{
joints = DefaultLeftSkeleton.joints.Select(joint => new HandSkeletonJoint()
{
parent = joint.parent,
pose = new Pose(-joint.pose.position, joint.pose.rotation)
}).ToArray()
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3140fee102ed8c2489e5442c16b1b36f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,144 @@
/*
* 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>
/// HandRef is a utility component that delegates all of its IHand implementation
/// to the provided Hand object.
/// </summary>
public class HandRef : MonoBehaviour, IHand, IActiveState
{
[SerializeField, Interface(typeof(IHand))]
private UnityEngine.Object _hand;
public IHand Hand { get; private set; }
public Handedness Handedness => Hand.Handedness;
public bool IsConnected => Hand.IsConnected;
public bool IsHighConfidence => Hand.IsHighConfidence;
public bool IsDominantHand => Hand.IsDominantHand;
public float Scale => Hand.Scale;
public bool IsPointerPoseValid => Hand.IsPointerPoseValid;
public bool IsTrackedDataValid => Hand.IsTrackedDataValid;
public int CurrentDataVersion => Hand.CurrentDataVersion;
public event Action WhenHandUpdated
{
add => Hand.WhenHandUpdated += value;
remove => Hand.WhenHandUpdated -= value;
}
public bool Active => IsConnected;
protected virtual void Awake()
{
Hand = _hand as IHand;
}
protected virtual void Start()
{
this.AssertField(Hand, nameof(Hand));
}
public bool GetFingerIsPinching(HandFinger finger)
{
return Hand.GetFingerIsPinching(finger);
}
public bool GetIndexFingerIsPinching()
{
return Hand.GetIndexFingerIsPinching();
}
public bool GetPointerPose(out Pose pose)
{
return Hand.GetPointerPose(out pose);
}
public bool GetJointPose(HandJointId handJointId, out Pose pose)
{
return Hand.GetJointPose(handJointId, out pose);
}
public bool GetJointPoseLocal(HandJointId handJointId, out Pose pose)
{
return Hand.GetJointPoseLocal(handJointId, out pose);
}
public bool GetJointPosesLocal(out ReadOnlyHandJointPoses jointPosesLocal)
{
return Hand.GetJointPosesLocal(out jointPosesLocal);
}
public bool GetJointPoseFromWrist(HandJointId handJointId, out Pose pose)
{
return Hand.GetJointPoseFromWrist(handJointId, out pose);
}
public bool GetJointPosesFromWrist(out ReadOnlyHandJointPoses jointPosesFromWrist)
{
return Hand.GetJointPosesFromWrist(out jointPosesFromWrist);
}
public bool GetPalmPoseLocal(out Pose pose)
{
return Hand.GetPalmPoseLocal(out pose);
}
public bool GetFingerIsHighConfidence(HandFinger finger)
{
return Hand.GetFingerIsHighConfidence(finger);
}
public float GetFingerPinchStrength(HandFinger finger)
{
return Hand.GetFingerPinchStrength(finger);
}
public bool GetRootPose(out Pose pose)
{
return Hand.GetRootPose(out pose);
}
#region Inject
public void InjectAllHandRef(IHand hand)
{
InjectHand(hand);
}
public void InjectHand(IHand hand)
{
_hand = hand as UnityEngine.Object;
Hand = hand;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b14164f8f23faae4293baeb84485b3d6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,136 @@
/*
* 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 UnityEngine;
using System;
namespace Oculus.Interaction.Input
{
public interface IHand
{
Handedness Handedness { get; }
bool IsConnected { get; }
/// <summary>
/// The hand is connected and tracked, and the root pose's tracking data is marked as
/// high confidence.
/// If this is true, then it implies that IsConnected and IsRootPoseValid are also true,
/// so they don't need to be checked in addition to this.
/// </summary>
bool IsHighConfidence { get; }
bool IsDominantHand { get; }
float Scale { get; }
bool GetFingerIsPinching(HandFinger finger);
bool GetIndexFingerIsPinching();
/// <summary>
/// Will return true if a pointer pose is available, that can be retrieved via
/// <see cref="GetPointerPose"/>
/// </summary>
bool IsPointerPoseValid { get; }
/// <summary>
/// Attempts to calculate the pose that can be used as a root for raycasting, in world space
/// Returns false if there is no valid tracking data.
/// </summary>
bool GetPointerPose(out Pose pose);
/// <summary>
/// Attempts to calculate the pose of the requested hand joint, in world space.
/// Returns false if the skeleton is not yet initialized, or there is no valid
/// tracking data.
/// </summary>
bool GetJointPose(HandJointId handJointId, out Pose pose);
/// <summary>
/// Attempts to calculate the pose of the requested hand joint, in local space.
/// Returns false if the skeleton is not yet initialized, or there is no valid
/// tracking data.
/// </summary>
bool GetJointPoseLocal(HandJointId handJointId, out Pose pose);
/// <summary>
/// Returns an array containing the local pose of each joint. The poses
/// do not have the root pose applied, nor the hand scale. It is in the same coordinate
/// system as the hand skeleton.
/// </summary>
/// <param name="localJointPoses">The array with the local joint poses.
/// It will be empty if no poses where found</param>
/// <returns>
/// True if the poses collection was correctly populated. False otherwise.
/// </returns>
bool GetJointPosesLocal(out ReadOnlyHandJointPoses localJointPoses);
/// <summary>
/// Attempts to calculate the pose of the requested hand joint relative to the wrist.
/// Returns false if the skeleton is not yet initialized, or there is no valid
/// tracking data.
/// </summary>
bool GetJointPoseFromWrist(HandJointId handJointId, out Pose pose);
/// <summary>
/// Returns an array containing the pose of each joint relative to the wrist. The poses
/// do not have the root pose applied, nor the hand scale. It is in the same coordinate
/// system as the hand skeleton.
/// </summary>
/// <param name="jointPosesFromWrist">The array with the joint poses from the wrist.
/// It will be empty if no poses where found</param>
/// <returns>
/// True if the poses collection was correctly populated. False otherwise.
/// </returns>
bool GetJointPosesFromWrist(out ReadOnlyHandJointPoses jointPosesFromWrist);
/// <summary>
/// Obtains palm pose in local space.
/// </summary>
/// <param name="pose">The pose to populate</param>
/// <returns>
/// True if pose was obtained.
/// </returns>
bool GetPalmPoseLocal(out Pose pose);
bool GetFingerIsHighConfidence(HandFinger finger);
float GetFingerPinchStrength(HandFinger finger);
/// <summary>
/// True if the hand is currently tracked, thus tracking poses are available for the hand
/// root and finger joints.
/// This property does not indicate pointing pose validity, which has its own property:
/// <see cref="IsPointerPoseValid"/>.
/// </summary>
bool IsTrackedDataValid { get; }
/// <summary>
/// Gets the root pose of the wrist, in world space.
/// Will return true if a pose was available; false otherwise.
/// Confidence level of the pose is exposed via <see cref="IsHighConfidence"/>.
/// </summary>
bool GetRootPose(out Pose pose);
/// <summary>
/// Incremented every time the source tracking or state data changes.
/// </summary>
int CurrentDataVersion { get; }
event Action WhenHandUpdated;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e500f060fe544c21b9b8892fb57511e1
timeCreated: 1630518667

View File

@ -0,0 +1,27 @@
/*
* 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.
*/
namespace Oculus.Interaction.Input
{
public interface IHandSkeletonProvider
{
HandSkeleton this[Handedness handedness] { get; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: be3974c2b13042ecb43558bf99c1603b
timeCreated: 1629410878

View File

@ -0,0 +1,39 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
public class JointsRadiusFeature : MonoBehaviour
{
[SerializeField]
private Hand _hand;
public float GetJointRadius(HandJointId id)
{
return _hand.GetData().Config.HandSkeleton[(int)id].radius;
}
#region Inject
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4dffc2a801520fb4cbe2c11894faf431
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,168 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
/// <summary>
/// A thin HandJoint skeleton implementation that can be used for computing
/// world joints from local joints data.
/// </summary>
public class ShadowHand
{
private Pose[] _localJointMap;
private Pose[] _worldJointMap;
private Pose _rootPose;
private float _rootScale;
private int _dirtyMap;
public ShadowHand()
{
_localJointMap = new Pose[(int)HandJointId.HandEnd];
_worldJointMap = new Pose[(int)HandJointId.HandEnd];
for (int i = 0; i < _localJointMap.Length; i++)
{
_localJointMap[i] = Pose.identity;
_worldJointMap[i] = Pose.identity;
}
_rootPose = Pose.identity;
_rootScale = 1f;
_dirtyMap = 0;
}
public Pose GetLocalPose(HandJointId handJointId)
{
return _localJointMap[(int)handJointId];
}
public void SetLocalPose(HandJointId jointId, Pose pose)
{
_localJointMap[(int)jointId] = pose;
MarkDirty(jointId);
}
public Pose GetWorldPose(HandJointId jointId)
{
UpdateDirty(jointId);
return _worldJointMap[(int)jointId];
}
public Pose GetRoot() => _rootPose;
public void SetRoot(Pose rootPose)
{
_rootPose = rootPose;
MarkDirty(HandJointId.HandStart);
}
public float GetRootScale() => _rootScale;
public void SetRootScale(float scale)
{
_rootScale = scale;
MarkDirty(HandJointId.HandStart);
}
private bool CheckDirtyBit(int i) => ((_dirtyMap >> i) & 1) == 1;
private void SetDirtyBit(int i) => _dirtyMap = _dirtyMap | (1 << i);
private void ClearDirtyBit(int i) => _dirtyMap = _dirtyMap & ~(1 << i);
private void MarkDirty(HandJointId jointId)
{
if (CheckDirtyBit((int)jointId))
{
return;
}
SetDirtyBit((int)jointId);
foreach (HandJointId child in HandJointUtils.JointChildrenList[(int)jointId])
{
MarkDirty(child);
}
}
private void UpdateDirty(HandJointId jointId)
{
if (!CheckDirtyBit((int)jointId))
{
return;
}
HandJointId parent = HandJointUtils.JointParentList[(int)jointId];
if (parent != HandJointId.Invalid)
{
UpdateDirty(parent);
}
ClearDirtyBit((int)jointId);
Pose parentWorldPose = (int)jointId == 0 ? _rootPose : GetWorldPose(parent);
Pose localPose = _localJointMap[(int)jointId];
_worldJointMap[(int)jointId] = new Pose(
parentWorldPose.position + parentWorldPose.rotation * localPose.position * _rootScale,
parentWorldPose.rotation * localPose.rotation
);
}
public void Copy(ShadowHand hand)
{
SetRoot(hand.GetRoot());
SetRootScale(hand.GetRootScale());
for (int i = 0; i < (int)HandJointId.HandEnd; i++)
{
HandJointId jointId = (HandJointId)i;
SetLocalPose(jointId, hand.GetLocalPose(jointId));
}
}
}
public static class ShadowHandExtensions
{
public static void FromHandRoot(this ShadowHand shadow, IHand hand)
{
hand.GetRootPose(out Pose root);
shadow.SetRoot(root);
shadow.SetRootScale(hand.Scale);
}
public static void FromHandFingers(this ShadowHand shadow, IHand hand, bool flipHandedness)
{
hand.GetJointPosesLocal(out ReadOnlyHandJointPoses localJointPoses);
if (localJointPoses.Count != (int)HandJointId.HandEnd)
{
return;
}
for (int i = 0; i < (int)HandJointId.HandEnd; i++)
{
Pose pose = localJointPoses[i];
pose.position *= flipHandedness ? -1f : 1f;
shadow.SetLocalPose((HandJointId)i, pose);
}
}
public static void FromHand(this ShadowHand shadow, IHand hand, bool flipHandedness = false)
{
FromHandRoot(shadow, hand);
FromHandFingers(shadow, hand, flipHandedness);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 938180d832e378e4591295df67622751
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
public interface IAxis1D
{
float Value();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cccec6d969d9bb14987f73c8ecd7c55f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
public interface IAxis2D
{
Vector2 Value();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67f59c05da2420f459585127dd9eeb33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
public interface IButton
{
bool Value();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6f0649520c797774c80446a44cd2ca2f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,43 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
public interface ITrackingToWorldTransformer
{
Transform Transform { get; }
/// <summary>
/// Converts a tracking space pose to a pose in in Unity's world coordinate space
/// (i.e. teleportation applied)
/// </summary>
Pose ToWorldPose(Pose poseRh);
/// <summary>
/// Converts a world space pose in Unity's coordinate space
/// to a pose in tracking space (i.e. no teleportation applied)
/// </summary>
Pose ToTrackingPose(in Pose worldPose);
Quaternion WorldToTrackingWristJointFixup { get; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf17380a7dbcc344dbdbdbc163933e4a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 315b3e47cee7df349bc10128cf62dc2e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,96 @@
/*
* 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 UnityEngine;
using Oculus.Interaction.Input;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Utils
{
public class FilteredTransform : MonoBehaviour
{
[SerializeField]
private Transform _sourceTransform;
[SerializeField]
private bool _filterPosition;
[SerializeField]
private OneEuroFilterPropertyBlock _positionFilterProperties =
new OneEuroFilterPropertyBlock(2f, 3f);
[SerializeField]
private bool _filterRotation;
[SerializeField]
private OneEuroFilterPropertyBlock _rotationFilterProperties =
new OneEuroFilterPropertyBlock(2f, 3f);
private IOneEuroFilter<Vector3> _positionFilter;
private IOneEuroFilter<Quaternion> _rotationFilter;
protected virtual void Start()
{
this.AssertField(_sourceTransform, nameof(_sourceTransform));
_positionFilter = OneEuroFilter.CreateVector3();
_rotationFilter = OneEuroFilter.CreateQuaternion();
}
protected virtual void Update()
{
if (_filterPosition)
{
Vector3 position = _sourceTransform.position;
_positionFilter.SetProperties(_positionFilterProperties);
transform.position =
_positionFilter.Step(_sourceTransform.position, Time.deltaTime);
}
else
{
transform.position = _sourceTransform.position;
}
if (_filterRotation)
{
_rotationFilter.SetProperties(_rotationFilterProperties);
transform.rotation =
_rotationFilter.Step(_sourceTransform.rotation, Time.deltaTime);
}
else
{
transform.rotation = _sourceTransform.rotation;
}
}
#region Inject
public void InjectAllFilteredTransform(Transform sourceTransform)
{
InjectSourceTransform(sourceTransform);
}
public void InjectSourceTransform(Transform sourceTransform)
{
_sourceTransform = sourceTransform;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a174518816f7e645860e70cf978ba1c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
/*
* 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.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Oculus.Interaction.Input
{
[CreateAssetMenu(menuName = "Oculus/Interaction/SDK/Input/Hand Filter Parameters")]
[System.Serializable]
public class HandFilterParameterBlock : ScriptableObject
{
[Header("One Euro Filter Parameters")]
[SerializeField]
[Tooltip("Smoothing for wrist position")]
public OneEuroFilterPropertyBlock wristPositionParameters = new OneEuroFilterPropertyBlock { _beta = 5.0f, _minCutoff = 0.5f, _dCutoff = 1.0f };
[SerializeField]
[Tooltip("Smoothing for wrist rotation")]
public OneEuroFilterPropertyBlock wristRotationParameters = new OneEuroFilterPropertyBlock { _beta = 5.0f, _minCutoff = 0.5f, _dCutoff = 1.0f };
[SerializeField]
[Tooltip("Smoothing for finger rotation")]
public OneEuroFilterPropertyBlock fingerRotationParameters = new OneEuroFilterPropertyBlock { _beta = 1.0f, _minCutoff = 0.5f, _dCutoff = 1.0f };
[SerializeField]
[Tooltip("Frequency (in frames per sec)")]
public float frequency = 72.0f;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8cb1d32171371c744947b9c3f1911fb8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@
/*
* 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.
*/
namespace Oculus.Interaction.Input
{
public interface IOneEuroFilter<TData>
{
/// <summary>
/// The last value returned by <see cref="Step(TData, float)"/>
/// </summary>
TData Value { get; }
/// <summary>
/// Update the parameters of the filter
/// </summary>
/// <param name="propertyBlock">The property block containing the parameters to se</param>
void SetProperties(in OneEuroFilterPropertyBlock properties);
/// <summary>
/// Update the filter with a new noisy value to be smoothed.
/// This is a destructive operation that should be run once per frame, as
/// calling this updates the previous frame data.
/// </summary>
/// <param name="rawValue">The noisy value to be filtered</param>
/// <param name="deltaTime">The time between steps, use to derive filter frequency.
/// Omitting this value will fallback to <see cref="OneEuroFilter._DEFAULT_FREQUENCY_HZ"/></param>
/// <returns>The filtered value, equivalent to <see cref="Value"/></returns>
TData Step(TData rawValue, float deltaTime = 1f / OneEuroFilter._DEFAULT_FREQUENCY_HZ);
/// <summary>
/// Clear previous values and reset the filter
/// </summary>
void Reset();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 00c937dac239f0644ae25e3b81b10753
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,122 @@
/*
* 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 UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Input
{
/// <summary>
/// The One Euro Filter, a speed-based lowpass filter with adaptive cutoff.
/// Based on https://hal.inria.fr/hal-00670496/document
/// </summary>
public partial class OneEuroFilter : IOneEuroFilter<float>
{
public const float _DEFAULT_FREQUENCY_HZ = 60f;
public float Value { get; private set; }
private OneEuroFilterPropertyBlock _properties;
private bool _isFirstUpdate;
private LowPassFilter _xfilt;
private LowPassFilter _dxfilt;
private OneEuroFilter()
{
_xfilt = new LowPassFilter();
_dxfilt = new LowPassFilter();
_isFirstUpdate = true;
SetProperties(OneEuroFilterPropertyBlock.Default);
}
public void SetProperties(in OneEuroFilterPropertyBlock properties)
{
Assert.IsTrue(properties.MinCutoff >= 0, $"{nameof(properties.MinCutoff)} must be >= 0");
Assert.IsTrue(properties.DCutoff >= 0, $"{nameof(properties.DCutoff)} must be >= 0");
Assert.IsTrue(properties.Beta >= 0, $"{nameof(properties.Beta)} must be >= 0");
_properties = properties;
}
public float Step(float newValue, float deltaTime)
{
if (deltaTime > 0)
{
float freqHz = 1f / deltaTime;
float dx = _isFirstUpdate ? 0 : (newValue - _xfilt.PrevValue) * freqHz;
_isFirstUpdate = false;
float edx = _dxfilt.Filter(dx, GetAlpha(freqHz, _properties.DCutoff));
float cutoff = _properties.MinCutoff + _properties.Beta * Mathf.Abs(edx);
Value = _xfilt.Filter(newValue, GetAlpha(freqHz, cutoff));
}
return Value;
}
public void Reset()
{
Value = default;
_xfilt.Reset();
_dxfilt.Reset();
_isFirstUpdate = true;
}
private float GetAlpha(float rate, float cutoff)
{
float tau = 1f / (2.0f * Mathf.PI * cutoff);
float te = 1f / rate;
return 1f / (1f + tau / te);
}
private class LowPassFilter
{
public float PrevValue => _hatxprev;
private bool _isFirstUpdate;
private float _hatx;
private float _hatxprev;
public LowPassFilter()
{
_isFirstUpdate = true;
}
public void Reset()
{
_isFirstUpdate = true;
_hatx = _hatxprev = default;
}
public float Filter(float x, float alpha)
{
if (_isFirstUpdate)
{
_isFirstUpdate = false;
_hatxprev = x;
}
_hatx = alpha * x + (1 - alpha) * _hatxprev;
_hatxprev = _hatx;
return _hatx;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3d7c849d35713b40af7b25d745369ed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,60 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Input
{
public partial class OneEuroFilter
{
public static IOneEuroFilter<float> CreateFloat()
{
return new OneEuroFilter();
}
public static IOneEuroFilter<Vector2> CreateVector2()
{
return new OneEuroFilterMulti<Vector2>(2,
(values) => new Vector2(values[0], values[1]),
(value, index) => value[index]);
}
public static IOneEuroFilter<Vector3> CreateVector3()
{
return new OneEuroFilterMulti<Vector3>(3,
(values) => new Vector3(values[0], values[1], values[2]),
(value, index) => value[index]);
}
public static IOneEuroFilter<Vector4> CreateVector4()
{
return new OneEuroFilterMulti<Vector4>(4,
(values) => new Vector4(values[0], values[1], values[2], values[3]),
(value, index) => value[index]);
}
public static IOneEuroFilter<Quaternion> CreateQuaternion()
{
return new OneEuroFilterMulti<Quaternion>(4,
(values) => new Quaternion(values[0], values[1], values[2], values[3]).normalized,
(value, index) => value[index]);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3cc371463610efd41a2e0dc11a8178c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,91 @@
/*
* 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.Assertions;
namespace Oculus.Interaction.Input
{
public partial class OneEuroFilter
{
/// <summary>
/// Implementation of <see cref="IOneEuroFilter{TData}"/> that acts on
/// data types with multiple <see cref="float"/> components, such as
/// <see cref="UnityEngine.Vector3"/>
/// </summary>
/// <typeparam name="TData">The multi-component datatype to filter</typeparam>
private class OneEuroFilterMulti<TData> : IOneEuroFilter<TData>
{
public TData Value { get; private set; }
private readonly Func<float[], TData> _arrayToType;
private readonly Func<TData, int, float> _getValAtIndex;
private readonly IOneEuroFilter<float>[] _filters;
private readonly float[] _componentValues;
public OneEuroFilterMulti(int numComponents,
Func<float[], TData> arrayToType,
Func<TData, int, float> getValAtIndex)
{
Assert.IsNotNull(arrayToType);
Assert.IsNotNull(getValAtIndex);
Assert.IsTrue(numComponents > 0);
_filters = new OneEuroFilter[numComponents];
_componentValues = new float[numComponents];
_arrayToType = arrayToType;
_getValAtIndex = getValAtIndex;
for (int i = 0; i < _filters.Length; ++i)
{
_filters[i] = new OneEuroFilter();
}
}
public void SetProperties(in OneEuroFilterPropertyBlock properties)
{
foreach (var filter in _filters)
{
filter.SetProperties(properties);
}
}
public TData Step(TData newValue, float deltaTime)
{
for (int i = 0; i < _filters.Length; ++i)
{
float componentValue = _getValAtIndex(newValue, i);
_componentValues[i] = _filters[i].Step(componentValue, deltaTime);
}
Value = _arrayToType(_componentValues);
return Value;
}
public void Reset()
{
foreach (var filter in _filters)
{
filter.Reset();
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 162ae485931cbb8418cc08cbfe83931e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,81 @@
/*
* 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>
/// Property block for OneEuroFilter parameters
/// </summary>
[Serializable]
public struct OneEuroFilterPropertyBlock
{
[SerializeField]
[Tooltip("Decrease min cutoff until jitter is eliminated")]
public float _minCutoff;
[SerializeField]
[Tooltip("Increase beta from zero to reduce lag")]
public float _beta;
[SerializeField]
[Tooltip("Smaller values of dCutoff smooth more but slow accuracy")]
public float _dCutoff;
/// <summary>
/// The minimum cutoff frequency of the filter, in Hertz
/// </summary>
public float MinCutoff => _minCutoff;
/// <summary>
/// Filter cutoff slope
/// </summary>
public float Beta => _beta;
/// <summary>
/// Cutoff frequency for derivative, in Hertz
/// </summary>
public float DCutoff => _dCutoff;
static private float DefaultMinCutoff => 1;
static private float DefaultBeta => 0;
static private float DefaultDCutoff => 1;
public OneEuroFilterPropertyBlock(float minCutoff, float beta, float dCutoff)
{
_minCutoff = minCutoff;
_beta = beta;
_dCutoff = dCutoff;
}
public OneEuroFilterPropertyBlock(float minCutoff, float beta)
{
_minCutoff = minCutoff;
_beta = beta;
_dCutoff = DefaultDCutoff;
}
public static OneEuroFilterPropertyBlock Default =>
new OneEuroFilterPropertyBlock() { _minCutoff = DefaultMinCutoff, _beta = DefaultBeta, _dCutoff = DefaultDCutoff };
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3e7ef3e1aa979ab42a3583acb36d3a77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,54 @@
/*
* 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>
/// Describes where the pose data originated. Can be used to determine how much pre-processing
/// has been applied by modifiers. This can be useful in determining how to render the hands.
/// </summary>
public enum PoseOrigin
{
/// <summary>
/// Pose is invalid and has no meaning.
/// </summary>
None,
/// <summary>
/// Pose matches this frames tracking data; no filtering or modification has occured.
/// </summary>
RawTrackedPose,
/// <summary>
/// Pose originated from this frames tracking data but has had additional filtering or
/// modification applied by an IInputDataModifier
/// </summary>
FilteredTrackedPose,
/// <summary>
/// Pose is valid but was not derived from this frames tracking data. Examples include
/// last-known-good data when tracking is lost, or puppet-hands for tutorials.
/// </summary>
SyntheticPose,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3dfaf230e6b59cf45ae73e7cb6b9f817
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: