Initialer Upload neues Unity-Projekt
This commit is contained in:
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 462d07f0e530df04491907e3d9383e25
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a05b4d817add3f44c996755c74db002b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96dc2ac9879a2ae409623a72b39f8340
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ae60cfa43388e449a7518f36418f81c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad374a43a946e7d4496818c6b194809f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe4ca944e80140841985b0f273793641
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1675de914d203a4a910d701ade05ce3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8489868b0a7582e43890f869877224e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3673df66324d8f34d9433049c54de631
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77db11dffd48abd4cb96a6076a2a2545
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b392af6b92984f2887f80975b07141ba
|
||||
timeCreated: 1629344165
|
||||
115
Assets/Oculus/Interaction/Runtime/Scripts/Input/DataModifier.cs
Normal file
115
Assets/Oculus/Interaction/Runtime/Scripts/Input/DataModifier.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12efd6a9760b4bcaafd9cefbe1db0bad
|
||||
timeCreated: 1630526111
|
||||
204
Assets/Oculus/Interaction/Runtime/Scripts/Input/DataSource.cs
Normal file
204
Assets/Oculus/Interaction/Runtime/Scripts/Input/DataSource.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e3eaa7f7e074674929b88928616f063
|
||||
timeCreated: 1630527498
|
||||
8
Assets/Oculus/Interaction/Runtime/Scripts/Input/HMD.meta
Normal file
8
Assets/Oculus/Interaction/Runtime/Scripts/Input/HMD.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b30e59c0241c28d46b6f42afca0d444d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
63
Assets/Oculus/Interaction/Runtime/Scripts/Input/HMD/Hmd.cs
Normal file
63
Assets/Oculus/Interaction/Runtime/Scripts/Input/HMD/Hmd.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2606bf2f0c914a7aba4390f29ba2eb6e
|
||||
timeCreated: 1629334585
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 332eb9fb3feb493aa2632f2739bf3d41
|
||||
timeCreated: 1629321466
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e6945eb8bd2e9941a48a55b14cda4d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 484167e684014224c9a8bff92b293c6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
31
Assets/Oculus/Interaction/Runtime/Scripts/Input/HMD/IHmd.cs
Normal file
31
Assets/Oculus/Interaction/Runtime/Scripts/Input/HMD/IHmd.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d92925b93eb01cf4ba528518efe2705a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c02ebbf428f995b45a218658ebd541d6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b1422466b455b248ad5e0e27e1e043e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b927808915b4eba41b76168390b99c24
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fcebab82b3c5dd4fac023079b7fed29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b5d61cebc8bc2644b45d6034694ea8f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7daff26e66096cd49a253ca3ab592c03
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c67033f580359c4581dff1ccffcca91
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c86919be0f18a54b8706542dc38dede
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 151f9be47337d9e4daef0b8725e30eab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
237
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/Hand.cs
Normal file
237
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/Hand.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a379f34d4f4f2e408d34f14bfb753ce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24ccc8fce03318d4fb4fbd8bb782ded2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf1f3c380e869844ba59339cad4a325c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f06ae507a1989e04daeeedc9859327ff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1e477b940c8a084ab4f2dda703b49ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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()
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3140fee102ed8c2489e5442c16b1b36f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
144
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/HandRef.cs
Normal file
144
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/HandRef.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b14164f8f23faae4293baeb84485b3d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
136
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/IHand.cs
Normal file
136
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/IHand.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e500f060fe544c21b9b8892fb57511e1
|
||||
timeCreated: 1630518667
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be3974c2b13042ecb43558bf99c1603b
|
||||
timeCreated: 1629410878
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4dffc2a801520fb4cbe2c11894faf431
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 938180d832e378e4591295df67622751
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Assets/Oculus/Interaction/Runtime/Scripts/Input/IAxis1D.cs
Normal file
29
Assets/Oculus/Interaction/Runtime/Scripts/Input/IAxis1D.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cccec6d969d9bb14987f73c8ecd7c55f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Assets/Oculus/Interaction/Runtime/Scripts/Input/IAxis2D.cs
Normal file
29
Assets/Oculus/Interaction/Runtime/Scripts/Input/IAxis2D.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67f59c05da2420f459585127dd9eeb33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Assets/Oculus/Interaction/Runtime/Scripts/Input/IButton.cs
Normal file
29
Assets/Oculus/Interaction/Runtime/Scripts/Input/IButton.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f0649520c797774c80446a44cd2ca2f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf17380a7dbcc344dbdbdbc163933e4a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 315b3e47cee7df349bc10128cf62dc2e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a174518816f7e645860e70cf978ba1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cb1d32171371c744947b9c3f1911fb8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00c937dac239f0644ae25e3b81b10753
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3d7c849d35713b40af7b25d745369ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cc371463610efd41a2e0dc11a8178c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 162ae485931cbb8418cc08cbfe83931e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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 };
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e7ef3e1aa979ab42a3583acb36d3a77
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3dfaf230e6b59cf45ae73e7cb6b9f817
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user