clean project
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96dc2ac9879a2ae409623a72b39f8340
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,119 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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, ControllerDataSourceConfig>,
|
||||
IController
|
||||
{
|
||||
public Handedness Handedness => 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 ControllerUpdated = delegate { };
|
||||
|
||||
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 = 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 = Config.TrackingToWorldTransformer.ToWorldPose(GetData().PointerPose);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void MarkInputDataRequiresUpdate()
|
||||
{
|
||||
base.MarkInputDataRequiresUpdate();
|
||||
|
||||
if (Started)
|
||||
{
|
||||
ControllerUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Apply(ControllerDataAsset data)
|
||||
{
|
||||
// Default implementation does nothing, to allow instantiation of this modifier directly
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ae60cfa43388e449a7518f36418f81c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 void CopyFrom(ControllerDataAsset source)
|
||||
{
|
||||
IsDataValid = source.IsDataValid;
|
||||
IsConnected = source.IsConnected;
|
||||
IsTracked = source.IsTracked;
|
||||
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,24 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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; }
|
||||
public IDataSource<HmdDataAsset, HmdDataSourceConfig> HmdData { 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,33 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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,86 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 MonoBehaviour _controller;
|
||||
private IController Controller;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Controller = _controller as IController;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
Assert.IsNotNull(Controller);
|
||||
}
|
||||
|
||||
public Handedness Handedness => Controller.Handedness;
|
||||
|
||||
public bool IsConnected => Controller.IsConnected;
|
||||
|
||||
public bool IsPoseValid => Controller.IsPoseValid;
|
||||
|
||||
public event Action ControllerUpdated
|
||||
{
|
||||
add => Controller.ControllerUpdated += value;
|
||||
remove => Controller.ControllerUpdated -= 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 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 MonoBehaviour;
|
||||
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,29 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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);
|
||||
bool IsButtonUsageAnyActive(ControllerButtonUsage buttonUsage);
|
||||
bool IsButtonUsageAllActive(ControllerButtonUsage buttonUsage);
|
||||
event Action ControllerUpdated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77db11dffd48abd4cb96a6076a2a2545
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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
|
||||
118
Assets/Oculus/Interaction/Runtime/Scripts/Input/DataModifier.cs
Normal file
118
Assets/Oculus/Interaction/Runtime/Scripts/Input/DataModifier.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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
|
||||
{
|
||||
public abstract class
|
||||
DataModifier<TData, TConfig> : DataSource<TData, TConfig>
|
||||
where TData : class, ICopyFrom<TData>, new()
|
||||
{
|
||||
[Header("Data Modifier")]
|
||||
[SerializeField, Interface(nameof(_modifyDataFromSource))]
|
||||
protected MonoBehaviour _iModifyDataFromSourceMono;
|
||||
private IDataSource<TData, TConfig> _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;
|
||||
private TConfig _configCache;
|
||||
|
||||
protected override TData DataAsset => _currentDataAsset;
|
||||
|
||||
public virtual IDataSource<TData, TConfig> ModifyDataFromSource => _modifyDataFromSource == null
|
||||
? (_modifyDataFromSource = _iModifyDataFromSourceMono as IDataSource<TData, TConfig>)
|
||||
: _modifyDataFromSource;
|
||||
|
||||
public override int CurrentDataVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return _applyModifier
|
||||
? base.CurrentDataVersion
|
||||
: ModifyDataFromSource.CurrentDataVersion;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetSources(IDataSource<TData, TConfig> modifyDataFromSource, IDataSource updateAfter, UpdateModeFlags updateMode)
|
||||
{
|
||||
ResetUpdateAfter(updateAfter, updateMode);
|
||||
_modifyDataFromSource = modifyDataFromSource;
|
||||
_currentDataAsset = InvalidAsset;
|
||||
_configCache = default;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
base.Start();
|
||||
Assert.IsNotNull(ModifyDataFromSource);
|
||||
}
|
||||
|
||||
public override TConfig Config
|
||||
{
|
||||
get
|
||||
{
|
||||
return _configCache != null
|
||||
? _configCache
|
||||
: (_configCache = ModifyDataFromSource.Config);
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllDataModifier(UpdateModeFlags updateMode, IDataSource updateAfter, IDataSource<TData, TConfig> modifyDataFromSource, bool applyModifier)
|
||||
{
|
||||
base.InjectAllDataSource(updateMode, updateAfter);
|
||||
InjectModifyDataFromSource(modifyDataFromSource);
|
||||
InjectApplyModifier(applyModifier);
|
||||
}
|
||||
|
||||
public void InjectModifyDataFromSource(IDataSource<TData, TConfig> modifyDataFromSource)
|
||||
{
|
||||
_modifyDataFromSource = modifyDataFromSource;
|
||||
}
|
||||
|
||||
public void InjectApplyModifier(bool applyModifier)
|
||||
{
|
||||
_applyModifier = applyModifier;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12efd6a9760b4bcaafd9cefbe1db0bad
|
||||
timeCreated: 1630526111
|
||||
202
Assets/Oculus/Interaction/Runtime/Scripts/Input/DataSource.cs
Normal file
202
Assets/Oculus/Interaction/Runtime/Scripts/Input/DataSource.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
public interface IDataSource
|
||||
{
|
||||
int CurrentDataVersion { get; }
|
||||
void MarkInputDataRequiresUpdate();
|
||||
event Action InputDataAvailable;
|
||||
}
|
||||
|
||||
public interface IDataSource<TData, TConfig> : IDataSource
|
||||
{
|
||||
TData GetData();
|
||||
TConfig Config { get; }
|
||||
}
|
||||
|
||||
public abstract class DataSource<TData, TConfig> : MonoBehaviour, IDataSource<TData, TConfig>
|
||||
where TData : class, ICopyFrom<TData>, new()
|
||||
{
|
||||
public bool Started { get; private set; }
|
||||
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]
|
||||
private MonoBehaviour _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;
|
||||
Assert.IsNotNull(UpdateAfter);
|
||||
}
|
||||
Started = true;
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
if (Started && 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 Started && _requiresUpdate && isActiveAndEnabled;
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
|
||||
public abstract TConfig Config { 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 MonoBehaviour;
|
||||
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:
|
||||
53
Assets/Oculus/Interaction/Runtime/Scripts/Input/HMD/Hmd.cs
Normal file
53
Assets/Oculus/Interaction/Runtime/Scripts/Input/HMD/Hmd.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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, HmdDataSourceConfig>, IHmd
|
||||
{
|
||||
public ITrackingToWorldTransformer TrackingToWorldTransformer =>
|
||||
Config.TrackingToWorldTransformer;
|
||||
|
||||
public event Action HmdUpdated = 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)
|
||||
{
|
||||
HmdUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetRootPose(out Pose pose)
|
||||
{
|
||||
var currentData = GetData();
|
||||
|
||||
if (!currentData.IsTracked)
|
||||
{
|
||||
pose = Pose.identity;
|
||||
return false;
|
||||
}
|
||||
pose = TrackingToWorldTransformer.ToWorldPose(currentData.Root);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2606bf2f0c914a7aba4390f29ba2eb6e
|
||||
timeCreated: 1629334585
|
||||
@@ -0,0 +1,32 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 void CopyFrom(HmdDataAsset source)
|
||||
{
|
||||
Root = source.Root;
|
||||
IsTracked = source.IsTracked;
|
||||
FrameId = source.FrameId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 332eb9fb3feb493aa2632f2739bf3d41
|
||||
timeCreated: 1629321466
|
||||
@@ -0,0 +1,22 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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,62 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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(Hmd))]
|
||||
private MonoBehaviour _hmd;
|
||||
private IHmd Hmd;
|
||||
|
||||
public event Action HmdUpdated
|
||||
{
|
||||
add => Hmd.HmdUpdated += value;
|
||||
remove => Hmd.HmdUpdated -= value;
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Hmd = _hmd as IHmd;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
Assert.IsNotNull(Hmd);
|
||||
}
|
||||
|
||||
public bool GetRootPose(out Pose pose)
|
||||
{
|
||||
return Hmd.GetRootPose(out pose);
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllHmdRef(IHmd hmd)
|
||||
{
|
||||
InjectHmd(hmd);
|
||||
}
|
||||
|
||||
public void InjectHmd(IHmd hmd)
|
||||
{
|
||||
_hmd = hmd as MonoBehaviour;
|
||||
Hmd = hmd;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 484167e684014224c9a8bff92b293c6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
23
Assets/Oculus/Interaction/Runtime/Scripts/Input/HMD/IHmd.cs
Normal file
23
Assets/Oculus/Interaction/Runtime/Scripts/Input/HMD/IHmd.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 GetRootPose(out Pose pose);
|
||||
event Action HmdUpdated;
|
||||
}
|
||||
}
|
||||
@@ -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,39 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 DummyDataModifier : Hand
|
||||
{
|
||||
public Vector3 offset;
|
||||
public float animationTime;
|
||||
|
||||
#region IHandInputDataModifier Implementation
|
||||
protected override void Apply(HandDataAsset handDataAsset)
|
||||
{
|
||||
if (!handDataAsset.IsTracked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var interpolant = Mathf.Sin(Mathf.PI * 2.0f * (Time.time % animationTime) / animationTime) * 0.5f + 0.5f;
|
||||
handDataAsset.Root.position = handDataAsset.Root.position + interpolant * offset + offset * -0.5f;
|
||||
|
||||
ref var joint = ref handDataAsset.Joints[(int)HandJointId.HandIndex1];
|
||||
var rot = Quaternion.AngleAxis(interpolant * 90 - 45, Vector3.forward);
|
||||
joint = joint * rot;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d9aa5fe07a77244d89c6c80b08b33ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 FixedScaleDataModifier : Hand
|
||||
{
|
||||
[SerializeField]
|
||||
private float _scale = 1f;
|
||||
|
||||
#region DataModifier Implementation
|
||||
protected override void Apply(HandDataAsset data)
|
||||
{
|
||||
Pose rootToPointer = PoseUtils.RelativeOffset(data.PointerPose, data.Root);
|
||||
rootToPointer.position = (rootToPointer.position / data.HandScale) * _scale;
|
||||
PoseUtils.Multiply(data.Root, rootToPointer, ref data.PointerPose);
|
||||
|
||||
data.HandScale = _scale;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Inject
|
||||
public void InjectAllFixedScaleDataModifier(UpdateModeFlags updateMode, IDataSource updateAfter,
|
||||
DataModifier<HandDataAsset, HandDataSourceConfig> modifyDataFromSource, bool applyModifier,
|
||||
Component[] aspects, float scale)
|
||||
{
|
||||
base.InjectAllHand(updateMode, updateAfter, modifyDataFromSource, applyModifier, aspects);
|
||||
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,101 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 JointRotationHistoryModifier : 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 InjectAllJointRotationHistoryModifier(UpdateModeFlags updateMode, IDataSource updateAfter,
|
||||
DataModifier<HandDataAsset, HandDataSourceConfig> modifyDataFromSource, bool applyModifier,
|
||||
Component[] aspects, int historyLength, int historyOffset)
|
||||
{
|
||||
base.InjectAllHand(updateMode, updateAfter, modifyDataFromSource, applyModifier, aspects);
|
||||
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,49 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 LastKnownGoodDataModifier : Hand
|
||||
{
|
||||
private readonly HandDataAsset _lastState = new HandDataAsset();
|
||||
|
||||
#region DataModifier Implementation
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7daff26e66096cd49a253ca3ab592c03
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,83 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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.Profiling;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
public class OneEuroFilterPositionDataModifier : Hand
|
||||
{
|
||||
[Header("Wrist")]
|
||||
[SerializeField]
|
||||
private OneEuroFilterPropertyBlock _wristFilterProperties =
|
||||
new OneEuroFilterPropertyBlock(2f, 10f);
|
||||
|
||||
private IOneEuroFilter<Vector3> _wristFilter;
|
||||
private int _lastFrameUpdated;
|
||||
private Pose _lastSmoothedPose;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
_lastFrameUpdated = 0;
|
||||
_wristFilter = OneEuroFilter.CreateVector3();
|
||||
}
|
||||
|
||||
#region IHandInputDataModifier Implementation
|
||||
protected override void Apply(HandDataAsset handDataAsset)
|
||||
{
|
||||
if (!handDataAsset.IsTracked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Profiler.BeginSample($"{nameof(OneEuroFilterPositionDataModifier)}." +
|
||||
$"{nameof(OneEuroFilterPositionDataModifier.Apply)}");
|
||||
|
||||
if (Time.frameCount > _lastFrameUpdated)
|
||||
{
|
||||
_lastFrameUpdated = Time.frameCount;
|
||||
_lastSmoothedPose = ApplyFilter(handDataAsset.Root);
|
||||
}
|
||||
|
||||
handDataAsset.Root = _lastSmoothedPose;
|
||||
handDataAsset.RootPoseOrigin = PoseOrigin.FilteredTrackedPose;
|
||||
|
||||
Profiler.EndSample();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private Pose ApplyFilter(Pose pose)
|
||||
{
|
||||
_wristFilter.SetProperties(_wristFilterProperties);
|
||||
pose.position = _wristFilter.Step(pose.position, Time.fixedDeltaTime);
|
||||
return pose;
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllOneEuroFilterPositionDataModifier(UpdateModeFlags updateMode, IDataSource updateAfter,
|
||||
DataModifier<HandDataAsset, HandDataSourceConfig> modifyDataFromSource, bool applyModifier,
|
||||
Component[] aspects, OneEuroFilterPropertyBlock wristFilterProperties)
|
||||
{
|
||||
base.InjectAllHand(updateMode, updateAfter, modifyDataFromSource, applyModifier, aspects);
|
||||
InjectWristFilterProperties(wristFilterProperties);
|
||||
}
|
||||
|
||||
public void InjectWristFilterProperties(OneEuroFilterPropertyBlock wristFilterProperties)
|
||||
{
|
||||
_wristFilterProperties = wristFilterProperties;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77f93b4928ccdb2448c377308b7f4731
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,162 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 System.Collections.Generic;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Data Modifier use to apply the One Euro Filter to a hand pose.
|
||||
/// Filtering can be applied to the wrist and any number of finger joints.
|
||||
/// </summary>
|
||||
public class OneEuroFilterRotationDataModifier : Hand
|
||||
{
|
||||
[Header("Wrist")]
|
||||
[SerializeField]
|
||||
private bool _wristFilterEnabled = true;
|
||||
|
||||
[SerializeField]
|
||||
private OneEuroFilterPropertyBlock _wristFilterProperties =
|
||||
new OneEuroFilterPropertyBlock(2f, 3f);
|
||||
|
||||
[Header("Fingers")]
|
||||
[SerializeField]
|
||||
private bool _fingerFiltersEnabled = true;
|
||||
|
||||
[SerializeField]
|
||||
private HandFingerJointFlags _fingerJoints = HandFingerJointFlags.None;
|
||||
|
||||
[SerializeField]
|
||||
private OneEuroFilterPropertyBlock _fingerFilterProperties =
|
||||
new OneEuroFilterPropertyBlock(1f, 2f);
|
||||
|
||||
private IOneEuroFilter<Quaternion> _wristFilter;
|
||||
private List<JointFilter> _fingerJointFilters;
|
||||
private HandDataAsset _lastFiltered;
|
||||
private int _lastFrameUpdated;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
InitFingerJointFilters();
|
||||
|
||||
_lastFrameUpdated = 0;
|
||||
_lastFiltered = new HandDataAsset();
|
||||
_wristFilter = OneEuroFilter.CreateQuaternion();
|
||||
}
|
||||
|
||||
private void InitFingerJointFilters()
|
||||
{
|
||||
_fingerJointFilters = new List<JointFilter>();
|
||||
if (_fingerJoints == HandFingerJointFlags.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var jointId in HandJointUtils.JointIds)
|
||||
{
|
||||
HandFingerJointFlags jointFlag = (HandFingerJointFlags)(1 << (int)jointId);
|
||||
if (!Enum.IsDefined(typeof(HandFingerJointFlags), jointFlag))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_fingerJoints.HasFlag(jointFlag))
|
||||
{
|
||||
_fingerJointFilters.Add(new JointFilter(jointId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IHandInputDataModifier Implementation
|
||||
protected override void Apply(HandDataAsset handDataAsset)
|
||||
{
|
||||
if (!handDataAsset.IsTracked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Profiler.BeginSample($"{nameof(OneEuroFilterRotationDataModifier)}." +
|
||||
$"{nameof(OneEuroFilterRotationDataModifier.Apply)}");
|
||||
|
||||
if (Time.frameCount > _lastFrameUpdated)
|
||||
{
|
||||
_lastFrameUpdated = Time.frameCount;
|
||||
ApplyFilters(handDataAsset);
|
||||
_lastFiltered.CopyFrom(handDataAsset);
|
||||
}
|
||||
else
|
||||
{
|
||||
handDataAsset.CopyFrom(_lastFiltered);
|
||||
}
|
||||
|
||||
handDataAsset.RootPoseOrigin = PoseOrigin.FilteredTrackedPose;
|
||||
|
||||
Profiler.EndSample();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void ApplyFilters(HandDataAsset handDataAsset)
|
||||
{
|
||||
if (_wristFilterEnabled)
|
||||
{
|
||||
Pose rootPose = handDataAsset.Root;
|
||||
_wristFilter.SetProperties(_wristFilterProperties);
|
||||
rootPose.rotation = _wristFilter.Step(rootPose.rotation, Time.fixedDeltaTime);
|
||||
handDataAsset.Root = rootPose;
|
||||
}
|
||||
|
||||
if (_fingerFiltersEnabled)
|
||||
{
|
||||
foreach (var joint in _fingerJointFilters)
|
||||
{
|
||||
joint.Filter.SetProperties(_fingerFilterProperties);
|
||||
handDataAsset.Joints[(int)joint.JointId] =
|
||||
joint.Filter.Step(handDataAsset.Joints[(int)joint.JointId], Time.fixedDeltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class JointFilter
|
||||
{
|
||||
public HandJointId JointId => _jointId;
|
||||
public IOneEuroFilter<Quaternion> Filter => _filter;
|
||||
|
||||
private readonly HandJointId _jointId;
|
||||
private readonly IOneEuroFilter<Quaternion> _filter;
|
||||
|
||||
public JointFilter(HandJointId jointId)
|
||||
{
|
||||
_jointId = jointId;
|
||||
_filter = OneEuroFilter.CreateQuaternion();
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllOneEuroFilterRotationDataModifier(UpdateModeFlags updateMode, IDataSource updateAfter,
|
||||
DataModifier<HandDataAsset, HandDataSourceConfig> modifyDataFromSource, bool applyModifier,
|
||||
Component[] aspects, OneEuroFilterPropertyBlock wristFilterProperties)
|
||||
{
|
||||
base.InjectAllHand(updateMode, updateAfter, modifyDataFromSource, applyModifier, aspects);
|
||||
InjectWristFilterProperties(wristFilterProperties);
|
||||
}
|
||||
|
||||
public void InjectWristFilterProperties(OneEuroFilterPropertyBlock wristFilterProperties)
|
||||
{
|
||||
_wristFilterProperties = wristFilterProperties;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e410712d2f3ef084daeb2eac77899ce2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,508 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 SyntheticHandModifier : Hand
|
||||
{
|
||||
[System.Flags]
|
||||
public enum WristLockMode
|
||||
{
|
||||
Position = 1 << 0,
|
||||
Rotation = 1 << 1,
|
||||
Full = (1 << 2) - 1
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private ProgressCurve _wristPositionLockCurve = new ProgressCurve();
|
||||
[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()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; i++)
|
||||
{
|
||||
_jointLockProgressCurves[i] = new ProgressCurve(_jointLockCurve);
|
||||
_jointUnlockProgressCurves[i] = new ProgressCurve(_jointUnlockCurve);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Apply(HandDataAsset data)
|
||||
{
|
||||
if (!data.IsDataValid || !data.IsTracked || !data.IsHighConfidence)
|
||||
{
|
||||
data.IsConnected = false;
|
||||
data.RootPoseOrigin = PoseOrigin.None;
|
||||
_hasConnectedData = false;
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateRequired.Invoke();
|
||||
_lastStates.CopyFrom(data);
|
||||
|
||||
if (!_hasConnectedData)
|
||||
{
|
||||
_constrainedWristPose = 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);
|
||||
}
|
||||
|
||||
/// <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.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)
|
||||
{
|
||||
SyntheticWristLockChangedState(WristLockMode.Position, skipAnimation);
|
||||
_wristPositionLocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void LockWristRotation(Quaternion rotation, float overrideFactor = 1f, bool skipAnimation = false)
|
||||
{
|
||||
_wristRotationOverrideFactor = overrideFactor;
|
||||
_desiredWristPose.rotation = rotation;
|
||||
if (!_wristRotationLocked)
|
||||
{
|
||||
SyntheticWristLockChangedState(WristLockMode.Rotation, skipAnimation);
|
||||
_wristRotationLocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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, HandDataSourceConfig> modifyDataFromSource, bool applyModifier,
|
||||
Component[] aspects,
|
||||
ProgressCurve wristPositionLockCurve, ProgressCurve wristPositionUnlockCurve,
|
||||
ProgressCurve wristRotationLockCurve, ProgressCurve wristRotationUnlockCurve,
|
||||
ProgressCurve jointLockCurve, ProgressCurve jointUnlockCurve,
|
||||
float spreadAllowance)
|
||||
{
|
||||
base.InjectAllHand(updateMode, updateAfter, modifyDataFromSource, applyModifier, aspects);
|
||||
|
||||
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,137 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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}
|
||||
};
|
||||
|
||||
/// <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>
|
||||
/// Sorted like HAND_JOINT_IDS, this array is used to retrieve the finger
|
||||
/// each joint belongs to.
|
||||
/// </summary>
|
||||
public static readonly HandFinger[] HAND_FINGER_ID = new HandFinger[]
|
||||
{
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c86919be0f18a54b8706542dc38dede
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
274
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/Hand.cs
Normal file
274
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/Hand.cs
Normal file
@@ -0,0 +1,274 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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
|
||||
{
|
||||
// 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, HandDataSourceConfig>, IHand
|
||||
{
|
||||
[SerializeField]
|
||||
[Tooltip("Provides access to additional functionality on top of what the IHand interface provides." +
|
||||
"For example, this list can be used to provide access to the SkinnedMeshRenderer through " +
|
||||
"the IHand.GetHandAspect method.")]
|
||||
private Component[] _aspects;
|
||||
|
||||
public IReadOnlyList<Component> Aspects => _aspects;
|
||||
|
||||
public ITrackingToWorldTransformer TrackingToWorldTransformer =>
|
||||
Config.TrackingToWorldTransformer;
|
||||
|
||||
private HandJointCache _jointPosesCache;
|
||||
|
||||
public event Action HandUpdated = delegate { };
|
||||
|
||||
public bool IsConnected => GetData().IsDataValidAndConnected;
|
||||
public bool IsHighConfidence => GetData().IsHighConfidence;
|
||||
public bool IsDominantHand => GetData().IsDominantHand;
|
||||
public Handedness Handedness => Config.Handedness;
|
||||
public float Scale => GetData().HandScale * TrackingToWorldTransformer.Transform.localScale.x;
|
||||
|
||||
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();
|
||||
HandUpdated.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeJointPosesCache()
|
||||
{
|
||||
if (_jointPosesCache == null && GetData().IsDataValidAndConnected)
|
||||
{
|
||||
_jointPosesCache = new HandJointCache(Config.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);
|
||||
}
|
||||
|
||||
public bool IsCenterEyePoseValid => Config.HmdData.GetData().IsTracked;
|
||||
|
||||
public bool GetCenterEyePose(out Pose pose)
|
||||
{
|
||||
HmdDataAsset hmd = Config.HmdData.GetData();
|
||||
if (!hmd.IsTracked)
|
||||
{
|
||||
pose = new Pose();
|
||||
return false;
|
||||
}
|
||||
|
||||
pose = TrackingToWorldTransformer.ToWorldPose(hmd.Root);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public Transform TrackingToWorldSpace
|
||||
{
|
||||
get
|
||||
{
|
||||
return TrackingToWorldTransformer.Transform;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidatePose(in Pose sourcePose, PoseOrigin sourcePoseOrigin, out Pose pose)
|
||||
{
|
||||
if (IsPoseOriginDisallowed(sourcePoseOrigin))
|
||||
{
|
||||
pose = Pose.identity;
|
||||
return false;
|
||||
}
|
||||
pose = TrackingToWorldTransformer.ToWorldPose(sourcePose);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsPoseOriginAllowed(PoseOrigin poseOrigin)
|
||||
{
|
||||
return poseOrigin != PoseOrigin.None;
|
||||
}
|
||||
|
||||
private bool IsPoseOriginDisallowed(PoseOrigin poseOrigin)
|
||||
{
|
||||
return poseOrigin == PoseOrigin.None;
|
||||
}
|
||||
|
||||
public bool GetHandAspect<TComponent>(out TComponent foundComponent) where TComponent : class
|
||||
{
|
||||
foreach (Component aspect in _aspects)
|
||||
{
|
||||
foundComponent = aspect as TComponent;
|
||||
if (foundComponent != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foundComponent = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllHand(UpdateModeFlags updateMode, IDataSource updateAfter,
|
||||
DataModifier<HandDataAsset, HandDataSourceConfig> modifyDataFromSource, bool applyModifier,
|
||||
Component[] aspects)
|
||||
{
|
||||
base.InjectAllDataModifier(updateMode, updateAfter, modifyDataFromSource, applyModifier);
|
||||
InjectAspects(aspects);
|
||||
}
|
||||
|
||||
public void InjectAspects(Component[] aspects)
|
||||
{
|
||||
_aspects = aspects;
|
||||
}
|
||||
|
||||
#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,62 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 bool IsDataValidAndConnected => IsDataValid && IsConnected;
|
||||
|
||||
public void CopyFrom(HandDataAsset source)
|
||||
{
|
||||
IsDataValid = source.IsDataValid;
|
||||
IsConnected = source.IsConnected;
|
||||
IsTracked = source.IsTracked;
|
||||
IsHighConfidence = source.IsHighConfidence;
|
||||
IsDominantHand = source.IsDominantHand;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24ccc8fce03318d4fb4fbd8bb782ded2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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; }
|
||||
public IDataSource<HmdDataAsset, HmdDataSourceConfig> HmdData { 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,145 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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,245 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 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
|
||||
{
|
||||
Thumb = 0,
|
||||
Index = 1,
|
||||
Middle = 2,
|
||||
Ring = 3,
|
||||
Pinky = 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
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum HandFingerJointFlags
|
||||
{
|
||||
None = 0,
|
||||
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,
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3140fee102ed8c2489e5442c16b1b36f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
150
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/HandRef.cs
Normal file
150
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/HandRef.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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>
|
||||
/// 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 MonoBehaviour _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 bool IsCenterEyePoseValid => Hand.IsCenterEyePoseValid;
|
||||
|
||||
public Transform TrackingToWorldSpace => Hand.TrackingToWorldSpace;
|
||||
public int CurrentDataVersion => Hand.CurrentDataVersion;
|
||||
|
||||
public event Action HandUpdated
|
||||
{
|
||||
add => Hand.HandUpdated += value;
|
||||
remove => Hand.HandUpdated -= value;
|
||||
}
|
||||
|
||||
public bool Active => IsConnected;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Hand = _hand as IHand;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
Assert.IsNotNull(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);
|
||||
}
|
||||
|
||||
public bool GetCenterEyePose(out Pose pose)
|
||||
{
|
||||
return Hand.GetCenterEyePose(out pose);
|
||||
}
|
||||
|
||||
public bool GetHandAspect<TComponent>(out TComponent foundComponent) where TComponent : class
|
||||
{
|
||||
return Hand.GetHandAspect(out foundComponent);
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllHandRef(IHand hand)
|
||||
{
|
||||
InjectHand(hand);
|
||||
}
|
||||
|
||||
public void InjectHand(IHand hand)
|
||||
{
|
||||
_hand = hand as MonoBehaviour;
|
||||
Hand = hand;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b14164f8f23faae4293baeb84485b3d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
157
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/IHand.cs
Normal file
157
Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/IHand.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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>
|
||||
/// Will return true if an HMD Center Eye pose available, that can be retrieved via
|
||||
/// <see cref="GetCenterEyePose"/>
|
||||
/// </summary>
|
||||
bool IsCenterEyePoseValid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pose of the center eye (HMD), in world space.
|
||||
/// Will return true if a pose was available; false otherwise.
|
||||
/// </summary>
|
||||
bool GetCenterEyePose(out Pose pose);
|
||||
|
||||
/// <summary>
|
||||
/// The transform that was applied to all tracking space poses to convert them to world
|
||||
/// space.
|
||||
/// </summary>
|
||||
Transform TrackingToWorldSpace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Incremented every time the source tracking or state data changes.
|
||||
/// </summary>
|
||||
int CurrentDataVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An Aspect provides additional functionality on top of what the HandState provides.
|
||||
/// The underlying hand is responsible for finding the most appropriate component.
|
||||
/// It is usually, but not necessarily, located within the same GameObject as the
|
||||
/// underlying hand.
|
||||
/// For example, this method can be used to source the SkinnedMeshRenderer representing the
|
||||
/// hand, if one exists.
|
||||
/// <returns>true if an aspect of the requested type was found, false otherwise</returns>
|
||||
/// </summary>
|
||||
bool GetHandAspect<TComponent>(out TComponent foundComponent) where TComponent : class;
|
||||
|
||||
event Action HandUpdated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e500f060fe544c21b9b8892fb57511e1
|
||||
timeCreated: 1630518667
|
||||
@@ -0,0 +1,19 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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,131 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
public class JointLock
|
||||
{
|
||||
public HandJointId JointId;
|
||||
public bool Locked;
|
||||
public Quaternion SourceRotation;
|
||||
|
||||
public Quaternion AnimationStartRotation;
|
||||
public Quaternion AnimationTargetRotation;
|
||||
public float AnimationTime = 0f;
|
||||
public Quaternion OutputRotation;
|
||||
}
|
||||
|
||||
public class JointLocking
|
||||
{
|
||||
public static void Initialize(HandDataAsset data,
|
||||
out Dictionary<HandJointId, JointLock> jointMap)
|
||||
{
|
||||
jointMap = new Dictionary<HandJointId, JointLock>();
|
||||
foreach (HandJointId jointId in HandJointUtils.JointIds)
|
||||
{
|
||||
JointLock jointLock = new JointLock()
|
||||
{
|
||||
JointId = jointId,
|
||||
SourceRotation = data.Joints[(int)jointId],
|
||||
AnimationTargetRotation = data.Joints[(int)jointId],
|
||||
OutputRotation = data.Joints[(int)jointId],
|
||||
AnimationTime = 0f,
|
||||
Locked = false
|
||||
};
|
||||
|
||||
jointMap.Add(jointId, jointLock);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateLockedJoints(
|
||||
HandDataAsset data,
|
||||
Dictionary<HandJointId, JointLock> jointMap,
|
||||
float totalEaseTime,
|
||||
AnimationCurve animationCurve)
|
||||
{
|
||||
foreach (JointLock angleLock in jointMap.Values)
|
||||
{
|
||||
angleLock.SourceRotation = data.Joints[(int)angleLock.JointId];
|
||||
if (!angleLock.Locked)
|
||||
{
|
||||
angleLock.AnimationTargetRotation = data.Joints[(int)angleLock.JointId];
|
||||
}
|
||||
|
||||
angleLock.AnimationTime = Math.Min(angleLock.AnimationTime + Time.deltaTime /
|
||||
totalEaseTime, 1.0f);
|
||||
float easeTime = animationCurve.Evaluate(angleLock.AnimationTime);
|
||||
|
||||
angleLock.OutputRotation = Quaternion.Slerp(angleLock.AnimationStartRotation,
|
||||
angleLock.AnimationTargetRotation, easeTime);
|
||||
data.Joints[(int)angleLock.JointId] = angleLock.OutputRotation;
|
||||
}
|
||||
}
|
||||
|
||||
public static void LockJoint(
|
||||
HandJointId jointId,
|
||||
Quaternion targetRotation,
|
||||
Dictionary<HandJointId, JointLock> jointMap)
|
||||
{
|
||||
if (jointMap[jointId].Locked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
jointMap[jointId].Locked = true;
|
||||
jointMap[jointId].AnimationTargetRotation = targetRotation;
|
||||
jointMap[jointId].AnimationStartRotation = jointMap[jointId].OutputRotation;
|
||||
jointMap[jointId].AnimationTime = 0f;
|
||||
}
|
||||
|
||||
public static void UnlockJoint(HandJointId jointId,
|
||||
Dictionary<HandJointId, JointLock> jointMap)
|
||||
{
|
||||
if (!jointMap[jointId].Locked) return;
|
||||
|
||||
jointMap[jointId].Locked = false;
|
||||
jointMap[jointId].AnimationStartRotation = jointMap[jointId].OutputRotation;
|
||||
jointMap[jointId].AnimationTime = 0f;
|
||||
}
|
||||
|
||||
public static void UnlockAllJoints(Dictionary<HandJointId, JointLock> jointMap)
|
||||
{
|
||||
foreach (var jointId in jointMap.Keys)
|
||||
{
|
||||
UnlockJoint(jointId, jointMap);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LockByFingerId(HandFinger finger,
|
||||
Dictionary<HandJointId, JointLock> jointMap)
|
||||
{
|
||||
HandJointId[] joints = HandJointUtils.FingerToJointList[(int)finger];
|
||||
foreach (var jointId in joints)
|
||||
{
|
||||
LockJoint(jointId, jointMap[jointId].SourceRotation, jointMap);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnlockByFingerId(HandFinger finger,
|
||||
Dictionary<HandJointId, JointLock> jointMap)
|
||||
{
|
||||
HandJointId[] joints = HandJointUtils.FingerToJointList[(int)finger];
|
||||
foreach (var jointId in joints)
|
||||
{
|
||||
UnlockJoint(jointId, jointMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d043893ca7b199f42a4d1c3785a23d02
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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: 96e4e4f16838b9548b5c62249aa3b023
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,114 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 AnimatedHandOVR : MonoBehaviour
|
||||
{
|
||||
public const string ANIM_LAYER_NAME_POINT = "Point Layer";
|
||||
public const string ANIM_LAYER_NAME_THUMB = "Thumb Layer";
|
||||
public const string ANIM_PARAM_NAME_FLEX = "Flex";
|
||||
|
||||
public const float INPUT_RATE_CHANGE = 20.0f;
|
||||
|
||||
[SerializeField]
|
||||
private OVRInput.Controller _controller = OVRInput.Controller.None;
|
||||
[SerializeField]
|
||||
private Animator _animator = null;
|
||||
|
||||
private int _animLayerIndexThumb = -1;
|
||||
private int _animLayerIndexPoint = -1;
|
||||
private int _animParamIndexFlex = -1;
|
||||
|
||||
private bool _isPointing = false;
|
||||
private bool _isGivingThumbsUp = false;
|
||||
private float _pointBlend = 0.0f;
|
||||
private float _thumbsUpBlend = 0.0f;
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
_animLayerIndexPoint = _animator.GetLayerIndex(ANIM_LAYER_NAME_POINT);
|
||||
_animLayerIndexThumb = _animator.GetLayerIndex(ANIM_LAYER_NAME_THUMB);
|
||||
_animParamIndexFlex = Animator.StringToHash(ANIM_PARAM_NAME_FLEX);
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
UpdateCapTouchStates();
|
||||
|
||||
_pointBlend = InputValueRateChange(_isPointing, _pointBlend);
|
||||
_thumbsUpBlend = InputValueRateChange(_isGivingThumbsUp, _thumbsUpBlend);
|
||||
|
||||
UpdateAnimStates();
|
||||
}
|
||||
|
||||
private void UpdateCapTouchStates()
|
||||
{
|
||||
_isPointing = !OVRInput.Get(OVRInput.NearTouch.PrimaryIndexTrigger, _controller);
|
||||
_isGivingThumbsUp = !OVRInput.Get(OVRInput.NearTouch.PrimaryThumbButtons, _controller);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on InputValueRateChange from OVR Samples it ensures
|
||||
/// the animation blending happens with controlled timing instead of instantly
|
||||
/// </summary>
|
||||
/// <param name="isDown">Direction of the animation</param>
|
||||
/// <param name="value">Value to change</param>
|
||||
/// <returns>The input value increased or decreased at a fixed rate</returns>
|
||||
private float InputValueRateChange(bool isDown, float value)
|
||||
{
|
||||
float rateDelta = Time.deltaTime * INPUT_RATE_CHANGE;
|
||||
float sign = isDown ? 1.0f : -1.0f;
|
||||
return Mathf.Clamp01(value + rateDelta * sign);
|
||||
}
|
||||
|
||||
private void UpdateAnimStates()
|
||||
{
|
||||
// Flex
|
||||
// blend between open hand and fully closed fist
|
||||
float flex = OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, _controller);
|
||||
_animator.SetFloat(_animParamIndexFlex, flex);
|
||||
|
||||
// Point
|
||||
_animator.SetLayerWeight(_animLayerIndexPoint, _pointBlend);
|
||||
|
||||
// Thumbs up
|
||||
_animator.SetLayerWeight(_animLayerIndexThumb, _thumbsUpBlend);
|
||||
|
||||
float pinch = OVRInput.Get(OVRInput.Axis1D.PrimaryIndexTrigger, _controller);
|
||||
_animator.SetFloat("Pinch", pinch);
|
||||
}
|
||||
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllAnimatedHandOVR(OVRInput.Controller controller, Animator animator)
|
||||
{
|
||||
InjectController(controller);
|
||||
InjectAnimator(animator);
|
||||
}
|
||||
|
||||
public void InjectController(OVRInput.Controller controller)
|
||||
{
|
||||
_controller = controller;
|
||||
}
|
||||
|
||||
public void InjectAnimator(Animator animator)
|
||||
{
|
||||
_animator = animator;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2864da2f393a3e948bb12ec4ac9b1782
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,283 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
struct UsageMapping
|
||||
{
|
||||
public UsageMapping(ControllerButtonUsage usage, OVRInput.Touch touch)
|
||||
{
|
||||
Usage = usage;
|
||||
Touch = touch;
|
||||
Button = OVRInput.Button.None;
|
||||
}
|
||||
|
||||
public UsageMapping(ControllerButtonUsage usage, OVRInput.Button button)
|
||||
{
|
||||
Usage = usage;
|
||||
Touch = OVRInput.Touch.None;
|
||||
Button = button;
|
||||
}
|
||||
|
||||
public bool IsTouch => Touch != OVRInput.Touch.None;
|
||||
public bool IsButton => Button != OVRInput.Button.None;
|
||||
public ControllerButtonUsage Usage { get; }
|
||||
public OVRInput.Touch Touch { get; }
|
||||
public OVRInput.Button Button { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Pointer Pose for the active controller model
|
||||
/// as found in the official prefabs.
|
||||
/// This point is usually located at the front tip of the controller.
|
||||
/// </summary>
|
||||
struct OVRPointerPoseSelector
|
||||
{
|
||||
private static readonly Pose[] QUEST1_POINTERS = new Pose[2]
|
||||
{
|
||||
new Pose(new Vector3(-0.00779999979f,-0.00410000002f,0.0375000015f),
|
||||
Quaternion.Euler(359.209534f, 6.45196056f, 6.95544577f)),
|
||||
new Pose(new Vector3(0.00779999979f,-0.00410000002f,0.0375000015f),
|
||||
Quaternion.Euler(359.209534f, 353.548035f, 353.044556f))
|
||||
};
|
||||
|
||||
private static readonly Pose[] QUEST2_POINTERS = new Pose[2]
|
||||
{
|
||||
new Pose(new Vector3(0.00899999961f, -0.00321028521f, 0.030869998f),
|
||||
Quaternion.Euler(359.209534f, 6.45196056f, 6.95544577f)),
|
||||
new Pose(new Vector3(-0.00899999961f, -0.00321028521f, 0.030869998f),
|
||||
Quaternion.Euler(359.209534f, 353.548035f, 353.044556f))
|
||||
};
|
||||
|
||||
public Pose LocalPointerPose { get; private set; }
|
||||
|
||||
public OVRPointerPoseSelector(Handedness handedness)
|
||||
{
|
||||
OVRPlugin.SystemHeadset headset = OVRPlugin.GetSystemHeadsetType();
|
||||
switch (headset)
|
||||
{
|
||||
case OVRPlugin.SystemHeadset.Oculus_Quest_2:
|
||||
case OVRPlugin.SystemHeadset.Oculus_Link_Quest_2:
|
||||
LocalPointerPose = QUEST2_POINTERS[(int)handedness];
|
||||
break;
|
||||
default:
|
||||
LocalPointerPose = QUEST1_POINTERS[(int)handedness];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FromOVRControllerDataSource : DataSource<ControllerDataAsset, ControllerDataSourceConfig>
|
||||
{
|
||||
[Header("OVR Data Source")]
|
||||
[SerializeField, Interface(typeof(IOVRCameraRigRef))]
|
||||
private MonoBehaviour _cameraRigRef;
|
||||
|
||||
[Header("Shared Configuration")]
|
||||
[SerializeField]
|
||||
private Handedness _handedness;
|
||||
|
||||
[SerializeField, Interface(typeof(ITrackingToWorldTransformer))]
|
||||
private MonoBehaviour _trackingToWorldTransformer;
|
||||
private ITrackingToWorldTransformer TrackingToWorldTransformer;
|
||||
|
||||
[SerializeField, Interface(typeof(IDataSource<HmdDataAsset, HmdDataSourceConfig>))]
|
||||
private MonoBehaviour _hmdData;
|
||||
private IDataSource<HmdDataAsset, HmdDataSourceConfig> HmdData;
|
||||
|
||||
private readonly ControllerDataAsset _controllerDataAsset = new ControllerDataAsset();
|
||||
private OVRInput.Controller _ovrController;
|
||||
private Transform _ovrControllerAnchor;
|
||||
private ControllerDataSourceConfig _config;
|
||||
|
||||
private OVRPointerPoseSelector _pointerPoseSelector;
|
||||
|
||||
public IOVRCameraRigRef CameraRigRef { get; private set; }
|
||||
|
||||
#region OVR Controller Mappings
|
||||
|
||||
// Mappings from Unity XR CommonUsage to Oculus Button/Touch.
|
||||
private static readonly UsageMapping[] ControllerUsageMappings =
|
||||
{
|
||||
new UsageMapping(ControllerButtonUsage.PrimaryButton, OVRInput.Button.One),
|
||||
new UsageMapping(ControllerButtonUsage.PrimaryTouch, OVRInput.Touch.One),
|
||||
new UsageMapping(ControllerButtonUsage.SecondaryButton, OVRInput.Button.Two),
|
||||
new UsageMapping(ControllerButtonUsage.SecondaryTouch, OVRInput.Touch.Two),
|
||||
new UsageMapping(ControllerButtonUsage.GripButton,
|
||||
OVRInput.Button.PrimaryHandTrigger),
|
||||
new UsageMapping(ControllerButtonUsage.TriggerButton,
|
||||
OVRInput.Button.PrimaryIndexTrigger),
|
||||
new UsageMapping(ControllerButtonUsage.MenuButton, OVRInput.Button.Start),
|
||||
new UsageMapping(ControllerButtonUsage.Primary2DAxisClick,
|
||||
OVRInput.Button.PrimaryThumbstick),
|
||||
new UsageMapping(ControllerButtonUsage.Primary2DAxisTouch,
|
||||
OVRInput.Touch.PrimaryThumbstick),
|
||||
new UsageMapping(ControllerButtonUsage.Thumbrest, OVRInput.Touch.PrimaryThumbRest)
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
protected void Awake()
|
||||
{
|
||||
TrackingToWorldTransformer = _trackingToWorldTransformer as ITrackingToWorldTransformer;
|
||||
HmdData = _hmdData as IDataSource<HmdDataAsset, HmdDataSourceConfig>;
|
||||
CameraRigRef = _cameraRigRef as IOVRCameraRigRef;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
Assert.IsNotNull(CameraRigRef);
|
||||
Assert.IsNotNull(TrackingToWorldTransformer);
|
||||
Assert.IsNotNull(HmdData);
|
||||
if (_handedness == Handedness.Left)
|
||||
{
|
||||
Assert.IsNotNull(CameraRigRef.LeftController);
|
||||
_ovrControllerAnchor = CameraRigRef.LeftController;
|
||||
_ovrController = OVRInput.Controller.LTouch;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNotNull(CameraRigRef.RightController);
|
||||
_ovrControllerAnchor = CameraRigRef.RightController;
|
||||
_ovrController = OVRInput.Controller.RTouch;
|
||||
}
|
||||
_pointerPoseSelector = new OVRPointerPoseSelector(_handedness);
|
||||
}
|
||||
|
||||
private void InitConfig()
|
||||
{
|
||||
if (_config != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_config = new ControllerDataSourceConfig()
|
||||
{
|
||||
Handedness = _handedness,
|
||||
TrackingToWorldTransformer = TrackingToWorldTransformer,
|
||||
HmdData = HmdData
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateData()
|
||||
{
|
||||
var worldToTrackingSpace = CameraRigRef.CameraRig.transform.worldToLocalMatrix;
|
||||
Transform ovrController = _ovrControllerAnchor;
|
||||
|
||||
_controllerDataAsset.IsDataValid = true;
|
||||
_controllerDataAsset.IsConnected =
|
||||
(OVRInput.GetConnectedControllers() & _ovrController) > 0;
|
||||
if (!_controllerDataAsset.IsConnected)
|
||||
{
|
||||
// revert state fields to their defaults
|
||||
_controllerDataAsset.IsTracked = default;
|
||||
_controllerDataAsset.ButtonUsageMask = default;
|
||||
_controllerDataAsset.RootPoseOrigin = default;
|
||||
return;
|
||||
}
|
||||
|
||||
_controllerDataAsset.IsTracked = true;
|
||||
|
||||
// Update button usages
|
||||
_controllerDataAsset.ButtonUsageMask = ControllerButtonUsage.None;
|
||||
OVRInput.Controller controllerMask = _ovrController;
|
||||
foreach (UsageMapping mapping in ControllerUsageMappings)
|
||||
{
|
||||
bool usageActive;
|
||||
if (mapping.IsTouch)
|
||||
{
|
||||
usageActive = OVRInput.Get(mapping.Touch, controllerMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsTrue(mapping.IsButton);
|
||||
usageActive = OVRInput.Get(mapping.Button, controllerMask);
|
||||
}
|
||||
|
||||
if (usageActive)
|
||||
{
|
||||
_controllerDataAsset.ButtonUsageMask |= mapping.Usage;
|
||||
}
|
||||
}
|
||||
|
||||
// Update poses
|
||||
|
||||
// Convert controller pose from world to tracking space.
|
||||
Pose worldRoot = new Pose(ovrController.position, ovrController.rotation);
|
||||
_controllerDataAsset.RootPose.position = worldToTrackingSpace.MultiplyPoint3x4(worldRoot.position);
|
||||
_controllerDataAsset.RootPose.rotation = worldToTrackingSpace.rotation * worldRoot.rotation;
|
||||
_controllerDataAsset.RootPoseOrigin = PoseOrigin.RawTrackedPose;
|
||||
|
||||
|
||||
// Convert controller pointer pose from local to tracking space.
|
||||
Pose pointerPose = PoseUtils.Multiply(worldRoot, _pointerPoseSelector.LocalPointerPose);
|
||||
_controllerDataAsset.PointerPose.position = worldToTrackingSpace.MultiplyPoint3x4(pointerPose.position);
|
||||
_controllerDataAsset.PointerPose.rotation = worldToTrackingSpace.rotation * pointerPose.rotation;
|
||||
_controllerDataAsset.PointerPoseOrigin = PoseOrigin.RawTrackedPose;
|
||||
|
||||
}
|
||||
|
||||
protected override ControllerDataAsset DataAsset => _controllerDataAsset;
|
||||
|
||||
// It is important that this creates an object on the fly, as it is possible it is called
|
||||
// from other components Awake methods.
|
||||
public override ControllerDataSourceConfig Config
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
InitConfig();
|
||||
}
|
||||
|
||||
return _config;
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllFromOVRControllerDataSource(UpdateModeFlags updateMode, IDataSource updateAfter,
|
||||
Handedness handedness, ITrackingToWorldTransformer trackingToWorldTransformer,
|
||||
IDataSource<HmdDataAsset, HmdDataSourceConfig> hmdData)
|
||||
{
|
||||
base.InjectAllDataSource(updateMode, updateAfter);
|
||||
InjectHandedness(handedness);
|
||||
InjectTrackingToWorldTransformer(trackingToWorldTransformer);
|
||||
InjectHmdData(hmdData);
|
||||
}
|
||||
|
||||
public void InjectHandedness(Handedness handedness)
|
||||
{
|
||||
_handedness = handedness;
|
||||
}
|
||||
|
||||
public void InjectTrackingToWorldTransformer(ITrackingToWorldTransformer trackingToWorldTransformer)
|
||||
{
|
||||
_trackingToWorldTransformer = trackingToWorldTransformer as MonoBehaviour;
|
||||
TrackingToWorldTransformer = trackingToWorldTransformer;
|
||||
}
|
||||
|
||||
public void InjectHmdData(IDataSource<HmdDataAsset,HmdDataSourceConfig> hmdData)
|
||||
{
|
||||
_hmdData = hmdData as MonoBehaviour;
|
||||
HmdData = hmdData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee6b94f7bc105c24ea3746dc4141a7eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,270 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
public class FromOVRControllerHandDataSource : DataSource<HandDataAsset, HandDataSourceConfig>
|
||||
{
|
||||
[SerializeField]
|
||||
private Transform[] _bones;
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve _pinchCurve = AnimationCurve.EaseInOut(0.1f, 0f, 0.9f, 1f);
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 _rootOffset;
|
||||
[SerializeField]
|
||||
private Vector3 _rootAngleOffset;
|
||||
|
||||
[Header("OVR Data Source")]
|
||||
[SerializeField, Interface(typeof(IOVRCameraRigRef))]
|
||||
private MonoBehaviour _cameraRigRef;
|
||||
private IOVRCameraRigRef CameraRigRef;
|
||||
|
||||
[Header("Shared Configuration")]
|
||||
[SerializeField]
|
||||
private Handedness _handedness;
|
||||
|
||||
[SerializeField, Interface(typeof(ITrackingToWorldTransformer))]
|
||||
private MonoBehaviour _trackingToWorldTransformer;
|
||||
private ITrackingToWorldTransformer TrackingToWorldTransformer;
|
||||
|
||||
[SerializeField, Interface(typeof(IDataSource<HmdDataAsset, HmdDataSourceConfig>))]
|
||||
private MonoBehaviour _hmdData;
|
||||
private IDataSource<HmdDataAsset, HmdDataSourceConfig> HmdData;
|
||||
|
||||
private readonly HandDataAsset _handDataAsset = new HandDataAsset();
|
||||
private OVRInput.Controller _ovrController;
|
||||
private Transform _ovrControllerAnchor;
|
||||
private HandDataSourceConfig _config;
|
||||
private Pose _poseOffset;
|
||||
|
||||
|
||||
public static Quaternion WristFixupRotation { get; } =
|
||||
new Quaternion(0.0f, 1.0f, 0.0f, 0.0f);
|
||||
|
||||
protected override HandDataAsset DataAsset => _handDataAsset;
|
||||
|
||||
private HandSkeleton _skeleton;
|
||||
|
||||
public override HandDataSourceConfig Config
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
InitConfig();
|
||||
}
|
||||
|
||||
return _config;
|
||||
}
|
||||
}
|
||||
|
||||
protected void Awake()
|
||||
{
|
||||
_skeleton = HandSkeletonOVR.CreateSkeletonData(_handedness);
|
||||
TrackingToWorldTransformer = _trackingToWorldTransformer as ITrackingToWorldTransformer;
|
||||
HmdData = _hmdData as IDataSource<HmdDataAsset, HmdDataSourceConfig>;
|
||||
CameraRigRef = _cameraRigRef as IOVRCameraRigRef;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
Assert.IsNotNull(CameraRigRef);
|
||||
Assert.IsNotNull(TrackingToWorldTransformer);
|
||||
Assert.IsNotNull(HmdData);
|
||||
if (_handedness == Handedness.Left)
|
||||
{
|
||||
Assert.IsNotNull(CameraRigRef.LeftHand);
|
||||
_ovrControllerAnchor = CameraRigRef.LeftController;
|
||||
_ovrController = OVRInput.Controller.LTouch;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNotNull(CameraRigRef.RightHand);
|
||||
_ovrControllerAnchor = CameraRigRef.RightController;
|
||||
_ovrController = OVRInput.Controller.RTouch;
|
||||
}
|
||||
|
||||
Pose offset = new Pose(_rootOffset, Quaternion.Euler(_rootAngleOffset));
|
||||
if (_handedness == Handedness.Left)
|
||||
{
|
||||
offset.position.x = -offset.position.x;
|
||||
offset.rotation = Quaternion.Euler(180f, 0f, 0f) * offset.rotation;
|
||||
}
|
||||
_poseOffset = offset;
|
||||
|
||||
InitConfig();
|
||||
|
||||
}
|
||||
|
||||
private void UpdateSkeleton()
|
||||
{
|
||||
for (int i = 0; i < _skeleton.joints.Length; i++)
|
||||
{
|
||||
_skeleton.joints[i].pose.position = _bones[i].localPosition;
|
||||
_skeleton.joints[i].pose.rotation = _bones[i].localRotation;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitConfig()
|
||||
{
|
||||
if (_config != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateSkeleton();
|
||||
|
||||
_config = new HandDataSourceConfig()
|
||||
{
|
||||
Handedness = _handedness,
|
||||
TrackingToWorldTransformer = TrackingToWorldTransformer,
|
||||
HandSkeleton = _skeleton,
|
||||
HmdData = HmdData
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateData()
|
||||
{
|
||||
_handDataAsset.IsDataValid = true;
|
||||
_handDataAsset.IsConnected = (OVRInput.GetConnectedControllers() & _ovrController) > 0;
|
||||
if (!_handDataAsset.IsConnected)
|
||||
{
|
||||
// revert state fields to their defaults
|
||||
_handDataAsset.IsTracked = default;
|
||||
_handDataAsset.RootPoseOrigin = default;
|
||||
_handDataAsset.PointerPoseOrigin = default;
|
||||
_handDataAsset.IsHighConfidence = default;
|
||||
for (var fingerIdx = 0; fingerIdx < Constants.NUM_FINGERS; fingerIdx++)
|
||||
{
|
||||
_handDataAsset.IsFingerPinching[fingerIdx] = default;
|
||||
_handDataAsset.IsFingerHighConfidence[fingerIdx] = default;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_handDataAsset.IsTracked = true;
|
||||
_handDataAsset.IsHighConfidence = true;
|
||||
_handDataAsset.HandScale = 1f;
|
||||
|
||||
_handDataAsset.IsDominantHand =
|
||||
OVRInput.GetDominantHand() == OVRInput.Handedness.LeftHanded
|
||||
&& _handedness == Handedness.Left
|
||||
|| (OVRInput.GetDominantHand() == OVRInput.Handedness.RightHanded
|
||||
&& _handedness == Handedness.Right);
|
||||
|
||||
float indexStrength = _pinchCurve.Evaluate(OVRInput.Get(OVRInput.Axis1D.PrimaryIndexTrigger, _ovrController));
|
||||
float gripStrength = _pinchCurve.Evaluate(OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, _ovrController));
|
||||
|
||||
_handDataAsset.IsFingerHighConfidence[(int)HandFinger.Thumb] = true;
|
||||
_handDataAsset.IsFingerPinching[(int)HandFinger.Thumb] = indexStrength >= 1f || gripStrength >= 1f;
|
||||
_handDataAsset.FingerPinchStrength[(int)HandFinger.Thumb] = Mathf.Max(indexStrength, gripStrength);
|
||||
|
||||
_handDataAsset.IsFingerHighConfidence[(int)HandFinger.Index] = true;
|
||||
_handDataAsset.IsFingerPinching[(int)HandFinger.Index] = indexStrength >= 1f;
|
||||
_handDataAsset.FingerPinchStrength[(int)HandFinger.Index] = indexStrength;
|
||||
|
||||
_handDataAsset.IsFingerHighConfidence[(int)HandFinger.Middle] = true;
|
||||
_handDataAsset.IsFingerPinching[(int)HandFinger.Middle] = gripStrength >= 1f;
|
||||
_handDataAsset.FingerPinchStrength[(int)HandFinger.Middle] = gripStrength;
|
||||
|
||||
_handDataAsset.IsFingerHighConfidence[(int)HandFinger.Ring] = true;
|
||||
_handDataAsset.IsFingerPinching[(int)HandFinger.Ring] = gripStrength >= 1f;
|
||||
_handDataAsset.FingerPinchStrength[(int)HandFinger.Ring] = gripStrength;
|
||||
|
||||
_handDataAsset.IsFingerHighConfidence[(int)HandFinger.Pinky] = true;
|
||||
_handDataAsset.IsFingerPinching[(int)HandFinger.Pinky] = gripStrength >= 1f;
|
||||
_handDataAsset.FingerPinchStrength[(int)HandFinger.Pinky] = gripStrength;
|
||||
|
||||
_handDataAsset.PointerPoseOrigin = PoseOrigin.RawTrackedPose;
|
||||
_handDataAsset.PointerPose = new Pose(
|
||||
OVRInput.GetLocalControllerPosition(_ovrController),
|
||||
OVRInput.GetLocalControllerRotation(_ovrController));
|
||||
|
||||
for (int i = 0; i < _bones.Length; i++)
|
||||
{
|
||||
_handDataAsset.Joints[i] = _bones[i].localRotation;
|
||||
}
|
||||
|
||||
_handDataAsset.Joints[0] = WristFixupRotation;
|
||||
|
||||
// Convert controller pose from world to tracking space.
|
||||
Pose pose = new Pose(_ovrControllerAnchor.position, _ovrControllerAnchor.rotation);
|
||||
pose = Config.TrackingToWorldTransformer.ToTrackingPose(pose);
|
||||
|
||||
PoseUtils.Multiply(pose, _poseOffset, ref _handDataAsset.Root);
|
||||
_handDataAsset.RootPoseOrigin = PoseOrigin.RawTrackedPose;
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllFromOVRControllerHandDataSource(UpdateModeFlags updateMode, IDataSource updateAfter,
|
||||
Handedness handedness, ITrackingToWorldTransformer trackingToWorldTransformer,
|
||||
IDataSource<HmdDataAsset, HmdDataSourceConfig> hmdData, Transform[] bones, AnimationCurve pinchCurve,
|
||||
Vector3 rootOffset, Vector3 rootAngleOffset)
|
||||
{
|
||||
base.InjectAllDataSource(updateMode, updateAfter);
|
||||
InjectHandedness(handedness);
|
||||
InjectTrackingToWorldTransformer(trackingToWorldTransformer);
|
||||
InjectHmdData(hmdData);
|
||||
InjectBones(bones);
|
||||
InjectPinchCurve(pinchCurve);
|
||||
InjectRootOffset(rootOffset);
|
||||
InjectRootAngleOffset(rootAngleOffset);
|
||||
}
|
||||
|
||||
public void InjectHandedness(Handedness handedness)
|
||||
{
|
||||
_handedness = handedness;
|
||||
}
|
||||
|
||||
public void InjectTrackingToWorldTransformer(ITrackingToWorldTransformer trackingToWorldTransformer)
|
||||
{
|
||||
_trackingToWorldTransformer = trackingToWorldTransformer as MonoBehaviour;
|
||||
TrackingToWorldTransformer = trackingToWorldTransformer;
|
||||
}
|
||||
|
||||
public void InjectHmdData(IDataSource<HmdDataAsset, HmdDataSourceConfig> hmdData)
|
||||
{
|
||||
_hmdData = hmdData as MonoBehaviour;
|
||||
HmdData = hmdData;
|
||||
}
|
||||
|
||||
public void InjectBones(Transform[] bones)
|
||||
{
|
||||
_bones = bones;
|
||||
}
|
||||
|
||||
public void InjectPinchCurve(AnimationCurve pinchCurve)
|
||||
{
|
||||
_pinchCurve = pinchCurve;
|
||||
}
|
||||
|
||||
public void InjectRootOffset(Vector3 rootOffset)
|
||||
{
|
||||
_rootOffset = rootOffset;
|
||||
}
|
||||
|
||||
public void InjectRootAngleOffset(Vector3 rootAngleOffset)
|
||||
{
|
||||
_rootAngleOffset = rootAngleOffset;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb7d6cff5f17513488d465cdc4efd298
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,247 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
using UnityEngine.Serialization;
|
||||
using static OVRSkeleton;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
public class FromOVRHandDataSource : DataSource<HandDataAsset, HandDataSourceConfig>
|
||||
{
|
||||
[Header("OVR Data Source")]
|
||||
[SerializeField, Interface(typeof(IOVRCameraRigRef))]
|
||||
private MonoBehaviour _cameraRigRef;
|
||||
|
||||
[Header("Shared Configuration")]
|
||||
[SerializeField]
|
||||
private Handedness _handedness;
|
||||
|
||||
[SerializeField, Interface(typeof(ITrackingToWorldTransformer))]
|
||||
private MonoBehaviour _trackingToWorldTransformer;
|
||||
private ITrackingToWorldTransformer TrackingToWorldTransformer;
|
||||
|
||||
[SerializeField, Interface(typeof(IHandSkeletonProvider))]
|
||||
private MonoBehaviour _handSkeletonProvider;
|
||||
private IHandSkeletonProvider HandSkeletonProvider;
|
||||
|
||||
[SerializeField, Interface(typeof(IDataSource<HmdDataAsset, HmdDataSourceConfig>))]
|
||||
private MonoBehaviour _hmdData;
|
||||
private IDataSource<HmdDataAsset, HmdDataSourceConfig> HmdData;
|
||||
|
||||
private readonly HandDataAsset _handDataAsset = new HandDataAsset();
|
||||
private OVRHand _ovrHand;
|
||||
private OVRInput.Controller _ovrController;
|
||||
private float _lastHandScale;
|
||||
private HandDataSourceConfig _config;
|
||||
|
||||
private IOVRCameraRigRef CameraRigRef;
|
||||
|
||||
protected override HandDataAsset DataAsset => _handDataAsset;
|
||||
|
||||
// Wrist rotations that come from OVR need correcting.
|
||||
public static Quaternion WristFixupRotation { get; } =
|
||||
new Quaternion(0.0f, 1.0f, 0.0f, 0.0f);
|
||||
|
||||
// It is important that this creates an object on the fly, as it is possible it is called
|
||||
// from other components Start methods.
|
||||
public override HandDataSourceConfig Config => _config ?? InitConfig();
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
TrackingToWorldTransformer = _trackingToWorldTransformer as ITrackingToWorldTransformer;
|
||||
HmdData = _hmdData as IDataSource<HmdDataAsset, HmdDataSourceConfig>;
|
||||
CameraRigRef = _cameraRigRef as IOVRCameraRigRef;
|
||||
HandSkeletonProvider = _handSkeletonProvider as IHandSkeletonProvider;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
Assert.IsNotNull(CameraRigRef);
|
||||
Assert.IsNotNull(TrackingToWorldTransformer);
|
||||
Assert.IsNotNull(HandSkeletonProvider);
|
||||
Assert.IsNotNull(HmdData);
|
||||
if (_handedness == Handedness.Left)
|
||||
{
|
||||
_ovrHand = CameraRigRef.LeftHand;
|
||||
_ovrController = OVRInput.Controller.LHand;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ovrHand = CameraRigRef.RightHand;
|
||||
_ovrController = OVRInput.Controller.RHand;
|
||||
}
|
||||
}
|
||||
|
||||
private HandDataSourceConfig InitConfig()
|
||||
{
|
||||
if (_config != null)
|
||||
{
|
||||
return _config;
|
||||
}
|
||||
|
||||
_config = new HandDataSourceConfig()
|
||||
{
|
||||
Handedness = _handedness,
|
||||
TrackingToWorldTransformer = TrackingToWorldTransformer,
|
||||
HandSkeleton = HandSkeletonProvider[_handedness],
|
||||
HmdData = HmdData
|
||||
};
|
||||
|
||||
return _config;
|
||||
}
|
||||
|
||||
protected override void UpdateData()
|
||||
{
|
||||
_handDataAsset.IsDataValid = true;
|
||||
_handDataAsset.IsConnected =
|
||||
(OVRInput.GetConnectedControllers() & _ovrController) > 0;
|
||||
|
||||
if (_ovrHand != null)
|
||||
{
|
||||
var skeletonProvider = (IOVRSkeletonDataProvider)_ovrHand;
|
||||
var poseData = skeletonProvider.GetSkeletonPoseData();
|
||||
if (poseData.IsDataValid && poseData.RootScale <= 0.0f)
|
||||
{
|
||||
if (_lastHandScale <= 0.0f)
|
||||
{
|
||||
poseData.IsDataValid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
poseData.RootScale = _lastHandScale;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastHandScale = poseData.RootScale;
|
||||
}
|
||||
|
||||
if (poseData.IsDataValid && _handDataAsset.IsConnected)
|
||||
{
|
||||
UpdateDataPoses(poseData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// revert state fields to their defaults
|
||||
_handDataAsset.IsConnected = default;
|
||||
_handDataAsset.IsTracked = default;
|
||||
_handDataAsset.RootPoseOrigin = default;
|
||||
_handDataAsset.PointerPoseOrigin = default;
|
||||
_handDataAsset.IsHighConfidence = default;
|
||||
for (var fingerIdx = 0; fingerIdx < Constants.NUM_FINGERS; fingerIdx++)
|
||||
{
|
||||
_handDataAsset.IsFingerPinching[fingerIdx] = default;
|
||||
_handDataAsset.IsFingerHighConfidence[fingerIdx] = default;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDataPoses(SkeletonPoseData poseData)
|
||||
{
|
||||
_handDataAsset.HandScale = poseData.RootScale;
|
||||
_handDataAsset.IsTracked = _ovrHand.IsTracked;
|
||||
_handDataAsset.IsHighConfidence = poseData.IsDataHighConfidence;
|
||||
_handDataAsset.IsDominantHand = _ovrHand.IsDominantHand;
|
||||
_handDataAsset.RootPoseOrigin = _handDataAsset.IsTracked
|
||||
? PoseOrigin.RawTrackedPose
|
||||
: PoseOrigin.None;
|
||||
|
||||
for (var fingerIdx = 0; fingerIdx < Constants.NUM_FINGERS; fingerIdx++)
|
||||
{
|
||||
var ovrFingerIdx = (OVRHand.HandFinger)fingerIdx;
|
||||
bool isPinching = _ovrHand.GetFingerIsPinching(ovrFingerIdx);
|
||||
_handDataAsset.IsFingerPinching[fingerIdx] = isPinching;
|
||||
|
||||
bool isHighConfidence =
|
||||
_ovrHand.GetFingerConfidence(ovrFingerIdx) == OVRHand.TrackingConfidence.High;
|
||||
_handDataAsset.IsFingerHighConfidence[fingerIdx] = isHighConfidence;
|
||||
|
||||
float fingerPinchStrength = _ovrHand.GetFingerPinchStrength(ovrFingerIdx);
|
||||
_handDataAsset.FingerPinchStrength[fingerIdx] = fingerPinchStrength;
|
||||
}
|
||||
|
||||
// Read the poses directly from the poseData, so it isn't in conflict with
|
||||
// any modifications that the application makes to OVRSkeleton
|
||||
_handDataAsset.Root = new Pose()
|
||||
{
|
||||
position = poseData.RootPose.Position.FromFlippedZVector3f(),
|
||||
rotation = poseData.RootPose.Orientation.FromFlippedZQuatf()
|
||||
};
|
||||
|
||||
if (_ovrHand.IsPointerPoseValid)
|
||||
{
|
||||
_handDataAsset.PointerPoseOrigin = PoseOrigin.RawTrackedPose;
|
||||
_handDataAsset.PointerPose = new Pose(_ovrHand.PointerPose.localPosition,
|
||||
_ovrHand.PointerPose.localRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
_handDataAsset.PointerPoseOrigin = PoseOrigin.None;
|
||||
}
|
||||
|
||||
// Hand joint rotations X axis needs flipping to get to Unity's coordinate system.
|
||||
var bones = poseData.BoneRotations;
|
||||
for (int i = 0; i < bones.Length; i++)
|
||||
{
|
||||
// When using Link in the Unity Editor, the first frame of hand data
|
||||
// sometimes contains bad joint data.
|
||||
_handDataAsset.Joints[i] = float.IsNaN(bones[i].w)
|
||||
? Config.HandSkeleton.joints[i].pose.rotation
|
||||
: bones[i].FromFlippedXQuatf();
|
||||
}
|
||||
|
||||
_handDataAsset.Joints[0] = WristFixupRotation;
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllFromOVRHandDataSource(UpdateModeFlags updateMode, IDataSource updateAfter,
|
||||
Handedness handedness, ITrackingToWorldTransformer trackingToWorldTransformer,
|
||||
IHandSkeletonProvider handSkeletonProvider, IDataSource<HmdDataAsset, HmdDataSourceConfig> hmdData)
|
||||
{
|
||||
base.InjectAllDataSource(updateMode, updateAfter);
|
||||
InjectHandedness(handedness);
|
||||
InjectTrackingToWorldTransformer(trackingToWorldTransformer);
|
||||
InjectHandSkeletonProvider(handSkeletonProvider);
|
||||
InjectHmdData(hmdData);
|
||||
}
|
||||
|
||||
public void InjectHandedness(Handedness handedness)
|
||||
{
|
||||
_handedness = handedness;
|
||||
}
|
||||
|
||||
public void InjectTrackingToWorldTransformer(ITrackingToWorldTransformer trackingToWorldTransformer)
|
||||
{
|
||||
_trackingToWorldTransformer = trackingToWorldTransformer as MonoBehaviour;
|
||||
TrackingToWorldTransformer = trackingToWorldTransformer;
|
||||
}
|
||||
|
||||
public void InjectHandSkeletonProvider(IHandSkeletonProvider handSkeletonProvider)
|
||||
{
|
||||
_handSkeletonProvider = handSkeletonProvider as MonoBehaviour;
|
||||
HandSkeletonProvider = handSkeletonProvider;
|
||||
}
|
||||
|
||||
public void InjectHmdData(IDataSource<HmdDataAsset,HmdDataSourceConfig> hmdData)
|
||||
{
|
||||
_hmdData = hmdData as MonoBehaviour;
|
||||
HmdData = hmdData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4855895ba1c44959a306beb7ae318fc2
|
||||
timeCreated: 1630528577
|
||||
@@ -0,0 +1,157 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.XR;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
public class FromOVRHmdDataSource : DataSource<HmdDataAsset, HmdDataSourceConfig>
|
||||
{
|
||||
[Header("OVR Data Source")]
|
||||
[SerializeField, Interface(typeof(IOVRCameraRigRef))]
|
||||
private MonoBehaviour _cameraRigRef;
|
||||
|
||||
public IOVRCameraRigRef CameraRigRef { get; private set; }
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("If true, uses OVRManager.headPoseRelativeOffset rather than sensor data for " +
|
||||
"HMD pose.")]
|
||||
private bool _useOvrManagerEmulatedPose = false;
|
||||
|
||||
[Header("Shared Configuration")]
|
||||
[SerializeField, Interface(typeof(ITrackingToWorldTransformer))]
|
||||
private MonoBehaviour _trackingToWorldTransformer;
|
||||
private ITrackingToWorldTransformer TrackingToWorldTransformer;
|
||||
|
||||
private HmdDataAsset _hmdDataAsset = new HmdDataAsset();
|
||||
private HmdDataSourceConfig _config;
|
||||
|
||||
protected void Awake()
|
||||
{
|
||||
CameraRigRef = _cameraRigRef as IOVRCameraRigRef;
|
||||
TrackingToWorldTransformer = _trackingToWorldTransformer as ITrackingToWorldTransformer;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
Assert.IsNotNull(CameraRigRef);
|
||||
Assert.IsNotNull(TrackingToWorldTransformer);
|
||||
InitConfig();
|
||||
}
|
||||
private void InitConfig()
|
||||
{
|
||||
if (_config != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_config = new HmdDataSourceConfig()
|
||||
{
|
||||
TrackingToWorldTransformer = TrackingToWorldTransformer
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateData()
|
||||
{
|
||||
bool hmdPresent = OVRNodeStateProperties.IsHmdPresent();
|
||||
ref var centerEyePose = ref _hmdDataAsset.Root;
|
||||
if (_useOvrManagerEmulatedPose)
|
||||
{
|
||||
Quaternion emulatedRotation = Quaternion.Euler(
|
||||
-OVRManager.instance.headPoseRelativeOffsetRotation.x,
|
||||
-OVRManager.instance.headPoseRelativeOffsetRotation.y,
|
||||
OVRManager.instance.headPoseRelativeOffsetRotation.z);
|
||||
centerEyePose.rotation = emulatedRotation;
|
||||
centerEyePose.position = OVRManager.instance.headPoseRelativeOffsetTranslation;
|
||||
hmdPresent = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var previousEyePose = new Pose();
|
||||
|
||||
if (_hmdDataAsset.IsTracked)
|
||||
{
|
||||
previousEyePose = _hmdDataAsset.Root;
|
||||
}
|
||||
|
||||
if (hmdPresent)
|
||||
{
|
||||
// These are already in Unity's coordinate system (LHS)
|
||||
if (!OVRNodeStateProperties.GetNodeStatePropertyVector3(XRNode.CenterEye,
|
||||
NodeStatePropertyType.Position, OVRPlugin.Node.EyeCenter,
|
||||
OVRPlugin.Step.Render, out centerEyePose.position))
|
||||
{
|
||||
centerEyePose.position = previousEyePose.position;
|
||||
}
|
||||
|
||||
if (!OVRNodeStateProperties.GetNodeStatePropertyQuaternion(XRNode.CenterEye,
|
||||
NodeStatePropertyType.Orientation, OVRPlugin.Node.EyeCenter,
|
||||
OVRPlugin.Step.Render, out centerEyePose.rotation))
|
||||
{
|
||||
centerEyePose.rotation = previousEyePose.rotation;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
centerEyePose = previousEyePose;
|
||||
}
|
||||
}
|
||||
|
||||
_hmdDataAsset.IsTracked = hmdPresent;
|
||||
_hmdDataAsset.FrameId = Time.frameCount;
|
||||
}
|
||||
|
||||
protected override HmdDataAsset DataAsset => _hmdDataAsset;
|
||||
|
||||
// It is important that this creates an object on the fly, as it is possible it is called
|
||||
// from other components Awake methods.
|
||||
public override HmdDataSourceConfig Config
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
InitConfig();
|
||||
}
|
||||
|
||||
return _config;
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllFromOVRHmdDataSource(UpdateModeFlags updateMode, IDataSource updateAfter,
|
||||
bool useOvrManagerEmulatedPose, ITrackingToWorldTransformer trackingToWorldTransformer)
|
||||
{
|
||||
base.InjectAllDataSource(updateMode, updateAfter);
|
||||
InjectUseOvrManagerEmulatedPose(useOvrManagerEmulatedPose);
|
||||
InjectTrackingToWorldTransformer(trackingToWorldTransformer);
|
||||
}
|
||||
|
||||
public void InjectUseOvrManagerEmulatedPose(bool useOvrManagerEmulatedPose)
|
||||
{
|
||||
_useOvrManagerEmulatedPose = useOvrManagerEmulatedPose;
|
||||
}
|
||||
|
||||
public void InjectTrackingToWorldTransformer(ITrackingToWorldTransformer trackingToWorldTransformer)
|
||||
{
|
||||
_trackingToWorldTransformer = trackingToWorldTransformer as MonoBehaviour;
|
||||
TrackingToWorldTransformer = trackingToWorldTransformer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f273abee0604ad2409c9bbc9a60e461e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,188 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
public class HandPhysicsCapsules : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private HandVisual _handVisual;
|
||||
|
||||
private GameObject _capsulesGO;
|
||||
private List<BoneCapsule> _capsules;
|
||||
public IList<BoneCapsule> Capsules { get; private set; }
|
||||
private OVRPlugin.Skeleton2 _skeleton;
|
||||
private bool _capsulesAreActive;
|
||||
protected bool _started;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Assert.IsNotNull(_handVisual);
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
|
||||
_skeleton = _handVisual.Hand.Handedness == Handedness.Left
|
||||
? OVRSkeletonData.LeftSkeleton
|
||||
: OVRSkeletonData.RightSkeleton;
|
||||
_capsulesGO = new GameObject("Capsules");
|
||||
_capsulesGO.transform.SetParent(transform, false);
|
||||
_capsulesGO.transform.localPosition = Vector3.zero;
|
||||
_capsulesGO.transform.localRotation = Quaternion.identity;
|
||||
|
||||
_capsules = new List<BoneCapsule>(new BoneCapsule[_skeleton.NumBoneCapsules]);
|
||||
Capsules = _capsules.AsReadOnly();
|
||||
|
||||
for (int i = 0; i < _capsules.Count; ++i)
|
||||
{
|
||||
Transform boneTransform = _handVisual.Joints[_skeleton.BoneCapsules[i].BoneIndex];
|
||||
BoneCapsule capsule = new BoneCapsule();
|
||||
_capsules[i] = capsule;
|
||||
|
||||
capsule.BoneIndex = _skeleton.BoneCapsules[i].BoneIndex;
|
||||
|
||||
capsule.CapsuleRigidbody = new GameObject((boneTransform.name).ToString() + "_CapsuleRigidbody")
|
||||
.AddComponent<Rigidbody>();
|
||||
capsule.CapsuleRigidbody.mass = 1.0f;
|
||||
capsule.CapsuleRigidbody.isKinematic = true;
|
||||
capsule.CapsuleRigidbody.useGravity = false;
|
||||
capsule.CapsuleRigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
|
||||
|
||||
GameObject rbGO = capsule.CapsuleRigidbody.gameObject;
|
||||
rbGO.transform.SetParent(_capsulesGO.transform, false);
|
||||
rbGO.transform.position = boneTransform.position;
|
||||
rbGO.transform.rotation = boneTransform.rotation;
|
||||
rbGO.SetActive(false);
|
||||
|
||||
capsule.CapsuleCollider = new GameObject((boneTransform.name).ToString() + "_CapsuleCollider")
|
||||
.AddComponent<CapsuleCollider>();
|
||||
capsule.CapsuleCollider.isTrigger = false;
|
||||
|
||||
var p0 = _skeleton.BoneCapsules[i].StartPoint.FromFlippedXVector3f();
|
||||
var p1 = _skeleton.BoneCapsules[i].EndPoint.FromFlippedXVector3f();
|
||||
var delta = p1 - p0;
|
||||
var mag = delta.magnitude;
|
||||
var rot = Quaternion.FromToRotation(Vector3.right, delta);
|
||||
capsule.CapsuleCollider.radius = _skeleton.BoneCapsules[i].Radius;
|
||||
capsule.CapsuleCollider.height = mag + _skeleton.BoneCapsules[i].Radius * 2.0f;
|
||||
capsule.CapsuleCollider.direction = 0;
|
||||
capsule.CapsuleCollider.center = Vector3.right * mag * 0.5f;
|
||||
|
||||
GameObject ccGO = capsule.CapsuleCollider.gameObject;
|
||||
ccGO.transform.SetParent(rbGO.transform, false);
|
||||
ccGO.transform.localPosition = p0;
|
||||
ccGO.transform.localRotation = rot;
|
||||
}
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
_handVisual.WhenHandVisualUpdated += HandleHandVisualUpdated;
|
||||
}
|
||||
}
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
_handVisual.WhenHandVisualUpdated -= HandleHandVisualUpdated;
|
||||
|
||||
if (_capsules != null)
|
||||
{
|
||||
for (int i = 0; i < _capsules.Count; ++i)
|
||||
{
|
||||
var capsuleGO = _capsules[i].CapsuleRigidbody.gameObject;
|
||||
capsuleGO.SetActive(false);
|
||||
}
|
||||
_capsulesAreActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void FixedUpdate()
|
||||
{
|
||||
if (_capsulesAreActive && !_handVisual.IsVisible)
|
||||
{
|
||||
for (int i = 0; i < _capsules.Count; ++i)
|
||||
{
|
||||
var capsuleGO = _capsules[i].CapsuleRigidbody.gameObject;
|
||||
capsuleGO.SetActive(false);
|
||||
}
|
||||
_capsulesAreActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleHandVisualUpdated()
|
||||
{
|
||||
_capsulesAreActive = _handVisual.IsVisible;
|
||||
|
||||
for (int i = 0; i < _capsules.Count; ++i)
|
||||
{
|
||||
BoneCapsule capsule = _capsules[i];
|
||||
var capsuleGO = capsule.CapsuleRigidbody.gameObject;
|
||||
|
||||
if (_capsulesAreActive)
|
||||
{
|
||||
Transform boneTransform = _handVisual.Joints[(int)capsule.BoneIndex];
|
||||
|
||||
if (capsuleGO.activeSelf)
|
||||
{
|
||||
capsule.CapsuleRigidbody.MovePosition(boneTransform.position);
|
||||
capsule.CapsuleRigidbody.MoveRotation(boneTransform.rotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
capsuleGO.SetActive(true);
|
||||
capsule.CapsuleRigidbody.position = boneTransform.position;
|
||||
capsule.CapsuleRigidbody.rotation = boneTransform.rotation;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (capsuleGO.activeSelf)
|
||||
{
|
||||
capsuleGO.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllOVRHandPhysicsCapsules(HandVisual hand)
|
||||
{
|
||||
InjectHandSkeleton(hand);
|
||||
}
|
||||
|
||||
public void InjectHandSkeleton(HandVisual hand)
|
||||
{
|
||||
_handVisual = hand;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class BoneCapsule
|
||||
{
|
||||
public short BoneIndex { get; set; }
|
||||
public Rigidbody CapsuleRigidbody { get; set; }
|
||||
public CapsuleCollider CapsuleCollider { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1e477b940c8a084ab4f2dda703b49ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,69 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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
|
||||
{
|
||||
public class HandSkeletonOVR : MonoBehaviour, IHandSkeletonProvider
|
||||
{
|
||||
private readonly HandSkeleton[] _skeletons = { null, null };
|
||||
|
||||
public HandSkeleton this[Handedness handedness] => _skeletons[(int)handedness];
|
||||
|
||||
protected void Awake()
|
||||
{
|
||||
_skeletons[0] = CreateSkeletonData(Handedness.Left);
|
||||
_skeletons[1] = CreateSkeletonData(Handedness.Right);
|
||||
}
|
||||
|
||||
public static HandSkeleton CreateSkeletonData(Handedness handedness)
|
||||
{
|
||||
HandSkeleton handSkeleton = new HandSkeleton();
|
||||
|
||||
// When running in the editor, the call to load the skeleton from OVRPlugin may fail. Use baked skeleton
|
||||
// data.
|
||||
if (handedness == Handedness.Left)
|
||||
{
|
||||
ApplyToSkeleton(OVRSkeletonData.LeftSkeleton, handSkeleton);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyToSkeleton(OVRSkeletonData.RightSkeleton, handSkeleton);
|
||||
}
|
||||
|
||||
return handSkeleton;
|
||||
}
|
||||
|
||||
private static void ApplyToSkeleton(in OVRPlugin.Skeleton2 ovrSkeleton, HandSkeleton handSkeleton)
|
||||
{
|
||||
int numJoints = handSkeleton.joints.Length;
|
||||
Assert.AreEqual(ovrSkeleton.NumBones, numJoints);
|
||||
|
||||
for (int i = 0; i < numJoints; ++i)
|
||||
{
|
||||
ref var srcPose = ref ovrSkeleton.Bones[i].Pose;
|
||||
handSkeleton.joints[i] = new HandSkeletonJoint()
|
||||
{
|
||||
pose = new Pose()
|
||||
{
|
||||
position = srcPose.Position.FromFlippedXVector3f(),
|
||||
rotation = srcPose.Orientation.FromFlippedXQuatf()
|
||||
},
|
||||
parent = ovrSkeleton.Bones[i].ParentBoneIndex
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86bd0d3098d25db4c9c839739557480b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,138 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
// When it is desired for HandDataAsset to drive OVRSkeleton, the DataSources:
|
||||
// Must execute after OVRHands (so that the modifier stack can read the latest IsTracked value)
|
||||
// Must execute before OVRSkeleton (so that the created IOVRSkeletonDataProvider injected into
|
||||
// OVRSkeleton can read from the updated HandDataAsset)
|
||||
// By deriving from InputDataProviderUpdateTrigger, we ensure that the InputData will be
|
||||
// invalidated at the right time (at priority -85), so that it is recalculated just-in-time
|
||||
// in the skeleton data callbacks (at priority -80)
|
||||
[DefaultExecutionOrder(-85)]
|
||||
public class InputDataProviderUpdateTriggerOVR : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Interface(typeof(IDataSource))]
|
||||
private MonoBehaviour _dataSource;
|
||||
private IDataSource DataSource;
|
||||
|
||||
[SerializeField]
|
||||
private bool _enableUpdate = true;
|
||||
[SerializeField]
|
||||
private bool _enableFixedUpdate = true;
|
||||
|
||||
[SerializeField, Interface(typeof(IOVRCameraRigRef)), Optional]
|
||||
private MonoBehaviour _cameraRigRef;
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
public IOVRCameraRigRef CameraRigRef { get; private set; } = null;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
DataSource = _dataSource as IDataSource;
|
||||
CameraRigRef = _cameraRigRef as IOVRCameraRigRef;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
Assert.IsNotNull(DataSource);
|
||||
if (_cameraRigRef != null)
|
||||
{
|
||||
Assert.IsNotNull(CameraRigRef);
|
||||
}
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
if (CameraRigRef != null)
|
||||
{
|
||||
CameraRigRef.OnAnchorsUpdated += MarkRequiresUpdate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MarkRequiresUpdate()
|
||||
{
|
||||
DataSource.MarkInputDataRequiresUpdate();
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (_enableUpdate)
|
||||
{
|
||||
MarkRequiresUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void FixedUpdate()
|
||||
{
|
||||
if (_enableFixedUpdate)
|
||||
{
|
||||
MarkRequiresUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
if (CameraRigRef != null)
|
||||
{
|
||||
CameraRigRef.OnAnchorsUpdated -= MarkRequiresUpdate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllInputDataProviderUpdateTriggerOVR(IDataSource dataSource, bool enableUpdate, bool enableFixedUpdate)
|
||||
{
|
||||
InjectDataSource(dataSource);
|
||||
InjectEnableUpdate(enableUpdate);
|
||||
InjectEnableFixedUpdate(enableFixedUpdate);
|
||||
}
|
||||
|
||||
public void InjectDataSource(IDataSource dataSource)
|
||||
{
|
||||
_dataSource = dataSource as MonoBehaviour;
|
||||
DataSource = dataSource;
|
||||
}
|
||||
|
||||
public void InjectEnableUpdate(bool enableUpdate)
|
||||
{
|
||||
_enableUpdate = enableUpdate;
|
||||
}
|
||||
|
||||
public void InjectEnableFixedUpdate(bool enableFixedUpdate)
|
||||
{
|
||||
_enableFixedUpdate = enableFixedUpdate;
|
||||
}
|
||||
|
||||
public void InjectOptionalCameraRigRef(IOVRCameraRigRef cameraRigRef)
|
||||
{
|
||||
_cameraRigRef = cameraRigRef as MonoBehaviour;
|
||||
CameraRigRef = cameraRigRef;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7198950508e319a40b9b3ca5c3960e58
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,133 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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
|
||||
{
|
||||
public interface IOVRCameraRigRef
|
||||
{
|
||||
OVRCameraRig CameraRig { get; }
|
||||
/// <summary>
|
||||
/// Returns a valid OVRHand object representing the left hand, if one exists on the
|
||||
/// OVRCameraRig. If none is available, returns null.
|
||||
/// </summary>
|
||||
OVRHand LeftHand { get; }
|
||||
/// <summary>
|
||||
/// Returns a valid OVRHand object representing the right hand, if one exists on the
|
||||
/// OVRCameraRig. If none is available, returns null.
|
||||
/// </summary>
|
||||
OVRHand RightHand { get; }
|
||||
Transform LeftController { get; }
|
||||
Transform RightController { get; }
|
||||
|
||||
event Action OnAnchorsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Points to an OVRCameraRig instance. This level of indirection provides a single
|
||||
/// configuration point on the root of a prefab.
|
||||
/// Must execute before all other OVR related classes so that the fields are
|
||||
/// initialized correctly and ready to use.
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-90)]
|
||||
public class OVRCameraRigRef : MonoBehaviour, IOVRCameraRigRef
|
||||
{
|
||||
[Header("Configuration")]
|
||||
[SerializeField]
|
||||
private OVRCameraRig _ovrCameraRig;
|
||||
|
||||
[SerializeField]
|
||||
private bool _requireOvrHands = true;
|
||||
|
||||
public OVRCameraRig CameraRig => _ovrCameraRig;
|
||||
|
||||
private OVRHand _leftHand;
|
||||
private OVRHand _rightHand;
|
||||
public OVRHand LeftHand => GetHandCached(ref _leftHand, _ovrCameraRig.leftHandAnchor);
|
||||
public OVRHand RightHand => GetHandCached(ref _rightHand, _ovrCameraRig.rightHandAnchor);
|
||||
|
||||
public Transform LeftController => _ovrCameraRig.leftControllerAnchor;
|
||||
public Transform RightController => _ovrCameraRig.rightControllerAnchor;
|
||||
|
||||
public event Action OnAnchorsUpdated = delegate { };
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
Assert.IsNotNull(_ovrCameraRig);
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
_ovrCameraRig.UpdatedAnchors += OnUpdateAnchors;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
_ovrCameraRig.UpdatedAnchors -= OnUpdateAnchors;
|
||||
}
|
||||
}
|
||||
|
||||
private OVRHand GetHandCached(ref OVRHand cachedValue, Transform handAnchor)
|
||||
{
|
||||
if (cachedValue != null)
|
||||
{
|
||||
return cachedValue;
|
||||
}
|
||||
|
||||
cachedValue = handAnchor.GetComponentInChildren<OVRHand>(true);
|
||||
if (_requireOvrHands)
|
||||
{
|
||||
Assert.IsNotNull(cachedValue);
|
||||
}
|
||||
|
||||
return cachedValue;
|
||||
}
|
||||
|
||||
private void OnUpdateAnchors(OVRCameraRig ovrCameraRig)
|
||||
{
|
||||
OnAnchorsUpdated();
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllOVRCameraRigRef(OVRCameraRig ovrCameraRig, bool requireHands)
|
||||
{
|
||||
InjectOVRCameraRig(ovrCameraRig);
|
||||
InjectRequireHands(requireHands);
|
||||
}
|
||||
|
||||
public void InjectOVRCameraRig(OVRCameraRig ovrCameraRig)
|
||||
{
|
||||
_ovrCameraRig = ovrCameraRig;
|
||||
// Clear the cached values to force new values to be read on next access
|
||||
_leftHand = null;
|
||||
_rightHand = null;
|
||||
}
|
||||
|
||||
public void InjectRequireHands(bool requireHands)
|
||||
{
|
||||
_requireOvrHands = requireHands;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7b47e36715521d4e8a30d2c5b6e83e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the active status of an OVRInput device based on whether
|
||||
/// OVRInput's current active controller matches any of the controller
|
||||
/// types set up in the inspector. OVRInput `Controllers` include
|
||||
/// types like Touch, L Touch, R TouchR, Hands, L Hand, R Hand
|
||||
/// </summary>
|
||||
public class OVRInputDeviceActiveState : MonoBehaviour, IActiveState
|
||||
{
|
||||
[SerializeField]
|
||||
private List<OVRInput.Controller> _controllerTypes;
|
||||
|
||||
public bool Active
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (OVRInput.Controller controllerType in _controllerTypes)
|
||||
{
|
||||
if (OVRInput.GetConnectedControllers() == controllerType) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllOVRInputDeviceActiveState(List<OVRInput.Controller> controllerTypes)
|
||||
{
|
||||
InjectControllerTypes(controllerTypes);
|
||||
}
|
||||
|
||||
public void InjectControllerTypes(List<OVRInput.Controller> controllerTypes)
|
||||
{
|
||||
_controllerTypes = controllerTypes;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce6a3535bf452cd42a0b2fb1c571b76e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,171 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 OVRSkeletonData
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// This method can be used to generate a serialized skeleton in C# format.
|
||||
public static string CreateString(OVRPlugin.Skeleton2 skeleton)
|
||||
{
|
||||
string bonesJson = "";
|
||||
for (int i = 0; i < skeleton.NumBones; i++)
|
||||
{
|
||||
var bone = skeleton.Bones[i];
|
||||
bonesJson +=
|
||||
"new OVRPlugin.Bone() { " +
|
||||
"Id=OVRPlugin.BoneId." + bone.Id.ToString() + ", " +
|
||||
"ParentBoneIndex=" + bone.ParentBoneIndex + ", " +
|
||||
"Pose=new OVRPlugin.Posef() { " +
|
||||
"Position=new OVRPlugin.Vector3f() {x=" + bone.Pose.Position.x + "f,y=" + bone.Pose.Position.y + "f,z=" + bone.Pose.Position.z + "f}, " +
|
||||
"Orientation=new OVRPlugin.Quatf(){x=" + bone.Pose.Orientation.x + "f,y=" + bone.Pose.Orientation.y + "f,z=" + bone.Pose.Orientation.z + "f,w=" + bone.Pose.Orientation.w + "f}}}";
|
||||
if (i != skeleton.Bones.Length - 1)
|
||||
{
|
||||
bonesJson += ",";
|
||||
}
|
||||
bonesJson += "\n";
|
||||
}
|
||||
|
||||
string boneCapsulesJson = "";
|
||||
for (int i = 0; i < skeleton.NumBoneCapsules; i++)
|
||||
{
|
||||
var cap = skeleton.BoneCapsules[i];
|
||||
boneCapsulesJson += "new OVRPlugin.BoneCapsule() { BoneIndex=" + cap.BoneIndex + ", Radius=" + cap.Radius + "f, " +
|
||||
"StartPoint=new OVRPlugin.Vector3f() {x=" + cap.StartPoint.x + "f,y=" + cap.StartPoint.y + "f,z=" + cap.StartPoint.z + "f}, " +
|
||||
"EndPoint=new OVRPlugin.Vector3f() {x=" + cap.EndPoint.x + "f,y=" + cap.EndPoint.y + "f,z=" + cap.EndPoint.z + "f}}";
|
||||
if (i != skeleton.Bones.Length - 1)
|
||||
{
|
||||
boneCapsulesJson += ",";
|
||||
}
|
||||
boneCapsulesJson += "\n";
|
||||
}
|
||||
|
||||
return "new OVRPlugin.Skeleton2() { " +
|
||||
"Type=OVRPlugin.SkeletonType." + skeleton.Type.ToString() + ", " +
|
||||
"NumBones=" + skeleton.NumBones + ", " +
|
||||
"NumBoneCapsules=" + skeleton.NumBoneCapsules + ", " +
|
||||
"Bones=new OVRPlugin.Bone[] {" + bonesJson + "}, " +
|
||||
"BoneCapsules=new OVRPlugin.BoneCapsule[] {" + boneCapsulesJson + "}" +
|
||||
"};";
|
||||
}
|
||||
#endif
|
||||
|
||||
public static readonly OVRPlugin.Skeleton2 LeftSkeleton = new OVRPlugin.Skeleton2()
|
||||
{
|
||||
Type = OVRPlugin.SkeletonType.HandLeft,
|
||||
NumBones = 24,
|
||||
NumBoneCapsules = 19,
|
||||
Bones = new OVRPlugin.Bone[] {
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Start, ParentBoneIndex=-1, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0f,y=0f,z=0f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_ForearmStub, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0f,y=0f,z=0f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Thumb0, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.0200693f,y=0.0115541f,z=-0.01049652f}, Orientation=new OVRPlugin.Quatf(){x=0.3753869f,y=0.4245841f,z=-0.007778856f,w=0.8238644f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Thumb1, ParentBoneIndex=2, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.02485256f,y=-9.31E-10f,z=-1.863E-09f}, Orientation=new OVRPlugin.Quatf(){x=0.2602303f,y=0.02433088f,z=0.125678f,w=0.9570231f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Thumb2, ParentBoneIndex=3, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.03251291f,y=5.82E-10f,z=1.863E-09f}, Orientation=new OVRPlugin.Quatf(){x=-0.08270377f,y=-0.0769617f,z=-0.08406223f,w=0.9900357f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Thumb3, ParentBoneIndex=4, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.0337931f,y=3.26E-09f,z=1.863E-09f}, Orientation=new OVRPlugin.Quatf(){x=0.08350593f,y=0.06501573f,z=-0.05827406f,w=0.9926752f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Index1, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.09599624f,y=0.007316455f,z=-0.02355068f}, Orientation=new OVRPlugin.Quatf(){x=0.03068309f,y=-0.01885559f,z=0.04328144f,w=0.9984136f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Index2, ParentBoneIndex=6, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.0379273f,y=-5.82E-10f,z=-5.97E-10f}, Orientation=new OVRPlugin.Quatf(){x=-0.02585241f,y=-0.007116061f,z=0.003292944f,w=0.999635f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Index3, ParentBoneIndex=7, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.02430365f,y=-6.73E-10f,z=-6.75E-10f}, Orientation=new OVRPlugin.Quatf(){x=-0.016056f,y=-0.02714872f,z=-0.072034f,w=0.9969034f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Middle1, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.09564661f,y=0.002543155f,z=-0.001725906f}, Orientation=new OVRPlugin.Quatf(){x=-0.009066326f,y=-0.05146559f,z=0.05183575f,w=0.9972874f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Middle2, ParentBoneIndex=9, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.042927f,y=-8.51E-10f,z=-1.193E-09f}, Orientation=new OVRPlugin.Quatf(){x=-0.01122823f,y=-0.004378874f,z=-0.001978267f,w=0.9999254f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Middle3, ParentBoneIndex=10, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.02754958f,y=3.09E-10f,z=1.128E-09f}, Orientation=new OVRPlugin.Quatf(){x=-0.03431955f,y=-0.004611839f,z=-0.09300701f,w=0.9950631f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Ring1, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.0886938f,y=0.006529308f,z=0.01746524f}, Orientation=new OVRPlugin.Quatf(){x=-0.05315936f,y=-0.1231034f,z=0.04981349f,w=0.9897162f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Ring2, ParentBoneIndex=12, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.0389961f,y=0f,z=5.24E-10f}, Orientation=new OVRPlugin.Quatf(){x=-0.03363252f,y=-0.00278984f,z=0.00567602f,w=0.9994143f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Ring3, ParentBoneIndex=13, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.02657339f,y=1.281E-09f,z=1.63E-09f}, Orientation=new OVRPlugin.Quatf(){x=-0.003477462f,y=0.02917945f,z=-0.02502854f,w=0.9992548f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Pinky0, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.03407356f,y=0.009419836f,z=0.02299858f}, Orientation=new OVRPlugin.Quatf(){x=-0.207036f,y=-0.1403428f,z=0.0183118f,w=0.9680417f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Pinky1, ParentBoneIndex=15, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.04565055f,y=9.97679E-07f,z=-2.193963E-06f}, Orientation=new OVRPlugin.Quatf(){x=0.09111304f,y=0.00407137f,z=0.02812923f,w=0.9954349f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Pinky2, ParentBoneIndex=16, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.03072042f,y=1.048E-09f,z=-1.75E-10f}, Orientation=new OVRPlugin.Quatf(){x=-0.03761665f,y=-0.04293772f,z=-0.01328605f,w=0.9982809f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Pinky3, ParentBoneIndex=17, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.02031138f,y=-2.91E-10f,z=9.31E-10f}, Orientation=new OVRPlugin.Quatf(){x=0.0006447434f,y=0.04917067f,z=-0.02401883f,w=0.9985014f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_ThumbTip, ParentBoneIndex=5, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.02459077f,y=-0.001026974f,z=0.0006703701f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_IndexTip, ParentBoneIndex=8, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.02236338f,y=-0.00102507f,z=0.0002956076f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_MiddleTip, ParentBoneIndex=11, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.02496492f,y=-0.001137299f,z=0.0003086528f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_RingTip, ParentBoneIndex=14, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.02432613f,y=-0.001608172f,z=0.000257905f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_PinkyTip, ParentBoneIndex=18, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0.02192238f,y=-0.001216086f,z=-0.0002464796f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
},
|
||||
BoneCapsules = new OVRPlugin.BoneCapsule[] {
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=0, Radius=0.01822828f, StartPoint=new OVRPlugin.Vector3f() {x=0.02755879f,y=0.01404149f,z=-0.01685145f}, EndPoint=new OVRPlugin.Vector3f() {x=0.07794081f,y=0.009090679f,z=-0.02178327f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=0, Radius=0.02323196f, StartPoint=new OVRPlugin.Vector3f() {x=0.02632602f,y=0.008661013f,z=-0.006531342f}, EndPoint=new OVRPlugin.Vector3f() {x=0.07255958f,y=0.004580691f,z=-0.003326343f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=0, Radius=0.01608828f, StartPoint=new OVRPlugin.Vector3f() {x=0.0297035f,y=0.00920606f,z=0.01111641f}, EndPoint=new OVRPlugin.Vector3f() {x=0.07271415f,y=0.007254403f,z=0.01574543f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=0, Radius=0.02346085f, StartPoint=new OVRPlugin.Vector3f() {x=0.02844799f,y=0.008827154f,z=0.01446979f}, EndPoint=new OVRPlugin.Vector3f() {x=0.06036391f,y=0.009573798f,z=0.02133043f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=3, Radius=0.01838252f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=2.561E-09f,z=1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.03251291f,y=6.98E-10f,z=-3.492E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=4, Radius=0.01028295f, StartPoint=new OVRPlugin.Vector3f() {x=7.451E-09f,y=2.794E-09f,z=-3.725E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.03379309f,y=6.519E-09f,z=-8.382E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=5, Radius=0.009768805f, StartPoint=new OVRPlugin.Vector3f() {x=7.451E-09f,y=5.588E-09f,z=-4.657E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.01500075f,y=-0.0006525163f,z=0.0005929575f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=6, Radius=0.01029526f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=0f,z=-1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.03792731f,y=4.66E-10f,z=-3.725E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=7, Radius=0.008038102f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=-9.31E-10f,z=-1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.02430364f,y=-1.863E-09f,z=-3.725E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=8, Radius=0.007636196f, StartPoint=new OVRPlugin.Vector3f() {x=-1.4901E-08f,y=-1.863E-09f,z=0f}, EndPoint=new OVRPlugin.Vector3f() {x=0.01507758f,y=-0.0005028695f,z=6.049499E-05f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=9, Radius=0.01117394f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=-4.66E-10f,z=9.31E-10f}, EndPoint=new OVRPlugin.Vector3f() {x=0.042927f,y=-2.328E-09f,z=-9.31E-10f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=10, Radius=0.008030958f, StartPoint=new OVRPlugin.Vector3f() {x=1.4901E-08f,y=-4.66E-10f,z=0f}, EndPoint=new OVRPlugin.Vector3f() {x=0.02754962f,y=-4.66E-10f,z=-1.863E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=11, Radius=0.007629411f, StartPoint=new OVRPlugin.Vector3f() {x=1.4901E-08f,y=-3.725E-09f,z=0f}, EndPoint=new OVRPlugin.Vector3f() {x=0.01719159f,y=-0.0007450115f,z=0.0004036371f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=12, Radius=0.009922137f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=2.33E-10f,z=2.328E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.03899612f,y=0f,z=4.66E-10f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=13, Radius=0.007611672f, StartPoint=new OVRPlugin.Vector3f() {x=1.4901E-08f,y=-4.66E-10f,z=1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.02657339f,y=1.397E-09f,z=0f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=14, Radius=0.007231089f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=9.31E-10f,z=2.328E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.01632451f,y=-0.001288094f,z=0.0001235888f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=16, Radius=0.008483353f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=-2.33E-10f,z=1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.03072041f,y=-1.164E-09f,z=0f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=17, Radius=0.006764194f, StartPoint=new OVRPlugin.Vector3f() {x=-7.451E-09f,y=-1.717E-09f,z=1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.02031137f,y=1.46E-10f,z=1.863E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=18, Radius=0.006425985f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=0f,z=-1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=0.01507002f,y=-0.0006056242f,z=-2.491474E-05f}},
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly OVRPlugin.Skeleton2 RightSkeleton = new OVRPlugin.Skeleton2()
|
||||
{
|
||||
Type = OVRPlugin.SkeletonType.HandRight,
|
||||
NumBones = 24,
|
||||
NumBoneCapsules = 19,
|
||||
Bones = new OVRPlugin.Bone[] {new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Start, ParentBoneIndex=-1, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0f,y=0f,z=0f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_ForearmStub, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=0f,y=0f,z=0f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Thumb0, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.0200693f,y=-0.0115541f,z=0.01049652f}, Orientation=new OVRPlugin.Quatf(){x=0.3753869f,y=0.4245841f,z=-0.007778856f,w=0.8238644f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Thumb1, ParentBoneIndex=2, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.02485256f,y=2.328E-09f,z=0f}, Orientation=new OVRPlugin.Quatf(){x=0.2602303f,y=0.02433088f,z=0.125678f,w=0.9570231f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Thumb2, ParentBoneIndex=3, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.03251291f,y=-1.16E-10f,z=0f}, Orientation=new OVRPlugin.Quatf(){x=-0.08270377f,y=-0.0769617f,z=-0.08406223f,w=0.9900357f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Thumb3, ParentBoneIndex=4, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.0337931f,y=-3.26E-09f,z=-1.863E-09f}, Orientation=new OVRPlugin.Quatf(){x=0.08350593f,y=0.06501573f,z=-0.05827406f,w=0.9926752f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Index1, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.09599624f,y=-0.007316455f,z=0.02355068f}, Orientation=new OVRPlugin.Quatf(){x=0.03068309f,y=-0.01885559f,z=0.04328144f,w=0.9984136f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Index2, ParentBoneIndex=6, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.0379273f,y=1.16E-10f,z=5.97E-10f}, Orientation=new OVRPlugin.Quatf(){x=-0.02585241f,y=-0.007116061f,z=0.003292944f,w=0.999635f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Index3, ParentBoneIndex=7, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.02430365f,y=6.73E-10f,z=6.75E-10f}, Orientation=new OVRPlugin.Quatf(){x=-0.016056f,y=-0.02714872f,z=-0.072034f,w=0.9969034f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Middle1, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.09564661f,y=-0.002543155f,z=0.001725906f}, Orientation=new OVRPlugin.Quatf(){x=-0.009066326f,y=-0.05146559f,z=0.05183575f,w=0.9972874f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Middle2, ParentBoneIndex=9, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.042927f,y=1.317E-09f,z=1.193E-09f}, Orientation=new OVRPlugin.Quatf(){x=-0.01122823f,y=-0.004378874f,z=-0.001978267f,w=0.9999254f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Middle3, ParentBoneIndex=10, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.02754958f,y=-7.71E-10f,z=-1.12E-09f}, Orientation=new OVRPlugin.Quatf(){x=-0.03431955f,y=-0.004611839f,z=-0.09300701f,w=0.9950631f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Ring1, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.0886938f,y=-0.006529307f,z=-0.01746524f}, Orientation=new OVRPlugin.Quatf(){x=-0.05315936f,y=-0.1231034f,z=0.04981349f,w=0.9897162f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Ring2, ParentBoneIndex=12, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.0389961f,y=-4.66E-10f,z=-5.24E-10f}, Orientation=new OVRPlugin.Quatf(){x=-0.03363252f,y=-0.00278984f,z=0.00567602f,w=0.9994143f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Ring3, ParentBoneIndex=13, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.02657339f,y=-1.281E-09f,z=-1.63E-09f}, Orientation=new OVRPlugin.Quatf(){x=-0.003477462f,y=0.02917945f,z=-0.02502854f,w=0.9992548f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Pinky0, ParentBoneIndex=0, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.03407356f,y=-0.009419835f,z=-0.02299858f}, Orientation=new OVRPlugin.Quatf(){x=-0.207036f,y=-0.1403428f,z=0.0183118f,w=0.9680417f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Pinky1, ParentBoneIndex=15, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.04565055f,y=-9.98611E-07f,z=2.193963E-06f}, Orientation=new OVRPlugin.Quatf(){x=0.09111304f,y=0.00407137f,z=0.02812923f,w=0.9954349f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Pinky2, ParentBoneIndex=16, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.03072042f,y=6.98E-10f,z=1.106E-09f}, Orientation=new OVRPlugin.Quatf(){x=-0.03761665f,y=-0.04293772f,z=-0.01328605f,w=0.9982809f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_Pinky3, ParentBoneIndex=17, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.02031138f,y=-1.455E-09f,z=-1.397E-09f}, Orientation=new OVRPlugin.Quatf(){x=0.0006447434f,y=0.04917067f,z=-0.02401883f,w=0.9985014f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_ThumbTip, ParentBoneIndex=5, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.02459077f,y=0.001026974f,z=-0.0006703701f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_IndexTip, ParentBoneIndex=8, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.02236338f,y=0.00102507f,z=-0.0002956076f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_MiddleTip, ParentBoneIndex=11, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.02496492f,y=0.001137299f,z=-0.0003086528f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_RingTip, ParentBoneIndex=14, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.02432613f,y=0.001608172f,z=-0.000257905f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
new OVRPlugin.Bone() { Id=OVRPlugin.BoneId.Hand_PinkyTip, ParentBoneIndex=18, Pose=new OVRPlugin.Posef() { Position=new OVRPlugin.Vector3f() {x=-0.02192238f,y=0.001216086f,z=0.0002464796f}, Orientation=new OVRPlugin.Quatf(){x=0f,y=0f,z=0f,w=1f}}},
|
||||
},
|
||||
BoneCapsules = new OVRPlugin.BoneCapsule[] {new OVRPlugin.BoneCapsule() { BoneIndex=0, Radius=0.01822828f, StartPoint=new OVRPlugin.Vector3f() {x=-0.02755879f,y=-0.01404148f,z=0.01685145f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.07794081f,y=-0.009090678f,z=0.02178326f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=0, Radius=0.02323196f, StartPoint=new OVRPlugin.Vector3f() {x=-0.02632602f,y=-0.008661013f,z=0.006531343f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.07255958f,y=-0.004580691f,z=0.003326343f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=0, Radius=0.01608828f, StartPoint=new OVRPlugin.Vector3f() {x=-0.0297035f,y=-0.00920606f,z=-0.01111641f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.07271415f,y=-0.007254403f,z=-0.01574543f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=0, Radius=0.02346085f, StartPoint=new OVRPlugin.Vector3f() {x=-0.02844799f,y=-0.008827153f,z=-0.01446979f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.06036392f,y=-0.009573797f,z=-0.02133043f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=3, Radius=0.01838251f, StartPoint=new OVRPlugin.Vector3f() {x=3.725E-09f,y=-6.98E-10f,z=-2.794E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.03251291f,y=-6.98E-10f,z=2.561E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=4, Radius=0.01028296f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=-9.31E-10f,z=5.588E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.03379308f,y=-4.657E-09f,z=1.0245E-08f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=5, Radius=0.009768807f, StartPoint=new OVRPlugin.Vector3f() {x=-7.451E-09f,y=1.863E-09f,z=8.382E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.0150008f,y=0.0006525647f,z=-0.000592957f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=6, Radius=0.01029526f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=4.66E-10f,z=1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.03792731f,y=-4.66E-10f,z=3.725E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=7, Radius=0.008038101f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=9.31E-10f,z=1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.02430364f,y=1.863E-09f,z=3.725E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=8, Radius=0.007636196f, StartPoint=new OVRPlugin.Vector3f() {x=1.4901E-08f,y=1.863E-09f,z=0f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.01507759f,y=0.0005028695f,z=-6.052852E-05f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=9, Radius=0.01117394f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=0f,z=-9.31E-10f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.042927f,y=1.863E-09f,z=9.31E-10f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=10, Radius=0.008030958f, StartPoint=new OVRPlugin.Vector3f() {x=-1.4901E-08f,y=0f,z=0f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.02754962f,y=4.66E-10f,z=1.863E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=11, Radius=0.00762941f, StartPoint=new OVRPlugin.Vector3f() {x=-1.4901E-08f,y=1.863E-09f,z=0f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.01719156f,y=0.0007450022f,z=-0.0004036473f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=12, Radius=0.009922139f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=-2.33E-10f,z=-2.328E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.03899612f,y=4.66E-10f,z=-4.66E-10f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=13, Radius=0.007611674f, StartPoint=new OVRPlugin.Vector3f() {x=-1.4901E-08f,y=1.863E-09f,z=-1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.02657339f,y=0f,z=0f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=14, Radius=0.00723109f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=9.31E-10f,z=-2.328E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.01632455f,y=0.001288087f,z=-0.0001235851f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=16, Radius=0.008483353f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=2.33E-10f,z=-1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.03072041f,y=1.164E-09f,z=0f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=17, Radius=0.006764191f, StartPoint=new OVRPlugin.Vector3f() {x=7.451E-09f,y=1.717E-09f,z=1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.02031137f,y=-1.46E-10f,z=-1.863E-09f}},
|
||||
new OVRPlugin.BoneCapsule() { BoneIndex=18, Radius=0.006425982f, StartPoint=new OVRPlugin.Vector3f() {x=0f,y=0f,z=1.863E-09f}, EndPoint=new OVRPlugin.Vector3f() {x=-0.01507004f,y=0.0006056186f,z=2.490915E-05f}},
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfb63309ade4ca04dbff7beff6784781
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,690 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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;
|
||||
using UnityEngine.Serialization;
|
||||
using static OVRMesh;
|
||||
using static OVRMeshRenderer;
|
||||
using static OVRSkeleton;
|
||||
using static OVRSkeletonRenderer;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
public delegate HandDataAsset HandInputDataReadFunction();
|
||||
|
||||
internal class OVRInputHandComponents
|
||||
{
|
||||
public OVRInputHandComponents(Transform handAnchor)
|
||||
{
|
||||
Anchor = handAnchor;
|
||||
OvrHand = handAnchor.GetComponentInChildren<OVRHand>(true);
|
||||
if (OvrHand != null)
|
||||
{
|
||||
OvrMesh = OvrHand.GetComponent<OVRMesh>();
|
||||
OvrMeshRenderer = OvrHand.GetComponent<OVRMeshRenderer>();
|
||||
OvrSkeleton = OvrHand.GetComponent<OVRSkeleton>();
|
||||
OvrSkeletonRenderer = OvrHand.GetComponent<OVRSkeletonRenderer>();
|
||||
}
|
||||
}
|
||||
|
||||
public Transform Anchor { get; }
|
||||
public OVRMesh OvrMesh { get; }
|
||||
public OVRMeshRenderer OvrMeshRenderer { get; }
|
||||
public OVRSkeleton OvrSkeleton { get; }
|
||||
public OVRSkeletonRenderer OvrSkeletonRenderer { get; }
|
||||
public OVRHand OvrHand { get; }
|
||||
}
|
||||
|
||||
public enum HandRenderPoseOriginBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Show hand if it's PoseOrigin is RawTrackedPose or FilteredTrackedPose, regardless of the
|
||||
/// tracking confidence
|
||||
/// </summary>
|
||||
ShowTrackedOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Show hand if it's PoseOrigin is RawTrackedPose or FilteredTrackedPose, and also has high
|
||||
/// tracking confidence
|
||||
/// </summary>
|
||||
ShowHighConfidenceTrackedOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Show hand if it's PoseOrigin is any value other than None, and it is connected.
|
||||
/// </summary>
|
||||
ShowConnectedOnly,
|
||||
}
|
||||
|
||||
public class BaseOVRDataProvider
|
||||
{
|
||||
private readonly HandRenderPoseOriginBehavior _poseOriginBehavior;
|
||||
|
||||
protected BaseOVRDataProvider(HandRenderPoseOriginBehavior poseOriginBehavior)
|
||||
{
|
||||
this._poseOriginBehavior = poseOriginBehavior;
|
||||
}
|
||||
|
||||
public bool ToOvrConfidence(HandDataAsset handDataAsset)
|
||||
{
|
||||
|
||||
bool isRootPoseTracked =
|
||||
handDataAsset.RootPoseOrigin == PoseOrigin.RawTrackedPose ||
|
||||
handDataAsset.RootPoseOrigin == PoseOrigin.FilteredTrackedPose;
|
||||
switch (_poseOriginBehavior)
|
||||
{
|
||||
case HandRenderPoseOriginBehavior.ShowConnectedOnly:
|
||||
{
|
||||
bool isSyntheticPose =
|
||||
handDataAsset.RootPoseOrigin == PoseOrigin.SyntheticPose;
|
||||
return isRootPoseTracked || isSyntheticPose;
|
||||
}
|
||||
case HandRenderPoseOriginBehavior.ShowTrackedOnly:
|
||||
return isRootPoseTracked;
|
||||
case HandRenderPoseOriginBehavior.ShowHighConfidenceTrackedOnly:
|
||||
return isRootPoseTracked && handDataAsset.IsHighConfidence;
|
||||
default:
|
||||
// Should not reach this.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class FromDataAssetOVRMeshRendererDataProvider : BaseOVRDataProvider, IOVRMeshRendererDataProvider
|
||||
{
|
||||
public IOVRMeshRendererDataProvider originalProvider;
|
||||
public HandInputDataReadFunction handInputDataProvider;
|
||||
|
||||
public FromDataAssetOVRMeshRendererDataProvider(
|
||||
HandRenderPoseOriginBehavior poseOriginBehavior) : base(poseOriginBehavior)
|
||||
{
|
||||
}
|
||||
|
||||
public MeshRendererData GetMeshRendererData()
|
||||
{
|
||||
var data = originalProvider.GetMeshRendererData();
|
||||
|
||||
var handInputData = handInputDataProvider.Invoke();
|
||||
if (handInputData == null)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
if (!data.IsDataValid)
|
||||
{
|
||||
data.ShouldUseSystemGestureMaterial = false;
|
||||
}
|
||||
|
||||
data.IsDataValid = handInputData.IsDataValid && handInputData.IsConnected;
|
||||
if (!data.IsDataValid)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
data.IsDataHighConfidence = ToOvrConfidence(handInputData);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public class FromDataAssetOVRSkeletonDataProvider : BaseOVRDataProvider, IOVRSkeletonDataProvider
|
||||
{
|
||||
public IOVRSkeletonDataProvider originalProvider;
|
||||
public HandInputDataReadFunction handInputDataProvider;
|
||||
public SkeletonType skeletonType;
|
||||
|
||||
private readonly OVRPlugin.Quatf[] _boneRotations =
|
||||
new OVRPlugin.Quatf[OVRSkeletonData.LeftSkeleton.NumBones];
|
||||
|
||||
private SkeletonPoseData _poseData;
|
||||
|
||||
public FromDataAssetOVRSkeletonDataProvider(
|
||||
HandRenderPoseOriginBehavior poseOriginBehavior) : base(poseOriginBehavior)
|
||||
{
|
||||
}
|
||||
|
||||
public static int InitialSkeletonChangeCount => 1;
|
||||
|
||||
public SkeletonPoseData GetSkeletonPoseData()
|
||||
{
|
||||
return _poseData;
|
||||
}
|
||||
|
||||
public void UpdateSkeletonPoseData()
|
||||
{
|
||||
_poseData = originalProvider.GetSkeletonPoseData();
|
||||
|
||||
// If the hand is not connected, treat it as if there is no valid data.
|
||||
var handInputData = handInputDataProvider.Invoke();
|
||||
|
||||
bool isValid = handInputData.IsDataValid && handInputData.IsConnected;
|
||||
_poseData.IsDataValid = isValid;
|
||||
if (!isValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _boneRotations.Length; i++)
|
||||
{
|
||||
_boneRotations[i] = handInputData.Joints[i].ToFlippedXQuatf();
|
||||
}
|
||||
|
||||
if (handInputData.HandScale <= 0.0f)
|
||||
{
|
||||
// If handScale is zero it will cause rendering artifacts on the hand meshes.
|
||||
handInputData.HandScale = 1.0f;
|
||||
}
|
||||
|
||||
_poseData.IsDataHighConfidence = ToOvrConfidence(handInputData);
|
||||
_poseData.BoneRotations = _boneRotations;
|
||||
_poseData.RootPose = new OVRPlugin.Posef()
|
||||
{
|
||||
Orientation = handInputData.Root.rotation.ToFlippedZQuatf(),
|
||||
Position = handInputData.Root.position.ToFlippedZVector3f()
|
||||
};
|
||||
_poseData.RootScale = handInputData.HandScale;
|
||||
}
|
||||
|
||||
public SkeletonType GetSkeletonType()
|
||||
{
|
||||
return skeletonType;
|
||||
}
|
||||
}
|
||||
|
||||
public class FromDataAssetOVRSkeletonRendererDataProvider : BaseOVRDataProvider, IOVRSkeletonRendererDataProvider
|
||||
{
|
||||
public HandInputDataReadFunction handInputDataProvider;
|
||||
public IOVRSkeletonRendererDataProvider originalProvider;
|
||||
|
||||
public FromDataAssetOVRSkeletonRendererDataProvider(
|
||||
HandRenderPoseOriginBehavior poseOriginBehavior) : base(poseOriginBehavior)
|
||||
{
|
||||
}
|
||||
|
||||
public SkeletonRendererData GetSkeletonRendererData()
|
||||
{
|
||||
var data = new SkeletonRendererData();
|
||||
var handInputData = handInputDataProvider.Invoke();
|
||||
if (handInputData == null)
|
||||
{
|
||||
return originalProvider.GetSkeletonRendererData();
|
||||
}
|
||||
|
||||
data.IsDataValid = handInputData.RootPoseOrigin != PoseOrigin.None;
|
||||
data.IsDataHighConfidence = ToOvrConfidence(handInputData);
|
||||
data.RootScale = handInputData.HandScale;
|
||||
data.ShouldUseSystemGestureMaterial = false;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads hand & HMD pose data from the DataSources, and copies them onto the
|
||||
/// given OVRSkeleton. Can also provide more sophisticated control of the mesh rendering,
|
||||
/// to keep hand meshes visible when tracking is lost.
|
||||
///
|
||||
/// This class is not required if you are using the HandSkeletonVisual to render the hand.
|
||||
/// </summary>
|
||||
public class OVRSkeletonDataProviders : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Interface(typeof(IOVRCameraRigRef))]
|
||||
private MonoBehaviour _cameraRigRef;
|
||||
|
||||
public IOVRCameraRigRef CameraRigRef { get; private set; }
|
||||
|
||||
[Header("Update CameraRig Transforms")]
|
||||
[SerializeField, Interface(typeof(IDataSource<HmdDataAsset, HmdDataSourceConfig>))]
|
||||
private MonoBehaviour _hmdData;
|
||||
private IDataSource<HmdDataAsset, HmdDataSourceConfig> _hmdDataSource;
|
||||
|
||||
[SerializeField]
|
||||
private Hand _leftHand;
|
||||
|
||||
[SerializeField]
|
||||
private Hand _rightHand;
|
||||
|
||||
[SerializeField]
|
||||
private bool _modifyCameraRigAnchorTransforms = true;
|
||||
|
||||
[SerializeField]
|
||||
private bool _modifyHandTransformsLeft = true;
|
||||
|
||||
[SerializeField]
|
||||
private bool _modifyHandTransformsRight = true;
|
||||
|
||||
[Header("Hand Meshes")]
|
||||
[Tooltip("If true, use the following hand meshes in place of those provided by OVRPlugin")]
|
||||
[SerializeField]
|
||||
private bool _replaceHandMeshRendererProviders = true;
|
||||
|
||||
[SerializeField, Optional]
|
||||
private Mesh _leftHandMesh;
|
||||
|
||||
[SerializeField, Optional]
|
||||
private Mesh _rightHandMesh;
|
||||
|
||||
[SerializeField]
|
||||
private HandRenderPoseOriginBehavior _handRenderBehavior =
|
||||
HandRenderPoseOriginBehavior.ShowConnectedOnly;
|
||||
|
||||
OVRInputHandComponents _leftHandComponents;
|
||||
OVRInputHandComponents _rightHandComponents;
|
||||
private FromDataAssetOVRSkeletonDataProvider _leftHandOvrSkeletonDataProvider;
|
||||
private FromDataAssetOVRSkeletonDataProvider _rightHandOvrSkeletonDataProvider;
|
||||
|
||||
private const System.Reflection.BindingFlags PrivateInstanceFlags =
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
|
||||
|
||||
private const System.Reflection.BindingFlags InstanceFlags =
|
||||
PrivateInstanceFlags | System.Reflection.BindingFlags.Public;
|
||||
|
||||
public void SetModifyCameraRigAndHandTransformState(bool modifyCameraRig,
|
||||
bool modifyLeftHand, bool modifyRightHand)
|
||||
{
|
||||
_modifyCameraRigAnchorTransforms = modifyCameraRig;
|
||||
_modifyHandTransformsLeft = modifyLeftHand;
|
||||
_modifyHandTransformsRight = modifyRightHand;
|
||||
}
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
CameraRigRef = _cameraRigRef as IOVRCameraRigRef;
|
||||
_hmdDataSource = _hmdData as IDataSource<HmdDataAsset, HmdDataSourceConfig>;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
Assert.IsNotNull(CameraRigRef);
|
||||
Assert.IsNotNull(_hmdDataSource);
|
||||
Assert.IsNotNull(_leftHand);
|
||||
Assert.IsNotNull(_rightHand);
|
||||
|
||||
OVRCameraRig cameraRig = CameraRigRef.CameraRig;
|
||||
Assert.IsNotNull(cameraRig);
|
||||
Assert.IsNotNull(cameraRig.leftHandAnchor);
|
||||
Assert.IsNotNull(cameraRig.rightHandAnchor);
|
||||
Assert.IsNotNull(cameraRig.centerEyeAnchor);
|
||||
|
||||
_leftHandComponents = new OVRInputHandComponents(cameraRig.leftHandAnchor);
|
||||
_rightHandComponents = new OVRInputHandComponents(cameraRig.rightHandAnchor);
|
||||
|
||||
HandDataAsset LeftHandProvider() =>
|
||||
_leftHand.isActiveAndEnabled
|
||||
? _leftHand.GetData()
|
||||
: null;
|
||||
|
||||
HandDataAsset RightHandProvider() =>
|
||||
_rightHand.isActiveAndEnabled
|
||||
? _rightHand.GetData()
|
||||
: null;
|
||||
|
||||
if (_replaceHandMeshRendererProviders && _leftHandComponents.OvrHand != null &&
|
||||
_rightHandComponents.OvrHand != null)
|
||||
{
|
||||
InitializeBakedMeshes();
|
||||
}
|
||||
|
||||
// Inject a custom DataProvider to provide confidence levels to the
|
||||
// OVRMeshRenderer
|
||||
InitializeMeshRendererDataProvider(_leftHandComponents, LeftHandProvider);
|
||||
InitializeMeshRendererDataProvider(_rightHandComponents, RightHandProvider);
|
||||
|
||||
// Inject a custom DataProvider to provide finger joints to the OVRSkeleton,
|
||||
// which are surfaced though the `Bones` property.
|
||||
// We update this providers state in this components Update method, so that we can
|
||||
// catch skeleton changes and notify the DataSources.
|
||||
_leftHandOvrSkeletonDataProvider = CreateHandSkeletonPoseDataProvider(
|
||||
_leftHandComponents, LeftHandProvider);
|
||||
_rightHandOvrSkeletonDataProvider = CreateHandSkeletonPoseDataProvider(
|
||||
_rightHandComponents, RightHandProvider);
|
||||
InitializeHandSkeletonPoseDataProvider(
|
||||
_leftHandComponents, _leftHandOvrSkeletonDataProvider);
|
||||
InitializeHandSkeletonPoseDataProvider(
|
||||
_rightHandComponents, _rightHandOvrSkeletonDataProvider);
|
||||
|
||||
// Inject a custom DataProvider to provide poses to the OVRSkeletonRenderer
|
||||
// (debug skeleton joint visualization)
|
||||
InitializeHandSkeletonRendererPoseDataProvider(_leftHandComponents, LeftHandProvider);
|
||||
InitializeHandSkeletonRendererPoseDataProvider(_rightHandComponents, RightHandProvider);
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
private void InitializeBakedMeshes()
|
||||
{
|
||||
/*
|
||||
Q: Why all the reflection to update OVRXYZ classes?
|
||||
A: Since OVRMesh, OVRSkeleton etc directly call OVRPlugin.XYZ methods, without any hooks,
|
||||
hooks, I can't easily get past the fact that on Oculus Link, when hand tracking is
|
||||
not active, OVRHands/OVRSkeleton do not work. (OVRPlugin.GetMesh returns nothing)
|
||||
The goal of this code below is to provide baked skeleton and mesh data to the OVR classes,
|
||||
classes, rather than them retrieving it from OVRPlugin. This allows OVRHands to
|
||||
render in the Editor using poses sourced from GRVF files, without needing to
|
||||
call the OVRPlugin API's.
|
||||
|
||||
Q: Why are the hand fingers all messed up!
|
||||
A: You might be using the mesh provided by the Oculus Integration (OculusHand_L or
|
||||
OculusHand_R). These meshes are not compatible with OVRSkeleton by themselves; they
|
||||
are intended for use with OVRCustomSkeleton. To fix this, use the provided meshes
|
||||
(HandLeft, HandRight).
|
||||
*/
|
||||
ReplaceSkeletonData(_leftHandComponents.OvrSkeleton, OVRSkeletonData.LeftSkeleton);
|
||||
ReplaceSkeletonData(_rightHandComponents.OvrSkeleton, OVRSkeletonData.RightSkeleton);
|
||||
|
||||
if (_leftHandMesh != null)
|
||||
{
|
||||
ReplaceHandMeshes(_leftHandComponents, _leftHandMesh);
|
||||
}
|
||||
|
||||
if (_rightHandMesh != null)
|
||||
{
|
||||
ReplaceHandMeshes(_rightHandComponents, _rightHandMesh);
|
||||
}
|
||||
// Inject a custom DataProvider to provide finger joints to the OVRSkeleton,
|
||||
// Inject a custom DataProvider to provide poses to the OVRSkeletonRenderer
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
_leftHand.HandUpdated += OnLeftHandDataAvailable;
|
||||
_rightHand.HandUpdated += OnRightHandDataAvailable;
|
||||
|
||||
// The root poses of the Hand and HMD must be updated just after OVRCameraRig, which is at
|
||||
// priority 0. Otherwise the changes will be overwritten in that update. To solve this, root
|
||||
// poses are updated in the OVRCameraRig.UpdatedAnchors callback.
|
||||
CameraRigRef.CameraRig.UpdatedAnchors += OnCameraRigUpdatedAnchors;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
CameraRigRef.CameraRig.UpdatedAnchors -= OnCameraRigUpdatedAnchors;
|
||||
_leftHand.HandUpdated -= OnLeftHandDataAvailable;
|
||||
_rightHand.HandUpdated -= OnRightHandDataAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
// Handle the case where OVRManager is not initialized (usually due to an HMD not being
|
||||
// plugged in, when using LINK.
|
||||
if (!OVRManager.OVRManagerinitialized && !CameraRigRef.CameraRig.useFixedUpdateForTracking)
|
||||
{
|
||||
CameraRigRef.CameraRig.EnsureGameObjectIntegrity();
|
||||
OverwriteHandTransforms();
|
||||
}
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
// Handle the case where OVRManager is not initialized (usually due to an HMD not being
|
||||
// plugged in, when using LINK.
|
||||
if (!OVRManager.OVRManagerinitialized && CameraRigRef.CameraRig.useFixedUpdateForTracking)
|
||||
{
|
||||
CameraRigRef.CameraRig.EnsureGameObjectIntegrity();
|
||||
OverwriteHandTransforms();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCameraRigUpdatedAnchors(OVRCameraRig obj)
|
||||
{
|
||||
OverwriteHandTransforms();
|
||||
}
|
||||
|
||||
private void OnLeftHandDataAvailable()
|
||||
{
|
||||
_leftHandOvrSkeletonDataProvider.UpdateSkeletonPoseData();
|
||||
}
|
||||
|
||||
private void OnRightHandDataAvailable()
|
||||
{
|
||||
_rightHandOvrSkeletonDataProvider.UpdateSkeletonPoseData();
|
||||
}
|
||||
|
||||
private void OverwriteHandTransforms()
|
||||
{
|
||||
// Apply modified state back to the camera rig anchors
|
||||
if (_modifyCameraRigAnchorTransforms && _hmdData.isActiveAndEnabled)
|
||||
{
|
||||
var hmdInputData = _hmdDataSource.GetData();
|
||||
if (_modifyCameraRigAnchorTransforms && hmdInputData.IsTracked)
|
||||
{
|
||||
SetLocalTransform(CameraRigRef.CameraRig.centerEyeAnchor, hmdInputData.Root);
|
||||
}
|
||||
}
|
||||
|
||||
if (_modifyHandTransformsLeft && _leftHand.isActiveAndEnabled)
|
||||
{
|
||||
if (_modifyHandTransformsLeft && _leftHand.GetRootPose(out Pose rootPose))
|
||||
{
|
||||
SetTransform(_leftHandComponents.Anchor, rootPose);
|
||||
}
|
||||
}
|
||||
|
||||
if (_modifyHandTransformsRight && _rightHand.isActiveAndEnabled)
|
||||
{
|
||||
if (_modifyHandTransformsRight && _rightHand.GetRootPose(out Pose rootPose))
|
||||
{
|
||||
SetTransform(_rightHandComponents.Anchor, rootPose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetLocalTransform(Transform transform, in Pose root)
|
||||
{
|
||||
transform.localPosition = root.position;
|
||||
transform.localRotation = root.rotation;
|
||||
}
|
||||
|
||||
private static void SetTransform(Transform transform, in Pose root)
|
||||
{
|
||||
transform.position = root.position;
|
||||
transform.rotation = root.rotation;
|
||||
}
|
||||
|
||||
private void ReplaceHandMeshes(OVRInputHandComponents handComponents, Mesh mesh)
|
||||
{
|
||||
if (handComponents.OvrMesh == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// OVRSkeleton and OVRMesh will not initialize the skeleton in editor mode if the hand
|
||||
// is not connected. This is a limitation with Link, the skeleton isn't available unless
|
||||
// hands are tracked.
|
||||
Assert.IsNotNull(mesh);
|
||||
|
||||
// Create a clone of the mesh, so that vertices in the asset file are not modified by
|
||||
// changes to sharedMesh.
|
||||
mesh = Instantiate(mesh);
|
||||
|
||||
InvokeSetField(handComponents.OvrMesh, "_mesh", mesh);
|
||||
InvokeSetProp(handComponents.OvrMesh, "IsInitialized", true);
|
||||
}
|
||||
|
||||
private void InitializeMeshRendererDataProvider(OVRInputHandComponents handComponents,
|
||||
HandInputDataReadFunction inputDataProvider)
|
||||
{
|
||||
if (handComponents.OvrMeshRenderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dataProvider = new FromDataAssetOVRMeshRendererDataProvider(_handRenderBehavior)
|
||||
{
|
||||
handInputDataProvider = inputDataProvider,
|
||||
originalProvider = handComponents.OvrHand
|
||||
};
|
||||
|
||||
InvokeSetField(handComponents.OvrMeshRenderer, "_dataProvider", dataProvider);
|
||||
}
|
||||
|
||||
private FromDataAssetOVRSkeletonDataProvider CreateHandSkeletonPoseDataProvider(
|
||||
OVRInputHandComponents handComponents, HandInputDataReadFunction inputDataProvider)
|
||||
{
|
||||
var ovrMeshType = handComponents.OvrHand.GetComponent<IOVRMeshDataProvider>()
|
||||
.GetMeshType();
|
||||
SkeletonType skeletonType =
|
||||
ovrMeshType == MeshType.HandLeft ? SkeletonType.HandLeft : SkeletonType.HandRight;
|
||||
return new FromDataAssetOVRSkeletonDataProvider(_handRenderBehavior)
|
||||
{
|
||||
originalProvider = handComponents.OvrHand,
|
||||
skeletonType = skeletonType,
|
||||
handInputDataProvider = inputDataProvider
|
||||
};
|
||||
}
|
||||
|
||||
private void InitializeHandSkeletonPoseDataProvider(OVRInputHandComponents handComponents,
|
||||
FromDataAssetOVRSkeletonDataProvider dataProvider)
|
||||
{
|
||||
InvokeSetField(handComponents.OvrSkeleton, "_dataProvider", dataProvider);
|
||||
}
|
||||
|
||||
private void InitializeHandSkeletonRendererPoseDataProvider(
|
||||
OVRInputHandComponents handComponents, HandInputDataReadFunction inputDataProvider)
|
||||
{
|
||||
if (handComponents.OvrSkeletonRenderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dataProvider = new FromDataAssetOVRSkeletonRendererDataProvider(_handRenderBehavior)
|
||||
{
|
||||
handInputDataProvider = inputDataProvider,
|
||||
originalProvider = handComponents.OvrHand
|
||||
};
|
||||
|
||||
InvokeSetField(handComponents.OvrSkeletonRenderer, "_dataProvider", dataProvider);
|
||||
}
|
||||
|
||||
private void ReplaceSkeletonData(OVRSkeleton ovrLeftHandSkeleton,
|
||||
OVRPlugin.Skeleton2 skeletonData)
|
||||
{
|
||||
var skeletonChangeCount = FromDataAssetOVRSkeletonDataProvider.InitialSkeletonChangeCount;
|
||||
var nullParams = new object[] { };
|
||||
|
||||
InvokeSetField(ovrLeftHandSkeleton, "_skeleton", skeletonData);
|
||||
|
||||
InvokeMethod(ovrLeftHandSkeleton, "InitializeBones", nullParams);
|
||||
InvokeMethod(ovrLeftHandSkeleton, "InitializeBindPose", nullParams);
|
||||
InvokeMethod(ovrLeftHandSkeleton, "InitializeCapsules", nullParams);
|
||||
InvokeSetProp(ovrLeftHandSkeleton, "IsInitialized", true);
|
||||
InvokeSetProp(ovrLeftHandSkeleton, "SkeletonChangedCount", -1);
|
||||
}
|
||||
|
||||
private static void InvokeMethod<T>(T instance, string methodName, object[] args)
|
||||
{
|
||||
var method = typeof(T).GetMethod(methodName, InstanceFlags);
|
||||
Assert.IsNotNull(method, methodName + " method must exist on type: " + nameof(T));
|
||||
method.Invoke(instance, args);
|
||||
}
|
||||
|
||||
private static void InvokeSetField<T>(T instance, string fieldName, object val)
|
||||
{
|
||||
var prop = typeof(T).GetField(fieldName, InstanceFlags);
|
||||
Assert.IsNotNull(prop, prop + " field must exist on type: " + nameof(T));
|
||||
prop.SetValue(instance, val);
|
||||
}
|
||||
|
||||
private static void InvokeSetProp<T>(T instance, string propName, object val)
|
||||
{
|
||||
var prop = typeof(T).GetProperty(propName, InstanceFlags);
|
||||
Assert.IsNotNull(prop, prop + " property must exist on type: " + nameof(T));
|
||||
prop.SetMethod.Invoke(instance, new[] { val });
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectOVRSkeletonDataProviders(IOVRCameraRigRef cameraRigRef,
|
||||
IDataSource<HmdDataAsset, HmdDataSourceConfig> hmdData,
|
||||
Hand leftHand, Hand rightHand, bool modifyCameraRigAnchorTransforms, bool modifyHandTransformsLeft,
|
||||
bool modifyHandTransformsRight, bool replaceHandMeshRendererProviders,
|
||||
HandRenderPoseOriginBehavior handRenderBehavior)
|
||||
{
|
||||
InjectCameraRigRef(cameraRigRef);
|
||||
InjectHmdData(hmdData);
|
||||
InjectLeftHand(leftHand);
|
||||
InjectRightHand(rightHand);
|
||||
InjectModifyCameraRigAnchorTransforms(modifyCameraRigAnchorTransforms);
|
||||
InjectModifyHandTransformsLeft(modifyHandTransformsLeft);
|
||||
InjectModifyHandTransformsRight(modifyHandTransformsRight);
|
||||
InjectReplaceHandMeshRendererProviders(replaceHandMeshRendererProviders);
|
||||
InjectHandRenderBehavior(handRenderBehavior);
|
||||
}
|
||||
|
||||
public void InjectCameraRigRef(IOVRCameraRigRef cameraRigRef)
|
||||
{
|
||||
_cameraRigRef = cameraRigRef as MonoBehaviour;
|
||||
CameraRigRef = cameraRigRef;
|
||||
}
|
||||
|
||||
public void InjectHmdData(IDataSource<HmdDataAsset,HmdDataSourceConfig> hmdData)
|
||||
{
|
||||
_hmdData = hmdData as MonoBehaviour;
|
||||
_hmdDataSource = hmdData;
|
||||
}
|
||||
|
||||
public void InjectLeftHand(Hand leftHand)
|
||||
{
|
||||
_leftHand = leftHand;
|
||||
}
|
||||
|
||||
public void InjectRightHand(Hand rightHand)
|
||||
{
|
||||
_rightHand = rightHand;
|
||||
}
|
||||
|
||||
public void InjectModifyCameraRigAnchorTransforms(bool modifyCameraRigAnchorTransforms)
|
||||
{
|
||||
_modifyCameraRigAnchorTransforms = modifyCameraRigAnchorTransforms;
|
||||
}
|
||||
|
||||
public void InjectModifyHandTransformsLeft(bool modifyHandTransformsLeft)
|
||||
{
|
||||
_modifyHandTransformsLeft = modifyHandTransformsLeft;
|
||||
}
|
||||
|
||||
public void InjectModifyHandTransformsRight(bool modifyHandTransformsRight)
|
||||
{
|
||||
_modifyHandTransformsRight = modifyHandTransformsRight;
|
||||
}
|
||||
|
||||
public void InjectReplaceHandMeshRendererProviders(bool replaceHandMeshRendererProviders)
|
||||
{
|
||||
_replaceHandMeshRendererProviders = replaceHandMeshRendererProviders;
|
||||
}
|
||||
|
||||
public void InjectHandRenderBehavior(HandRenderPoseOriginBehavior handRenderBehavior)
|
||||
{
|
||||
_handRenderBehavior = handRenderBehavior;
|
||||
}
|
||||
|
||||
public void InjectOptionalLeftHandMesh(Mesh handMesh)
|
||||
{
|
||||
_leftHandMesh = handMesh;
|
||||
}
|
||||
|
||||
public void InjectOptionalRightHandMesh(Mesh handMesh)
|
||||
{
|
||||
_rightHandMesh = handMesh;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffac1ecd519bd1d409558219f47d07b0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.Input
|
||||
{
|
||||
public class SetDisplayRefresh : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private float _desiredDisplayFrequency = 90f;
|
||||
|
||||
public void SetDesiredDisplayFrequency(float desiredDisplayFrequency)
|
||||
{
|
||||
var validFrequencies = OVRPlugin.systemDisplayFrequenciesAvailable;
|
||||
|
||||
if (validFrequencies.Contains(_desiredDisplayFrequency))
|
||||
{
|
||||
Debug.Log("[Oculus.Interaction] Setting desired display frequency to " + _desiredDisplayFrequency);
|
||||
OVRPlugin.systemDisplayFrequency = _desiredDisplayFrequency;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
SetDesiredDisplayFrequency(_desiredDisplayFrequency);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 215d90758f7ed4f42b7ad0a64d0dfec4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,79 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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
|
||||
{
|
||||
public class TrackingToWorldTransformerOVR : MonoBehaviour, ITrackingToWorldTransformer
|
||||
{
|
||||
[SerializeField, Interface(typeof(IOVRCameraRigRef))]
|
||||
private MonoBehaviour _cameraRigRef;
|
||||
public IOVRCameraRigRef CameraRigRef { get; private set; }
|
||||
|
||||
public Transform Transform => CameraRigRef.CameraRig.transform;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a tracking space pose to a world space pose (Applies any transform applied to the OVRCameraRig)
|
||||
/// </summary>
|
||||
public Pose ToWorldPose(Pose pose)
|
||||
{
|
||||
Transform trackingToWorldSpace = Transform;
|
||||
|
||||
pose.position = trackingToWorldSpace.TransformPoint(pose.position);
|
||||
pose.rotation = trackingToWorldSpace.rotation * pose.rotation;
|
||||
return pose;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a world space pose to a tracking space pose (Removes any transform applied to the OVRCameraRig)
|
||||
/// </summary>
|
||||
public Pose ToTrackingPose(in Pose worldPose)
|
||||
{
|
||||
Transform trackingToWorldSpace = Transform;
|
||||
|
||||
Vector3 position = trackingToWorldSpace.InverseTransformPoint(worldPose.position);
|
||||
Quaternion rotation = Quaternion.Inverse(trackingToWorldSpace.rotation) * worldPose.rotation;
|
||||
|
||||
return new Pose(position, rotation);
|
||||
}
|
||||
|
||||
public Quaternion WorldToTrackingWristJointFixup => FromOVRHandDataSource.WristFixupRotation;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
CameraRigRef = _cameraRigRef as IOVRCameraRigRef;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
Assert.IsNotNull(CameraRigRef);
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllTrackingToWorldTransformerOVR(IOVRCameraRigRef cameraRigRef)
|
||||
{
|
||||
InjectCameraRigRef(cameraRigRef);
|
||||
}
|
||||
|
||||
public void InjectCameraRigRef(IOVRCameraRigRef cameraRigRef)
|
||||
{
|
||||
_cameraRigRef = cameraRigRef as MonoBehaviour;
|
||||
CameraRigRef = cameraRigRef;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 998a5646185efb9488265f3a2f35a99a
|
||||
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,39 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00c937dac239f0644ae25e3b81b10753
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user