Initialer Upload neues Unity-Projekt
This commit is contained in:
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6dc5bcf8b10f564439a3c7603bf53db7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public class ConditionalHideAttribute : PropertyAttribute
|
||||
{
|
||||
public string ConditionalFieldPath { get; private set; }
|
||||
public object HideValue { get; private set; }
|
||||
|
||||
|
||||
public ConditionalHideAttribute(string fieldName, object hideValue)
|
||||
{
|
||||
ConditionalFieldPath = fieldName;
|
||||
HideValue = hideValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1def61d70ea1cd64ebbf612ff7b0b2f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class InspectorButtonAttribute : PropertyAttribute
|
||||
{
|
||||
private const float BUTTON_WIDTH = 80;
|
||||
private const float BUTTON_HEIGHT = 20;
|
||||
|
||||
public float ButtonWidth { get; set; } = BUTTON_WIDTH;
|
||||
|
||||
public readonly string methodName;
|
||||
public readonly float buttonHeight;
|
||||
|
||||
public InspectorButtonAttribute(string methodName)
|
||||
{
|
||||
this.methodName = methodName;
|
||||
this.buttonHeight = BUTTON_HEIGHT;
|
||||
}
|
||||
public InspectorButtonAttribute(string methodName, float buttonHeight)
|
||||
{
|
||||
this.methodName = methodName;
|
||||
this.buttonHeight = buttonHeight;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomPropertyDrawer(typeof(InspectorButtonAttribute))]
|
||||
public class InspectorButtonPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
private MethodInfo _method = null;
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
InspectorButtonAttribute inspectorButtonAttribute = (InspectorButtonAttribute)attribute;
|
||||
return inspectorButtonAttribute.buttonHeight;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect positionRect, SerializedProperty prop, GUIContent label)
|
||||
{
|
||||
InspectorButtonAttribute inspectorButtonAttribute = (InspectorButtonAttribute)attribute;
|
||||
Rect rect = positionRect;
|
||||
rect.height = inspectorButtonAttribute.buttonHeight;
|
||||
if (GUI.Button(rect, label.text))
|
||||
{
|
||||
Type eventType = prop.serializedObject.targetObject.GetType();
|
||||
string eventName = inspectorButtonAttribute.methodName;
|
||||
if (_method == null)
|
||||
{
|
||||
_method = eventType.GetMethod(eventName,
|
||||
BindingFlags.Public
|
||||
| BindingFlags.NonPublic
|
||||
| BindingFlags.Instance
|
||||
| BindingFlags.Static);
|
||||
}
|
||||
_method?.Invoke(prop.serializedObject.targetObject, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49e18ec05efde04499be425cfc618529
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// Used on a SerializedField surfaces the expectation that this field can remain empty.
|
||||
/// </summary>
|
||||
public class OptionalAttribute : PropertyAttribute
|
||||
{
|
||||
[System.Flags]
|
||||
public enum Flag
|
||||
{
|
||||
/// <summary>
|
||||
/// Presents the Optional tag and moves it into the Optional Section
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Indicates that if the reference is missing, a new object will be
|
||||
/// created and linked to it during runtime.
|
||||
/// </summary>
|
||||
AutoGenerated = 1 << 0,
|
||||
/// <summary>
|
||||
/// Indicates that even though the reference is Optional, it is important
|
||||
/// for the component to work as expected.
|
||||
/// </summary>
|
||||
DontHide = 1 << 1
|
||||
}
|
||||
|
||||
public Flag Flags { get; private set; } = Flag.None;
|
||||
|
||||
public OptionalAttribute() { }
|
||||
|
||||
public OptionalAttribute(Flag flags)
|
||||
{
|
||||
Flags = flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed8877737b1570c42b9dafa5f1dcce9f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// Used on a SerializedField tags the field to be added to a custom section
|
||||
/// </summary>
|
||||
public class SectionAttribute : PropertyAttribute
|
||||
{
|
||||
public string SectionName { get; private set; } = string.Empty;
|
||||
|
||||
public SectionAttribute(string sectionName)
|
||||
{
|
||||
SectionName = sectionName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c8ae2e66f5d90a4f8887d11f590497c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Oculus/Interaction/Runtime/Scripts/Body.meta
Normal file
8
Assets/Oculus/Interaction/Runtime/Scripts/Body.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14219c7540753a4428eaa41c86dc0da1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using Oculus.Interaction.Body.Input;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Oculus.Interaction.Body
|
||||
{
|
||||
public class BodyDebugGizmos : SkeletonDebugGizmos
|
||||
{
|
||||
public enum CoordSpace
|
||||
{
|
||||
World,
|
||||
Local,
|
||||
}
|
||||
|
||||
[SerializeField, Interface(typeof(IBody))]
|
||||
private UnityEngine.Object _body;
|
||||
private IBody Body;
|
||||
|
||||
[Tooltip("The coordinate space in which to draw the skeleton. " +
|
||||
"World space draws the skeleton at the world Body location. " +
|
||||
"Local draws the skeleton relative to this transform.")]
|
||||
[SerializeField]
|
||||
private CoordSpace _space = CoordSpace.World;
|
||||
|
||||
public CoordSpace Space
|
||||
{
|
||||
get => _space;
|
||||
set => _space = value;
|
||||
}
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Body = _body as IBody;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
Assert.IsNotNull(Body);
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Body.WhenBodyUpdated += HandleBodyUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Body.WhenBodyUpdated -= HandleBodyUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool TryGetWorldJointPose(BodyJointId jointId, out Pose pose)
|
||||
{
|
||||
bool result;
|
||||
switch (_space)
|
||||
{
|
||||
default:
|
||||
case CoordSpace.World:
|
||||
result = Body.GetJointPose(jointId, out pose);
|
||||
break;
|
||||
case CoordSpace.Local:
|
||||
result = Body.GetJointPoseFromRoot(jointId, out pose);
|
||||
pose.position = transform.TransformPoint(pose.position);
|
||||
pose.rotation = transform.rotation * pose.rotation;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override bool TryGetParentJointId(BodyJointId jointId, out BodyJointId parent)
|
||||
{
|
||||
return Body.SkeletonMapping.TryGetParentJointId(jointId, out parent);
|
||||
}
|
||||
|
||||
private VisibilityFlags GetModifiedDrawFlags()
|
||||
{
|
||||
VisibilityFlags modifiedFlags = Visibility;
|
||||
if (HasNegativeScale && Space == CoordSpace.Local)
|
||||
{
|
||||
modifiedFlags &= ~VisibilityFlags.Axes;
|
||||
}
|
||||
return modifiedFlags;
|
||||
}
|
||||
|
||||
private void HandleBodyUpdated()
|
||||
{
|
||||
foreach (BodyJointId joint in Body.SkeletonMapping.Joints)
|
||||
{
|
||||
Draw(joint, GetModifiedDrawFlags());
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllBodyJointDebugGizmos(IBody body)
|
||||
{
|
||||
InjectBody(body);
|
||||
}
|
||||
|
||||
public void InjectBody(IBody body)
|
||||
{
|
||||
_body = body as UnityEngine.Object;
|
||||
Body = body;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f90df98d80579a4eb0b25852e0f6758
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- _body: {instanceID: 0}
|
||||
- _debugPrefab: {fileID: 1332877395996002986, guid: d1ed0fc8d54eb1e4283a76baf1e42b1d,
|
||||
type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 080ee45c4ae29f847a3919f73fbc0df6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
133
Assets/Oculus/Interaction/Runtime/Scripts/Body/Input/Body.cs
Normal file
133
Assets/Oculus/Interaction/Runtime/Scripts/Body/Input/Body.cs
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using Oculus.Interaction.Input;
|
||||
|
||||
namespace Oculus.Interaction.Body.Input
|
||||
{
|
||||
public class Body : DataModifier<BodyDataAsset>, IBody
|
||||
{
|
||||
[Tooltip("If assigned, joint pose translations into world " +
|
||||
"space will be performed via this transform. If unassigned, " +
|
||||
"world joint poses will be returned in tracking space.")]
|
||||
[SerializeField, Optional]
|
||||
private Transform _trackingSpace;
|
||||
|
||||
public bool IsConnected => GetData().IsDataValid;
|
||||
|
||||
public bool IsHighConfidence => GetData().IsDataHighConfidence;
|
||||
|
||||
public float Scale => GetData().RootScale;
|
||||
|
||||
public ISkeletonMapping SkeletonMapping => GetData().SkeletonMapping;
|
||||
|
||||
public bool IsTrackedDataValid => GetData().IsDataValid;
|
||||
|
||||
public event Action WhenBodyUpdated = delegate { };
|
||||
|
||||
private BodyJointsCache _jointPosesCache;
|
||||
|
||||
public bool GetJointPose(BodyJointId bodyJointId, out Pose pose)
|
||||
{
|
||||
pose = Pose.identity;
|
||||
if (!IsTrackedDataValid || !SkeletonMapping.Joints.Contains(bodyJointId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckJointPosesCacheUpdate();
|
||||
pose = _jointPosesCache.GetWorldJointPose(bodyJointId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool GetJointPoseLocal(BodyJointId bodyJointId, out Pose pose)
|
||||
{
|
||||
pose = Pose.identity;
|
||||
if (!IsTrackedDataValid || !SkeletonMapping.Joints.Contains(bodyJointId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckJointPosesCacheUpdate();
|
||||
pose = _jointPosesCache.GetLocalJointPose(bodyJointId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool GetJointPoseFromRoot(BodyJointId bodyJointId, out Pose pose)
|
||||
{
|
||||
pose = Pose.identity;
|
||||
if (!IsTrackedDataValid || !SkeletonMapping.Joints.Contains(bodyJointId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckJointPosesCacheUpdate();
|
||||
pose = _jointPosesCache.GetJointPoseFromRoot(bodyJointId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool GetRootPose(out Pose pose)
|
||||
{
|
||||
pose = Pose.identity;
|
||||
if (!IsTrackedDataValid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckJointPosesCacheUpdate();
|
||||
pose = _jointPosesCache.GetWorldRootPose();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void InitializeJointPosesCache()
|
||||
{
|
||||
if (_jointPosesCache == null)
|
||||
{
|
||||
_jointPosesCache = new BodyJointsCache();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckJointPosesCacheUpdate()
|
||||
{
|
||||
if (_jointPosesCache != null
|
||||
&& CurrentDataVersion != _jointPosesCache.LocalDataVersion)
|
||||
{
|
||||
_jointPosesCache.Update(GetData(), CurrentDataVersion, _trackingSpace);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Apply(BodyDataAsset data)
|
||||
{
|
||||
// Default implementation does nothing, to allow instantiation of this modifier directly
|
||||
}
|
||||
|
||||
public override void MarkInputDataRequiresUpdate()
|
||||
{
|
||||
base.MarkInputDataRequiresUpdate();
|
||||
if (Started)
|
||||
{
|
||||
InitializeJointPosesCache();
|
||||
WhenBodyUpdated.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55afe974697276a46949c30d6d15f347
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using Oculus.Interaction.Input;
|
||||
|
||||
namespace Oculus.Interaction.Body.Input
|
||||
{
|
||||
public class BodyDataAsset : ICopyFrom<BodyDataAsset>
|
||||
{
|
||||
public ISkeletonMapping SkeletonMapping { get; set; }
|
||||
public Pose Root { get; set; } = Pose.identity;
|
||||
public float RootScale { get; set; } = 1f;
|
||||
public bool IsDataValid { get; set; } = false;
|
||||
public bool IsDataHighConfidence { get; set; } = false;
|
||||
public Pose[] JointPoses { get; set; } = new Pose[Constants.NUM_BODY_JOINTS];
|
||||
public int SkeletonChangedCount { get; set; } = 0;
|
||||
|
||||
public void CopyFrom(BodyDataAsset source)
|
||||
{
|
||||
SkeletonMapping = source.SkeletonMapping;
|
||||
Root = source.Root;
|
||||
RootScale = source.RootScale;
|
||||
IsDataValid = source.IsDataValid;
|
||||
IsDataHighConfidence = source.IsDataHighConfidence;
|
||||
SkeletonChangedCount = source.SkeletonChangedCount;
|
||||
|
||||
for (int i = 0; i < source.JointPoses.Length; ++i)
|
||||
{
|
||||
JointPoses[i] = source.JointPoses[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3341c364a9e73724bbd5f1f56e1f2bbc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.Body.Input
|
||||
{
|
||||
public class BodyJointsCache
|
||||
{
|
||||
private const int DIRTY_ARRAY_SIZE = 1 + (Constants.NUM_BODY_JOINTS / 64);
|
||||
|
||||
public int LocalDataVersion { get; private set; } = -1;
|
||||
|
||||
private Pose[] _originalPoses = new Pose[Constants.NUM_BODY_JOINTS];
|
||||
private Pose[] _posesFromRoot = new Pose[Constants.NUM_BODY_JOINTS];
|
||||
private Pose[] _localPoses = new Pose[Constants.NUM_BODY_JOINTS];
|
||||
private Pose[] _worldPoses = new Pose[Constants.NUM_BODY_JOINTS];
|
||||
|
||||
private ReadOnlyBodyJointPoses _posesFromRootCollection;
|
||||
private ReadOnlyBodyJointPoses _worldPosesCollection;
|
||||
private ReadOnlyBodyJointPoses _localPosesCollection;
|
||||
|
||||
private ulong[] _dirtyJointsFromRoot;
|
||||
private ulong[] _dirtyLocalJoints;
|
||||
private ulong[] _dirtyWorldJoints;
|
||||
|
||||
private Matrix4x4 _scale;
|
||||
private Pose _rootPose;
|
||||
private Pose _worldRoot;
|
||||
|
||||
private ISkeletonMapping _mapping;
|
||||
|
||||
public BodyJointsCache()
|
||||
{
|
||||
LocalDataVersion = -1;
|
||||
|
||||
_dirtyJointsFromRoot = new ulong[DIRTY_ARRAY_SIZE];
|
||||
_dirtyLocalJoints = new ulong[DIRTY_ARRAY_SIZE];
|
||||
_dirtyWorldJoints = new ulong[DIRTY_ARRAY_SIZE];
|
||||
|
||||
_localPosesCollection = new ReadOnlyBodyJointPoses(_localPoses);
|
||||
_worldPosesCollection = new ReadOnlyBodyJointPoses(_worldPoses);
|
||||
_posesFromRootCollection = new ReadOnlyBodyJointPoses(_posesFromRoot);
|
||||
}
|
||||
|
||||
public void Update(BodyDataAsset data, int dataVersion,
|
||||
Transform trackingSpace = null)
|
||||
{
|
||||
LocalDataVersion = dataVersion;
|
||||
_mapping = data.SkeletonMapping;
|
||||
|
||||
for (int i = 0; i < DIRTY_ARRAY_SIZE; ++i)
|
||||
{
|
||||
_dirtyJointsFromRoot[i] = ulong.MaxValue;
|
||||
_dirtyLocalJoints[i] = ulong.MaxValue;
|
||||
_dirtyWorldJoints[i] = ulong.MaxValue;
|
||||
}
|
||||
if (!data.IsDataValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_scale = Matrix4x4.Scale(Vector3.one * data.RootScale);
|
||||
_rootPose = data.Root;
|
||||
_worldRoot = _rootPose;
|
||||
|
||||
if (trackingSpace != null)
|
||||
{
|
||||
_scale *= Matrix4x4.Scale(trackingSpace.lossyScale);
|
||||
_worldRoot.position = trackingSpace.TransformPoint(_rootPose.position);
|
||||
_worldRoot.rotation = trackingSpace.rotation * _rootPose.rotation;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Constants.NUM_BODY_JOINTS; ++i)
|
||||
{
|
||||
_originalPoses[i] = data.JointPoses[i];
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetAllLocalPoses(out ReadOnlyBodyJointPoses localJointPoses)
|
||||
{
|
||||
UpdateAllLocalPoses();
|
||||
localJointPoses = _localPosesCollection;
|
||||
return _localPosesCollection.Count > 0;
|
||||
}
|
||||
|
||||
public bool GetAllPosesFromRoot(out ReadOnlyBodyJointPoses posesFromRoot)
|
||||
{
|
||||
UpdateAllPosesFromRoot();
|
||||
posesFromRoot = _posesFromRootCollection;
|
||||
return _posesFromRootCollection.Count > 0;
|
||||
}
|
||||
|
||||
public bool GetAllWorldPoses(out ReadOnlyBodyJointPoses worldJointPoses)
|
||||
{
|
||||
UpdateAllWorldPoses();
|
||||
worldJointPoses = _worldPosesCollection;
|
||||
return _worldPosesCollection.Count > 0;
|
||||
}
|
||||
|
||||
public Pose GetLocalJointPose(BodyJointId jointId)
|
||||
{
|
||||
UpdateLocalJointPose(jointId);
|
||||
return _localPoses[(int)jointId];
|
||||
}
|
||||
|
||||
public Pose GetJointPoseFromRoot(BodyJointId jointId)
|
||||
{
|
||||
UpdateJointPoseFromRoot(jointId);
|
||||
return _posesFromRoot[(int)jointId];
|
||||
}
|
||||
|
||||
public Pose GetWorldJointPose(BodyJointId jointId)
|
||||
{
|
||||
UpdateWorldJointPose(jointId);
|
||||
return _worldPoses[(int)jointId];
|
||||
}
|
||||
|
||||
public Pose GetWorldRootPose()
|
||||
{
|
||||
return _worldRoot;
|
||||
}
|
||||
|
||||
private void UpdateJointPoseFromRoot(BodyJointId jointId)
|
||||
{
|
||||
if (!CheckJointDirty(jointId, _dirtyJointsFromRoot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pose originalPose = _originalPoses[(int)jointId];
|
||||
Vector3 posFromRoot = Quaternion.Inverse(_rootPose.rotation) *
|
||||
(originalPose.position - _rootPose.position);
|
||||
Quaternion rotFromRoot = Quaternion.Inverse(_rootPose.rotation) *
|
||||
originalPose.rotation;
|
||||
_posesFromRoot[(int)jointId] = new Pose(posFromRoot, rotFromRoot);
|
||||
|
||||
SetJointClean(jointId, _dirtyJointsFromRoot);
|
||||
}
|
||||
|
||||
private void UpdateLocalJointPose(BodyJointId jointId)
|
||||
{
|
||||
if (!CheckJointDirty(jointId, _dirtyLocalJoints))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mapping.TryGetParentJointId(jointId, out BodyJointId parentId) &&
|
||||
parentId >= BodyJointId.Body_Root)
|
||||
{
|
||||
Pose originalPose = _originalPoses[(int)jointId];
|
||||
Pose parentPose = _originalPoses[(int)parentId];
|
||||
|
||||
Vector3 localPos = Quaternion.Inverse(parentPose.rotation) *
|
||||
(originalPose.position - parentPose.position);
|
||||
Quaternion localRot = Quaternion.Inverse(parentPose.rotation) *
|
||||
originalPose.rotation;
|
||||
_localPoses[(int)jointId] = new Pose(localPos, localRot);
|
||||
}
|
||||
else
|
||||
{
|
||||
_localPoses[(int)jointId] = Pose.identity;
|
||||
}
|
||||
SetJointClean(jointId, _dirtyLocalJoints);
|
||||
}
|
||||
|
||||
private void UpdateWorldJointPose(BodyJointId jointId)
|
||||
{
|
||||
if (!CheckJointDirty(jointId, _dirtyWorldJoints))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pose fromRoot = GetJointPoseFromRoot(jointId);
|
||||
fromRoot.position = _scale * fromRoot.position;
|
||||
fromRoot.Postmultiply(GetWorldRootPose());
|
||||
_worldPoses[(int)jointId] = fromRoot;
|
||||
SetJointClean(jointId, _dirtyWorldJoints);
|
||||
}
|
||||
|
||||
private void UpdateAllWorldPoses()
|
||||
{
|
||||
foreach (BodyJointId joint in _mapping.Joints)
|
||||
{
|
||||
UpdateWorldJointPose(joint);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAllLocalPoses()
|
||||
{
|
||||
foreach (BodyJointId joint in _mapping.Joints)
|
||||
{
|
||||
UpdateLocalJointPose(joint);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAllPosesFromRoot()
|
||||
{
|
||||
foreach (BodyJointId joint in _mapping.Joints)
|
||||
{
|
||||
UpdateJointPoseFromRoot(joint);
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckJointDirty(BodyJointId jointId, ulong[] dirtyFlags)
|
||||
{
|
||||
int outerIdx = (int)jointId / 64;
|
||||
int innerIdx = (int)jointId % 64;
|
||||
return (dirtyFlags[outerIdx] & (1UL << innerIdx)) != 0;
|
||||
}
|
||||
|
||||
private void SetJointClean(BodyJointId jointId, ulong[] dirtyFlags)
|
||||
{
|
||||
int outerIdx = (int)jointId / 64;
|
||||
int innerIdx = (int)jointId % 64;
|
||||
dirtyFlags[outerIdx] = dirtyFlags[outerIdx] & ~(1UL << innerIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3c3dce4531a44741a6120d58ae02113
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Primitive type serialization
|
||||
/// </summary>
|
||||
namespace Oculus.Interaction.Body.Input
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const int NUM_BODY_JOINTS = (int)BodyJointId.Body_End;
|
||||
}
|
||||
|
||||
public enum BodyJointId
|
||||
{
|
||||
Invalid = -1,
|
||||
|
||||
[InspectorName("Body Start")]
|
||||
Body_Start = 0,
|
||||
[InspectorName("Root")]
|
||||
Body_Root = Body_Start + 0,
|
||||
[InspectorName("Hips")]
|
||||
Body_Hips = Body_Start + 1,
|
||||
[InspectorName("Spine Lower")]
|
||||
Body_SpineLower = Body_Start + 2,
|
||||
[InspectorName("Spine Middle")]
|
||||
Body_SpineMiddle = Body_Start + 3,
|
||||
[InspectorName("Spine Upper")]
|
||||
Body_SpineUpper = Body_Start + 4,
|
||||
[InspectorName("Chest")]
|
||||
Body_Chest = Body_Start + 5,
|
||||
[InspectorName("Neck")]
|
||||
Body_Neck = Body_Start + 6,
|
||||
[InspectorName("Head")]
|
||||
Body_Head = Body_Start + 7,
|
||||
[InspectorName("Left Arm/Left Shoulder")]
|
||||
Body_LeftShoulder = Body_Start + 8,
|
||||
[InspectorName("Left Arm/Left Scapula")]
|
||||
Body_LeftScapula = Body_Start + 9,
|
||||
[InspectorName("Left Arm/Left Arm Upper")]
|
||||
Body_LeftArmUpper = Body_Start + 10,
|
||||
[InspectorName("Left Arm/Left Arm Lower")]
|
||||
Body_LeftArmLower = Body_Start + 11,
|
||||
[InspectorName("Left Arm/Left Hand Wrist Twist")]
|
||||
Body_LeftHandWristTwist = Body_Start + 12,
|
||||
[InspectorName("Right Arm/Right Shoulder")]
|
||||
Body_RightShoulder = Body_Start + 13,
|
||||
[InspectorName("Right Arm/Right Scapula")]
|
||||
Body_RightScapula = Body_Start + 14,
|
||||
[InspectorName("Right Arm/Right Arm Upper")]
|
||||
Body_RightArmUpper = Body_Start + 15,
|
||||
[InspectorName("Right Arm/Right Arm Lower")]
|
||||
Body_RightArmLower = Body_Start + 16,
|
||||
[InspectorName("Right Arm/Right Hand Wrist Twist")]
|
||||
Body_RightHandWristTwist = Body_Start + 17,
|
||||
|
||||
[InspectorName("Left Hand/Left Hand Wrist")]
|
||||
Body_LeftHandWrist = Body_Start + 18,
|
||||
[InspectorName("Left Hand/Left Hand Palm")]
|
||||
Body_LeftHandPalm = Body_Start + 19,
|
||||
[InspectorName("Left Hand/Left Hand Thumb Metacarpal")]
|
||||
Body_LeftHandThumbMetacarpal = Body_Start + 20,
|
||||
[InspectorName("Left Hand/Left Hand Thumb Proximal")]
|
||||
Body_LeftHandThumbProximal = Body_Start + 21,
|
||||
[InspectorName("Left Hand/Left Hand Thumb Distal")]
|
||||
Body_LeftHandThumbDistal = Body_Start + 22,
|
||||
[InspectorName("Left Hand/Left Hand Thumb Tip")]
|
||||
Body_LeftHandThumbTip = Body_Start + 23,
|
||||
[InspectorName("Left Hand/Left Hand Index Metacarpal")]
|
||||
Body_LeftHandIndexMetacarpal = Body_Start + 24,
|
||||
[InspectorName("Left Hand/Left Hand Index Proximal")]
|
||||
Body_LeftHandIndexProximal = Body_Start + 25,
|
||||
[InspectorName("Left Hand/Left Hand Index Intermediate")]
|
||||
Body_LeftHandIndexIntermediate = Body_Start + 26,
|
||||
[InspectorName("Left Hand/Left Hand Index Distal")]
|
||||
Body_LeftHandIndexDistal = Body_Start + 27,
|
||||
[InspectorName("Left Hand/Left Hand Index Tip")]
|
||||
Body_LeftHandIndexTip = Body_Start + 28,
|
||||
[InspectorName("Left Hand/Left Hand Middle Metacarpal")]
|
||||
Body_LeftHandMiddleMetacarpal = Body_Start + 29,
|
||||
[InspectorName("Left Hand/Left Hand Middle Proximal")]
|
||||
Body_LeftHandMiddleProximal = Body_Start + 30,
|
||||
[InspectorName("Left Hand/Left Hand Middle Intermediate")]
|
||||
Body_LeftHandMiddleIntermediate = Body_Start + 31,
|
||||
[InspectorName("Left Hand/Left Hand Middle Distal")]
|
||||
Body_LeftHandMiddleDistal = Body_Start + 32,
|
||||
[InspectorName("Left Hand/Left Hand Middle Tip")]
|
||||
Body_LeftHandMiddleTip = Body_Start + 33,
|
||||
[InspectorName("Left Hand/Left Hand Ring Metacarpal")]
|
||||
Body_LeftHandRingMetacarpal = Body_Start + 34,
|
||||
[InspectorName("Left Hand/Left Hand Ring Proximal")]
|
||||
Body_LeftHandRingProximal = Body_Start + 35,
|
||||
[InspectorName("Left Hand/Left Hand Ring Intermediate")]
|
||||
Body_LeftHandRingIntermediate = Body_Start + 36,
|
||||
[InspectorName("Left Hand/Left Hand Ring Distal")]
|
||||
Body_LeftHandRingDistal = Body_Start + 37,
|
||||
[InspectorName("Left Hand/Left Hand Ring Tip")]
|
||||
Body_LeftHandRingTip = Body_Start + 38,
|
||||
[InspectorName("Left Hand/Left Hand Little Metacarpal")]
|
||||
Body_LeftHandLittleMetacarpal = Body_Start + 39,
|
||||
[InspectorName("Left Hand/Left Hand Little Proximal")]
|
||||
Body_LeftHandLittleProximal = Body_Start + 40,
|
||||
[InspectorName("Left Hand/Left Hand Little Intermediate")]
|
||||
Body_LeftHandLittleIntermediate = Body_Start + 41,
|
||||
[InspectorName("Left Hand/Left Hand Little Distal")]
|
||||
Body_LeftHandLittleDistal = Body_Start + 42,
|
||||
[InspectorName("Left Hand/Left Hand Little Tip")]
|
||||
Body_LeftHandLittleTip = Body_Start + 43,
|
||||
|
||||
[InspectorName("Right Hand/Right Hand Wrist")]
|
||||
Body_RightHandWrist = Body_Start + 44,
|
||||
[InspectorName("Right Hand/Right Hand Palm")]
|
||||
Body_RightHandPalm = Body_Start + 45,
|
||||
[InspectorName("Right Hand/Right Hand Thumb Metacarpal")]
|
||||
Body_RightHandThumbMetacarpal = Body_Start + 46,
|
||||
[InspectorName("Right Hand/Right Hand Thumb Proximal")]
|
||||
Body_RightHandThumbProximal = Body_Start + 47,
|
||||
[InspectorName("Right Hand/Right Hand Thumb Distal")]
|
||||
Body_RightHandThumbDistal = Body_Start + 48,
|
||||
[InspectorName("Right Hand/Right Hand Thumb Tip")]
|
||||
Body_RightHandThumbTip = Body_Start + 49,
|
||||
[InspectorName("Right Hand/Right Hand Index Metacarpal")]
|
||||
Body_RightHandIndexMetacarpal = Body_Start + 50,
|
||||
[InspectorName("Right Hand/Right Hand Index Proximal")]
|
||||
Body_RightHandIndexProximal = Body_Start + 51,
|
||||
[InspectorName("Right Hand/Right Hand Index Intermediate")]
|
||||
Body_RightHandIndexIntermediate = Body_Start + 52,
|
||||
[InspectorName("Right Hand/Right Hand Index Distal")]
|
||||
Body_RightHandIndexDistal = Body_Start + 53,
|
||||
[InspectorName("Right Hand/Right Hand Index Tip")]
|
||||
Body_RightHandIndexTip = Body_Start + 54,
|
||||
[InspectorName("Right Hand/Right Hand Middle Metacarpal")]
|
||||
Body_RightHandMiddleMetacarpal = Body_Start + 55,
|
||||
[InspectorName("Right Hand/Right Hand Middle Proximal")]
|
||||
Body_RightHandMiddleProximal = Body_Start + 56,
|
||||
[InspectorName("Right Hand/Right Hand Middle Intermediate")]
|
||||
Body_RightHandMiddleIntermediate = Body_Start + 57,
|
||||
[InspectorName("Right Hand/Right Hand Middle Distal")]
|
||||
Body_RightHandMiddleDistal = Body_Start + 58,
|
||||
[InspectorName("Right Hand/Right Hand Middle Tip")]
|
||||
Body_RightHandMiddleTip = Body_Start + 59,
|
||||
[InspectorName("Right Hand/Right Hand Ring Metacarpal")]
|
||||
Body_RightHandRingMetacarpal = Body_Start + 60,
|
||||
[InspectorName("Right Hand/Right Hand Ring Proximal")]
|
||||
Body_RightHandRingProximal = Body_Start + 61,
|
||||
[InspectorName("Right Hand/Right Hand Ring Intermediate")]
|
||||
Body_RightHandRingIntermediate = Body_Start + 62,
|
||||
[InspectorName("Right Hand/Right Hand Ring Distal")]
|
||||
Body_RightHandRingDistal = Body_Start + 63,
|
||||
[InspectorName("Right Hand/Right Hand Ring Tip")]
|
||||
Body_RightHandRingTip = Body_Start + 64,
|
||||
[InspectorName("Right Hand/Right Hand Little Metacarpal")]
|
||||
Body_RightHandLittleMetacarpal = Body_Start + 65,
|
||||
[InspectorName("Right Hand/Right Hand Little Proximal")]
|
||||
Body_RightHandLittleProximal = Body_Start + 66,
|
||||
[InspectorName("Right Hand/Right Hand Little Intermediate")]
|
||||
Body_RightHandLittleIntermediate = Body_Start + 67,
|
||||
[InspectorName("Right Hand/Right Hand Little Distal")]
|
||||
Body_RightHandLittleDistal = Body_Start + 68,
|
||||
[InspectorName("Right Hand/Right Hand Little Tip")]
|
||||
Body_RightHandLittleTip = Body_Start + 69,
|
||||
[InspectorName("Body End")]
|
||||
Body_End = Body_Start + 70,
|
||||
}
|
||||
|
||||
public class ReadOnlyBodyJointPoses : IReadOnlyList<Pose>
|
||||
{
|
||||
private Pose[] _poses;
|
||||
|
||||
public ReadOnlyBodyJointPoses(Pose[] poses)
|
||||
{
|
||||
_poses = poses;
|
||||
}
|
||||
|
||||
public IEnumerator<Pose> GetEnumerator()
|
||||
{
|
||||
foreach (var pose in _poses)
|
||||
{
|
||||
yield return pose;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public static ReadOnlyBodyJointPoses Empty { get; } = new ReadOnlyBodyJointPoses(Array.Empty<Pose>());
|
||||
|
||||
public int Count => _poses.Length;
|
||||
|
||||
public Pose this[int index] => _poses[index];
|
||||
|
||||
public ref readonly Pose this[BodyJointId index] => ref _poses[(int)index];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9ade89b75ac75740827400fcb618e09
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Assertions;
|
||||
using Oculus.Interaction.Collections;
|
||||
|
||||
namespace Oculus.Interaction.Body.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a skeleton with joint id <see cref="TSourceJointId"/> to BodyJointId,
|
||||
/// and exposes parent/child relationships within the skeleton.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSourceJointId">The enum joint type of the source skeleton</typeparam>
|
||||
public abstract class BodySkeletonMapping<TSourceJointId> : ISkeletonMapping
|
||||
where TSourceJointId : System.Enum
|
||||
{
|
||||
public IEnumerableHashSet<BodyJointId> Joints => _joints;
|
||||
|
||||
public bool TryGetParentJointId(BodyJointId jointId, out BodyJointId parentJointId)
|
||||
{
|
||||
return _jointToParent.TryGetValue(jointId, out parentJointId);
|
||||
}
|
||||
|
||||
public bool TryGetSourceJointId(BodyJointId jointId, out TSourceJointId sourceJointId)
|
||||
{
|
||||
return _reverseMap.TryGetValue(jointId, out sourceJointId);
|
||||
}
|
||||
|
||||
public bool TryGetBodyJointId(TSourceJointId jointId, out BodyJointId bodyJointId)
|
||||
{
|
||||
return _forwardMap.TryGetValue(jointId, out bodyJointId);
|
||||
}
|
||||
|
||||
protected TSourceJointId GetSourceJointFromBodyJoint(BodyJointId jointId)
|
||||
{
|
||||
return _reverseMap[jointId];
|
||||
}
|
||||
|
||||
protected BodyJointId GetBodyJointFromSourceJoint(TSourceJointId sourceJointId)
|
||||
{
|
||||
return _forwardMap[sourceJointId];
|
||||
}
|
||||
|
||||
protected abstract TSourceJointId GetRoot();
|
||||
protected abstract IReadOnlyDictionary<BodyJointId, JointInfo> GetJointMapping();
|
||||
|
||||
public BodySkeletonMapping()
|
||||
{
|
||||
_tree = new SkeletonTree(GetRoot(), GetJointMapping());
|
||||
_joints = new EnumerableHashSet<BodyJointId>(_tree.Nodes.Select(n => n.BodyJointId));
|
||||
_forwardMap = _tree.Nodes.ToDictionary((n) => n.SourceJointId, (n) => n.BodyJointId);
|
||||
_reverseMap = _tree.Nodes.ToDictionary((n) => n.BodyJointId, (n) => n.SourceJointId);
|
||||
_jointToParent = _tree.Nodes.ToDictionary((n) => n.BodyJointId, (n) => n.Parent.BodyJointId);
|
||||
}
|
||||
|
||||
private readonly SkeletonTree _tree;
|
||||
private readonly IEnumerableHashSet<BodyJointId> _joints;
|
||||
private readonly IReadOnlyDictionary<TSourceJointId, BodyJointId> _forwardMap;
|
||||
private readonly IReadOnlyDictionary<BodyJointId, TSourceJointId> _reverseMap;
|
||||
private readonly IReadOnlyDictionary<BodyJointId, BodyJointId> _jointToParent;
|
||||
|
||||
private class SkeletonTree
|
||||
{
|
||||
public readonly Node Root;
|
||||
public readonly IReadOnlyList<Node> Nodes;
|
||||
|
||||
public SkeletonTree(TSourceJointId root,
|
||||
IReadOnlyDictionary<BodyJointId, JointInfo> mapping)
|
||||
{
|
||||
Dictionary<TSourceJointId, Node> nodes = new Dictionary<TSourceJointId, Node>();
|
||||
foreach (var map in mapping)
|
||||
{
|
||||
BodyJointId jointId = map.Key;
|
||||
JointInfo jointInfo = map.Value;
|
||||
Assert.IsFalse(nodes.ContainsKey(jointInfo.SourceJointId),
|
||||
"Duplicate Joint ID in Mapping");
|
||||
nodes[jointInfo.SourceJointId] =
|
||||
new Node(jointInfo.SourceJointId, jointId);
|
||||
}
|
||||
foreach (var jointInfo in mapping.Values)
|
||||
{
|
||||
Node thisNode = nodes[jointInfo.SourceJointId];
|
||||
thisNode.Parent = nodes[jointInfo.ParentJointId];
|
||||
thisNode.Parent.Children.Add(thisNode);
|
||||
}
|
||||
Nodes = new List<Node>(nodes.Values);
|
||||
Root = nodes[root];
|
||||
}
|
||||
|
||||
public class Node
|
||||
{
|
||||
public readonly TSourceJointId SourceJointId;
|
||||
public readonly BodyJointId BodyJointId;
|
||||
|
||||
public Node Parent;
|
||||
public List<Node> Children = new List<Node>();
|
||||
|
||||
public Node(TSourceJointId sourceJointId, BodyJointId bodyJointId)
|
||||
{
|
||||
SourceJointId = sourceJointId;
|
||||
BodyJointId = bodyJointId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected readonly struct JointInfo
|
||||
{
|
||||
public readonly TSourceJointId SourceJointId;
|
||||
public readonly TSourceJointId ParentJointId;
|
||||
|
||||
public JointInfo(
|
||||
TSourceJointId sourceJointId,
|
||||
TSourceJointId parentJointId)
|
||||
{
|
||||
SourceJointId = sourceJointId;
|
||||
ParentJointId = parentJointId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0163dd25f6c25141bbe3c5b18171c50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace Oculus.Interaction.Body.Input
|
||||
{
|
||||
public interface IBody
|
||||
{
|
||||
/// <summary>
|
||||
/// The mapping for this body, which allows querying of the supported joint set
|
||||
/// of the skeleton as well as joint parent/child relationship.
|
||||
/// </summary>
|
||||
ISkeletonMapping SkeletonMapping { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the body connected and tracked
|
||||
/// </summary>
|
||||
bool IsConnected { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body is connected and tracked, and the root pose's tracking data is marked as
|
||||
/// high confidence.
|
||||
/// If this is true, then it implies that <see cref="IsConnected"/> is also true,
|
||||
/// so they don't need to be checked in addition to this.
|
||||
/// </summary>
|
||||
bool IsHighConfidence { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the body is currently tracked, thus tracking poses are available for the body
|
||||
/// root and joints.
|
||||
/// </summary>
|
||||
bool IsTrackedDataValid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The scale of the body skeleton applied to world joint poses.
|
||||
/// </summary>
|
||||
float Scale { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root pose of the body, 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>
|
||||
/// Attempts to calculate the pose of the requested body joint, in world space.
|
||||
/// Returns false if the skeleton is not yet initialized, or there is no valid
|
||||
/// tracking data. Scale is applied to this joint position.
|
||||
/// </summary>
|
||||
bool GetJointPose(BodyJointId bodyJointId, out Pose pose);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to calculate the pose of the requested body joint, in local space
|
||||
/// to its parent joint. Returns false if the skeleton is not yet initialized,
|
||||
/// or there is no valid tracking data. Scale is not applied.
|
||||
/// </summary>
|
||||
bool GetJointPoseLocal(BodyJointId bodyJointId, out Pose pose);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to calculate the pose of the requested hand joint relative to the root.
|
||||
/// Returns false if the skeleton is not yet initialized, or there is no valid
|
||||
/// tracking data. Scale is not applied.
|
||||
/// </summary>
|
||||
bool GetJointPoseFromRoot(BodyJointId bodyJointId, out Pose pose);
|
||||
|
||||
/// <summary>
|
||||
/// Incremented every time the source tracking or state data changes.
|
||||
/// </summary>
|
||||
int CurrentDataVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Called each time the body is updated with new data
|
||||
/// </summary>
|
||||
event Action WhenBodyUpdated;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f35e83192bb091044bae08a796e8858d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Collections;
|
||||
|
||||
namespace Oculus.Interaction.Body.Input
|
||||
{
|
||||
public interface ISkeletonMapping
|
||||
{
|
||||
/// <summary>
|
||||
/// The set of <see cref="BodyJointId"/>s supported by this skeleton
|
||||
/// </summary>
|
||||
IEnumerableHashSet<BodyJointId> Joints { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the parent joint for a given body joint
|
||||
/// </summary>
|
||||
/// <param name="jointId">The joint to fetch the parent for</param>
|
||||
/// <param name="parent">The parent joint</param>
|
||||
/// <returns>True if parent could be retrieved</returns>
|
||||
bool TryGetParentJointId(BodyJointId jointId, out BodyJointId parent);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65f8c7ef36409cb40a43b76f21309db3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 458a8f64bd4fde84a85ac79b6cb254eb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Oculus.Interaction.Body.Input;
|
||||
|
||||
namespace Oculus.Interaction.Body.PoseDetection
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares a user-provided set of joints between two Body Poses.
|
||||
/// </summary>
|
||||
public class BodyPoseComparerActiveState :
|
||||
MonoBehaviour, IActiveState
|
||||
{
|
||||
public struct BodyPoseComparerFeatureState
|
||||
{
|
||||
public readonly float Delta;
|
||||
|
||||
public readonly float MaxDelta;
|
||||
|
||||
public BodyPoseComparerFeatureState(float delta, float maxDelta)
|
||||
{
|
||||
Delta = delta;
|
||||
MaxDelta = maxDelta;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class JointComparerConfig
|
||||
{
|
||||
[Tooltip("The joint to compare from each Body Pose")]
|
||||
public BodyJointId Joint = BodyJointId.Body_Head;
|
||||
|
||||
[Min(0)]
|
||||
[Tooltip("The maximum angle that two joint rotations " +
|
||||
"can be from each other to be considered equal.")]
|
||||
public float MaxDelta = 30f;
|
||||
|
||||
[Tooltip("The width of the threshold when transitioning " +
|
||||
"states. " + nameof(Width) + " / 2 is added to " +
|
||||
nameof(MaxDelta) + " when leaving Active state, and " +
|
||||
"subtracted when entering.")]
|
||||
[Min(0)]
|
||||
public float Width = 4f;
|
||||
}
|
||||
|
||||
[Tooltip("The first body pose to compare.")]
|
||||
[SerializeField, Interface(typeof(IBodyPose))]
|
||||
private UnityEngine.Object _poseA;
|
||||
private IBodyPose PoseA;
|
||||
|
||||
[Tooltip("The second body pose to compare.")]
|
||||
[SerializeField, Interface(typeof(IBodyPose))]
|
||||
private UnityEngine.Object _poseB;
|
||||
private IBodyPose PoseB;
|
||||
|
||||
[SerializeField]
|
||||
private List<JointComparerConfig> _configs =
|
||||
new List<JointComparerConfig>()
|
||||
{
|
||||
new JointComparerConfig()
|
||||
};
|
||||
|
||||
[Tooltip("A new state must be maintaned for at least this " +
|
||||
"many seconds before the Active property changes.")]
|
||||
[SerializeField]
|
||||
private float _minTimeInState = 0.05f;
|
||||
|
||||
public float MinTimeInState
|
||||
{
|
||||
get => _minTimeInState;
|
||||
set => _minTimeInState = value;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<JointComparerConfig, BodyPoseComparerFeatureState> FeatureStates =>
|
||||
_featureStates;
|
||||
|
||||
private Dictionary<JointComparerConfig, BodyPoseComparerFeatureState> _featureStates =
|
||||
new Dictionary<JointComparerConfig, BodyPoseComparerFeatureState>();
|
||||
|
||||
private Func<float> _timeProvider;
|
||||
private bool _isActive;
|
||||
private bool _internalActive;
|
||||
private float _lastStateChangeTime;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
PoseA = _poseA as IBodyPose;
|
||||
PoseB = _poseB as IBodyPose;
|
||||
_timeProvider = () => Time.time;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.AssertField(PoseA, nameof(PoseA));
|
||||
this.AssertField(PoseB, nameof(PoseB));
|
||||
}
|
||||
|
||||
public bool Active
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!isActiveAndEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wasActive = _internalActive;
|
||||
_internalActive = true;
|
||||
foreach (var config in _configs)
|
||||
{
|
||||
float maxDelta = wasActive ?
|
||||
config.MaxDelta + config.Width / 2f :
|
||||
config.MaxDelta - config.Width / 2f;
|
||||
|
||||
bool withinDelta = GetJointDelta(config.Joint, out float delta) &&
|
||||
Mathf.Abs(delta) <= maxDelta;
|
||||
_featureStates[config] = new BodyPoseComparerFeatureState(delta, maxDelta);
|
||||
_internalActive &= withinDelta;
|
||||
}
|
||||
float time = _timeProvider();
|
||||
if (wasActive != _internalActive)
|
||||
{
|
||||
_lastStateChangeTime = time;
|
||||
}
|
||||
if (time - _lastStateChangeTime >= _minTimeInState)
|
||||
{
|
||||
_isActive = _internalActive;
|
||||
}
|
||||
return _isActive;
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetJointDelta(BodyJointId joint, out float delta)
|
||||
{
|
||||
if (!PoseA.GetJointPoseLocal(joint, out Pose localA) ||
|
||||
!PoseB.GetJointPoseLocal(joint, out Pose localB))
|
||||
{
|
||||
delta = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
delta = Quaternion.Angle(localA.rotation, localB.rotation);
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllBodyPoseComparerActiveState(
|
||||
IBodyPose poseA, IBodyPose poseB,
|
||||
IEnumerable<JointComparerConfig> configs)
|
||||
{
|
||||
InjectPoseA(poseA);
|
||||
InjectPoseB(poseB);
|
||||
InjectJoints(configs);
|
||||
}
|
||||
|
||||
public void InjectPoseA(IBodyPose poseA)
|
||||
{
|
||||
_poseA = poseA as UnityEngine.Object;
|
||||
PoseA = poseA;
|
||||
}
|
||||
|
||||
public void InjectPoseB(IBodyPose poseB)
|
||||
{
|
||||
_poseB = poseB as UnityEngine.Object;
|
||||
PoseB = poseB;
|
||||
}
|
||||
|
||||
public void InjectJoints(IEnumerable<JointComparerConfig> configs)
|
||||
{
|
||||
_configs = new List<JointComparerConfig>(configs);
|
||||
}
|
||||
|
||||
public void InjectOptionalTimeProvider(Func<float> timeProvider)
|
||||
{
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebf7b9e4cfb7d2e4aa5a2dccb5ee7d0f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Body.Input;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.Body.PoseDetection
|
||||
{
|
||||
public class BodyPoseComparerActiveStateDebugVisual : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The PoseComparer to debug.")]
|
||||
[SerializeField]
|
||||
private BodyPoseComparerActiveState _bodyPoseComparer;
|
||||
|
||||
[Tooltip("Gizmos will be drawn at joint positions of this body pose.")]
|
||||
[SerializeField, Interface(typeof(IBodyPose))]
|
||||
private UnityEngine.Object _bodyPose;
|
||||
private IBodyPose BodyPose;
|
||||
|
||||
[Tooltip("The root transform of the body skeleton. Debug " +
|
||||
"gizmos will be drawn in the local space of this transform.")]
|
||||
[SerializeField]
|
||||
private Transform _root;
|
||||
|
||||
[Tooltip("The radius of the debug spheres.")]
|
||||
[SerializeField, Delayed]
|
||||
private float _radius = 0.1f;
|
||||
|
||||
public float Radius
|
||||
{
|
||||
get => _radius;
|
||||
set => _radius = value;
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
BodyPose = _bodyPose as IBodyPose;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.AssertField(_bodyPoseComparer, nameof(_bodyPoseComparer));
|
||||
this.AssertField(BodyPose, nameof(BodyPose));
|
||||
this.AssertField(_root, nameof(BodyPose));
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
DrawJointSpheres();
|
||||
}
|
||||
|
||||
private void DrawJointSpheres()
|
||||
{
|
||||
var featureStates = _bodyPoseComparer.FeatureStates;
|
||||
foreach (var kvp in featureStates)
|
||||
{
|
||||
BodyJointId joint = kvp.Key.Joint;
|
||||
var state = kvp.Value;
|
||||
if (BodyPose.GetJointPoseFromRoot(joint, out Pose pose))
|
||||
{
|
||||
Vector3 jointPos = _root.TransformPoint(pose.position);
|
||||
|
||||
Color color;
|
||||
if (state.Delta <= state.MaxDelta)
|
||||
{
|
||||
color = Color.green;
|
||||
}
|
||||
else if (state.MaxDelta > 0)
|
||||
{
|
||||
float amt = (state.Delta / state.MaxDelta) / 2f;
|
||||
color = Color.Lerp(Color.yellow, Color.red, amt);
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.red;
|
||||
}
|
||||
|
||||
DebugGizmos.LineWidth = _radius / 2f;
|
||||
DebugGizmos.Color = color;
|
||||
DebugGizmos.DrawPoint(jointPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllBodyPoseComparerActiveStateDebugVisual(
|
||||
BodyPoseComparerActiveState bodyPoseComparer,
|
||||
IBodyPose bodyPose, Transform root)
|
||||
{
|
||||
InjectBodyPoseComparer(bodyPoseComparer);
|
||||
InjectBodyPose(bodyPose);
|
||||
InjectRootTransform(root);
|
||||
}
|
||||
|
||||
public void InjectRootTransform(Transform root)
|
||||
{
|
||||
_root = root;
|
||||
}
|
||||
|
||||
public void InjectBodyPoseComparer(BodyPoseComparerActiveState bodyPoseComparer)
|
||||
{
|
||||
_bodyPoseComparer = bodyPoseComparer;
|
||||
}
|
||||
|
||||
public void InjectBodyPose(IBodyPose bodyPose)
|
||||
{
|
||||
_bodyPose = bodyPose as UnityEngine.Object;
|
||||
BodyPose = bodyPose;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9faf130193937b348be91e09c530a810
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Oculus.Interaction.Body.Input;
|
||||
using System;
|
||||
using Oculus.Interaction.Collections;
|
||||
|
||||
namespace Oculus.Interaction.Body.PoseDetection
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Oculus/Interaction/SDK/Pose Detection/Body Pose")]
|
||||
public class BodyPoseData : ScriptableObject,
|
||||
IBodyPose, ISerializationCallbackReceiver
|
||||
{
|
||||
[System.Serializable]
|
||||
private struct JointData
|
||||
{
|
||||
public BodyJointId JointId;
|
||||
public BodyJointId ParentId;
|
||||
public Pose PoseFromRoot;
|
||||
public Pose LocalPose;
|
||||
}
|
||||
|
||||
private class Mapping : ISkeletonMapping
|
||||
{
|
||||
public EnumerableHashSet<BodyJointId> Joints =
|
||||
new EnumerableHashSet<BodyJointId>();
|
||||
|
||||
public Dictionary<BodyJointId, BodyJointId> JointToParent =
|
||||
new Dictionary<BodyJointId, BodyJointId>();
|
||||
|
||||
IEnumerableHashSet<BodyJointId> ISkeletonMapping.Joints => Joints;
|
||||
|
||||
bool ISkeletonMapping.TryGetParentJointId(BodyJointId jointId, out BodyJointId parent) =>
|
||||
JointToParent.TryGetValue(jointId, out parent);
|
||||
}
|
||||
|
||||
public event Action WhenBodyPoseUpdated = delegate { };
|
||||
|
||||
[SerializeField, HideInInspector]
|
||||
private List<JointData> _jointData = new List<JointData>();
|
||||
|
||||
private Dictionary<BodyJointId, Pose> _posesFromRoot =
|
||||
new Dictionary<BodyJointId, Pose>();
|
||||
|
||||
private Dictionary<BodyJointId, Pose> _localPoses =
|
||||
new Dictionary<BodyJointId, Pose>();
|
||||
|
||||
private Mapping _mapping = new Mapping();
|
||||
|
||||
public bool GetJointPoseFromRoot(BodyJointId bodyJointId, out Pose pose) =>
|
||||
_posesFromRoot.TryGetValue(bodyJointId, out pose);
|
||||
|
||||
public bool GetJointPoseLocal(BodyJointId bodyJointId, out Pose pose) =>
|
||||
_localPoses.TryGetValue(bodyJointId, out pose);
|
||||
|
||||
public ISkeletonMapping SkeletonMapping => _mapping;
|
||||
|
||||
public void SetBodyPose(IBody body)
|
||||
{
|
||||
_jointData.Clear();
|
||||
foreach (var joint in body.SkeletonMapping.Joints)
|
||||
{
|
||||
if (body.GetJointPoseLocal(joint, out Pose local) &&
|
||||
body.GetJointPoseFromRoot(joint, out Pose fromRoot) &&
|
||||
body.SkeletonMapping.TryGetParentJointId(joint, out BodyJointId parent))
|
||||
{
|
||||
_jointData.Add(new JointData()
|
||||
{
|
||||
JointId = joint,
|
||||
ParentId = parent,
|
||||
PoseFromRoot = fromRoot,
|
||||
LocalPose = local,
|
||||
});
|
||||
}
|
||||
}
|
||||
Rebuild();
|
||||
WhenBodyPoseUpdated.Invoke();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
_localPoses.Clear();
|
||||
_posesFromRoot.Clear();
|
||||
_mapping.Joints.Clear();
|
||||
_mapping.JointToParent.Clear();
|
||||
|
||||
for (int i = 0; i < _jointData.Count; ++i)
|
||||
{
|
||||
_localPoses[_jointData[i].JointId] =
|
||||
_jointData[i].LocalPose;
|
||||
_posesFromRoot[_jointData[i].JointId] =
|
||||
_jointData[i].PoseFromRoot;
|
||||
_mapping.Joints.Add(
|
||||
_jointData[i].JointId);
|
||||
_mapping.JointToParent.Add(
|
||||
_jointData[i].JointId, _jointData[i].ParentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55ca4a17febb5bb4084ecb9e8cd230cc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using Oculus.Interaction.Body.Input;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Oculus.Interaction.Body.PoseDetection
|
||||
{
|
||||
public class BodyPoseDebugGizmos : SkeletonDebugGizmos
|
||||
{
|
||||
[SerializeField, Interface(typeof(IBodyPose))]
|
||||
private UnityEngine.Object _bodyPose;
|
||||
private IBodyPose BodyPose;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
BodyPose = _bodyPose as IBodyPose;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
Assert.IsNotNull(BodyPose);
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
foreach (BodyJointId joint in BodyPose.SkeletonMapping.Joints)
|
||||
{
|
||||
Draw(joint, GetVisibilityFlags());
|
||||
}
|
||||
}
|
||||
|
||||
private VisibilityFlags GetVisibilityFlags()
|
||||
{
|
||||
VisibilityFlags modifiedFlags = Visibility;
|
||||
if (HasNegativeScale)
|
||||
{
|
||||
modifiedFlags &= ~VisibilityFlags.Axes;
|
||||
}
|
||||
return modifiedFlags;
|
||||
}
|
||||
|
||||
protected override bool TryGetWorldJointPose(BodyJointId jointId, out Pose pose)
|
||||
{
|
||||
if (BodyPose.GetJointPoseFromRoot(jointId, out pose))
|
||||
{
|
||||
pose.position = transform.TransformPoint(pose.position);
|
||||
pose.rotation = transform.rotation * pose.rotation;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool TryGetParentJointId(BodyJointId jointId, out BodyJointId parent)
|
||||
{
|
||||
return BodyPose.SkeletonMapping.TryGetParentJointId(jointId, out parent);
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllBodyJointDebugGizmos(IBodyPose bodyPose)
|
||||
{
|
||||
InjectBodyPose(bodyPose);
|
||||
}
|
||||
|
||||
public void InjectBodyPose(IBodyPose bodyPose)
|
||||
{
|
||||
_bodyPose = bodyPose as UnityEngine.Object;
|
||||
BodyPose = bodyPose;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: feb0f50b1a0593647b5de2d2b20029a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Oculus.Interaction.Body.Input;
|
||||
|
||||
namespace Oculus.Interaction.Body.PoseDetection
|
||||
{
|
||||
public interface IBodyPose
|
||||
{
|
||||
/// <summary>
|
||||
/// Called each time the body pose is updated with new data
|
||||
/// </summary>
|
||||
event Action WhenBodyPoseUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// The mapping of the skeleton
|
||||
/// </summary>
|
||||
ISkeletonMapping SkeletonMapping { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to return the pose of the requested body joint,
|
||||
/// in local space relative to its parent joint.
|
||||
/// </summary>
|
||||
bool GetJointPoseLocal(BodyJointId bodyJointId, out Pose pose);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to return the pose of the requested body joint relative
|
||||
/// to the root joint <see cref="BodyJointId.Body_Root"/>.
|
||||
/// </summary>
|
||||
bool GetJointPoseFromRoot(BodyJointId bodyJointId, out Pose pose);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2eac04d5eb0c554bbbdcdcb2df831c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Oculus.Interaction.Body.Input;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Oculus.Interaction.Body.PoseDetection
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes an <see cref="IBodyPose"/> from an <see cref="IBody"/>
|
||||
/// </summary>
|
||||
public class PoseFromBody : MonoBehaviour, IBodyPose
|
||||
{
|
||||
public event Action WhenBodyPoseUpdated = delegate { };
|
||||
|
||||
[Tooltip("The IBodyPose will be derived from this IBody.")]
|
||||
[SerializeField, Interface(typeof(IBody))]
|
||||
private UnityEngine.Object _body;
|
||||
private IBody Body;
|
||||
|
||||
[Tooltip("If true, this component will track the provided IBody as " +
|
||||
"its data is updated. If false, you must call " +
|
||||
nameof(UpdatePose) + " to update joint data.")]
|
||||
[SerializeField]
|
||||
private bool _autoUpdate = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, this component will track the provided IBody as
|
||||
/// its data is updated. If false, you must call
|
||||
/// <see cref="UpdatePose"/> to update joint data.
|
||||
/// </summary>
|
||||
public bool AutoUpdate
|
||||
{
|
||||
get => _autoUpdate;
|
||||
set => _autoUpdate = value;
|
||||
}
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
private Dictionary<BodyJointId, Pose> _jointPosesLocal;
|
||||
private Dictionary<BodyJointId, Pose> _jointPosesFromRoot;
|
||||
|
||||
public ISkeletonMapping SkeletonMapping => Body.SkeletonMapping;
|
||||
|
||||
public bool GetJointPoseLocal(BodyJointId bodyJointId, out Pose pose) =>
|
||||
_jointPosesLocal.TryGetValue(bodyJointId, out pose);
|
||||
|
||||
public bool GetJointPoseFromRoot(BodyJointId bodyJointId, out Pose pose) =>
|
||||
_jointPosesFromRoot.TryGetValue(bodyJointId, out pose);
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
_jointPosesLocal = new Dictionary<BodyJointId, Pose>();
|
||||
_jointPosesFromRoot = new Dictionary<BodyJointId, Pose>();
|
||||
Body = _body as IBody;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(Body, nameof(Body));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Body.WhenBodyUpdated += Body_WhenBodyUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Body.WhenBodyUpdated -= Body_WhenBodyUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
private void Body_WhenBodyUpdated()
|
||||
{
|
||||
if (_autoUpdate)
|
||||
{
|
||||
UpdatePose();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePose()
|
||||
{
|
||||
_jointPosesLocal.Clear();
|
||||
_jointPosesFromRoot.Clear();
|
||||
|
||||
foreach (var joint in Body.SkeletonMapping.Joints)
|
||||
{
|
||||
if (Body.GetJointPoseLocal(joint,
|
||||
out Pose localPose))
|
||||
{
|
||||
_jointPosesLocal[joint] = localPose;
|
||||
}
|
||||
if (Body.GetJointPoseFromRoot(joint,
|
||||
out Pose poseFromRoot))
|
||||
{
|
||||
_jointPosesFromRoot[joint] = poseFromRoot;
|
||||
}
|
||||
}
|
||||
|
||||
WhenBodyPoseUpdated.Invoke();
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllPoseFromBody(IBody body)
|
||||
{
|
||||
InjectBody(body);
|
||||
}
|
||||
|
||||
public void InjectBody(IBody body)
|
||||
{
|
||||
_body = body as UnityEngine.Object;
|
||||
Body = body;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2be7e1a3256ca942865ee433ce9e3e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using Oculus.Interaction.Body.Input;
|
||||
|
||||
namespace Oculus.Interaction.Body
|
||||
{
|
||||
public abstract class SkeletonDebugGizmos : MonoBehaviour
|
||||
{
|
||||
[System.Flags]
|
||||
public enum VisibilityFlags
|
||||
{
|
||||
Joints = 1 << 0,
|
||||
Axes = 1 << 1,
|
||||
Bones = 1 << 2,
|
||||
}
|
||||
|
||||
[Tooltip("Which components of the skeleton will be visualized.")]
|
||||
[SerializeField]
|
||||
private VisibilityFlags _visibility =
|
||||
VisibilityFlags.Axes | VisibilityFlags.Joints;
|
||||
|
||||
[Tooltip("The joint debug spheres will be drawn with this color.")]
|
||||
[SerializeField]
|
||||
private Color _jointColor = Color.white;
|
||||
|
||||
[Tooltip("The bone connecting lines will be drawn with this color.")]
|
||||
[SerializeField]
|
||||
private Color _boneColor = Color.gray;
|
||||
|
||||
[Tooltip("The radius of the joint spheres and the thickness " +
|
||||
"of the bone and axis lines.")]
|
||||
[SerializeField]
|
||||
private float _radius = 0.02f;
|
||||
|
||||
public float Radius
|
||||
{
|
||||
get => _radius;
|
||||
set => _radius = value;
|
||||
}
|
||||
|
||||
public VisibilityFlags Visibility
|
||||
{
|
||||
get => _visibility;
|
||||
set => _visibility = value;
|
||||
}
|
||||
|
||||
public Color JointColor
|
||||
{
|
||||
get => _jointColor;
|
||||
set => _jointColor = value;
|
||||
}
|
||||
|
||||
public Color BoneColor
|
||||
{
|
||||
get => _boneColor;
|
||||
set => _boneColor = value;
|
||||
}
|
||||
|
||||
private float LineWidth => _radius / 2f;
|
||||
|
||||
protected abstract bool TryGetWorldJointPose(BodyJointId jointId, out Pose pose);
|
||||
|
||||
protected abstract bool TryGetParentJointId(BodyJointId jointId, out BodyJointId parent);
|
||||
|
||||
protected bool HasNegativeScale => transform.lossyScale.x < 0 ||
|
||||
transform.lossyScale.y < 0 ||
|
||||
transform.lossyScale.z < 0;
|
||||
|
||||
protected void Draw(BodyJointId joint, VisibilityFlags visibility)
|
||||
{
|
||||
if (TryGetWorldJointPose(joint, out Pose pose))
|
||||
{
|
||||
if (visibility.HasFlag(VisibilityFlags.Axes))
|
||||
{
|
||||
DebugGizmos.LineWidth = LineWidth;
|
||||
DebugGizmos.DrawAxis(pose, _radius);
|
||||
}
|
||||
if (visibility.HasFlag(VisibilityFlags.Joints))
|
||||
{
|
||||
DebugGizmos.Color = _jointColor;
|
||||
DebugGizmos.LineWidth = _radius;
|
||||
DebugGizmos.DrawPoint(pose.position);
|
||||
}
|
||||
if (visibility.HasFlag(VisibilityFlags.Bones) &&
|
||||
TryGetParentJointId(joint, out BodyJointId parent) &&
|
||||
TryGetWorldJointPose(parent, out Pose parentPose))
|
||||
{
|
||||
DebugGizmos.Color = _boneColor;
|
||||
DebugGizmos.LineWidth = LineWidth;
|
||||
DebugGizmos.DrawLine(pose.position, parentPose.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 179ced13df8b20443aaf9b57fdee24ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2878b4bd6d973d4d8a58a56afea4e63
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Oculus.Interaction.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes a GetEnumerator method with a non-allocating
|
||||
/// HashSet.Enumerator struct.
|
||||
/// </summary>
|
||||
public interface IEnumerableHashSet<T> : IEnumerable<T>
|
||||
{
|
||||
int Count { get; }
|
||||
new HashSet<T>.Enumerator GetEnumerator();
|
||||
bool Contains(T item);
|
||||
bool IsProperSubsetOf(IEnumerable<T> other);
|
||||
bool IsProperSupersetOf(IEnumerable<T> other);
|
||||
bool IsSubsetOf(IEnumerable<T> other);
|
||||
bool IsSupersetOf(IEnumerable<T> other);
|
||||
public bool Overlaps(IEnumerable<T> other);
|
||||
public bool SetEquals(IEnumerable<T> other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Hash set that implements the <see cref="IEnumerableHashSet{T}"/>
|
||||
/// interface, to use for non-allocating iteration of a HashSet
|
||||
/// </summary>
|
||||
public class EnumerableHashSet<T> : HashSet<T>, IEnumerableHashSet<T>
|
||||
{
|
||||
public EnumerableHashSet() : base() { }
|
||||
public EnumerableHashSet(IEnumerable<T> values) : base(values) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aaf955ff104833c44874f8763203ee16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2e4a61fac5024b4587fff4b305431a4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public static partial class Collisions
|
||||
{
|
||||
public static Vector3 ClosestPointToColliders(Vector3 point, Collider[] colliders)
|
||||
{
|
||||
Vector3 closestPoint = point;
|
||||
float closestDistance = float.MaxValue;
|
||||
foreach (Collider collider in colliders)
|
||||
{
|
||||
if (Collisions.IsPointWithinCollider(point, collider))
|
||||
{
|
||||
return point;
|
||||
}
|
||||
|
||||
Vector3 closest = collider.ClosestPoint(point);
|
||||
float distance = (closest - point).magnitude;
|
||||
if (distance < closestDistance)
|
||||
{
|
||||
closestDistance = distance;
|
||||
closestPoint = closest;
|
||||
}
|
||||
}
|
||||
|
||||
return closestPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd30b3e03b097864ba1f2119b21e222e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public class ColliderGroup
|
||||
{
|
||||
private Collider _boundsCollider;
|
||||
private List<Collider> _colliders;
|
||||
|
||||
public Collider Bounds => _boundsCollider;
|
||||
public List<Collider> Colliders => _colliders;
|
||||
|
||||
public ColliderGroup(List<Collider> colliders, Collider boundsCollider)
|
||||
{
|
||||
_colliders = colliders;
|
||||
_boundsCollider = boundsCollider;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08918bb687dee3143a956803427ad287
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public static partial class Collisions
|
||||
{
|
||||
/// <summary>
|
||||
/// Approximate capsule collision by doing sphere collisions down the capsule length
|
||||
/// </summary>
|
||||
/// <param name="p0">Capsule Start</param>
|
||||
/// <param name="p1">Capsule End</param>
|
||||
/// <param name="radius">Capsule Radius</param>
|
||||
/// <param name="collider">Collider to check against</param>
|
||||
/// <returns>Whether or not an approximate collision occured.</returns>
|
||||
public static bool IsCapsuleWithinColliderApprox(Vector3 p0, Vector3 p1, float radius, Collider collider)
|
||||
{
|
||||
int divisions = Mathf.CeilToInt((p1 - p0).magnitude / radius) * 2;
|
||||
|
||||
if (divisions == 0)
|
||||
{
|
||||
return IsSphereWithinCollider(p0, radius, collider);
|
||||
}
|
||||
|
||||
float tStep = 1f / divisions;
|
||||
for (int i = 0; i <= divisions; i++)
|
||||
{
|
||||
Vector3 point = Vector3.Lerp(p0, p1, tStep * i);
|
||||
if (IsSphereWithinCollider(point, radius, collider))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0502bb2a0224d844b255565fd926a1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public static partial class Collisions
|
||||
{
|
||||
public static bool IsPointWithinCollider(Vector3 point, Collider collider)
|
||||
{
|
||||
if (!collider.bounds.Contains(point))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 closestPoint = collider.ClosestPoint(point);
|
||||
if (collider is MeshCollider)
|
||||
{
|
||||
return (closestPoint - point).sqrMagnitude < collider.contactOffset * collider.contactOffset;
|
||||
}
|
||||
return closestPoint.Equals(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c02bb45a336bd74ba30737c1fcd2ebe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public static partial class Collisions
|
||||
{
|
||||
public static bool IsSphereWithinCollider(Vector3 point, float radius, Collider collider)
|
||||
{
|
||||
Vector3 boundsPoint = collider.bounds.ClosestPoint(point);
|
||||
if (Vector3.SqrMagnitude(boundsPoint - point) > radius * radius)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 closestPoint = collider.ClosestPoint(point);
|
||||
return Vector3.SqrMagnitude(closestPoint - point) <= radius * radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94a7474e6d755af4e88ddde6c0d4e9d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7c845fdfd564b4479b64fb4ea7ea439
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
[System.Serializable]
|
||||
public struct DistantPointDetectorFrustums
|
||||
{
|
||||
/// <summary>
|
||||
/// The main frustum, items inside this frustum will be selected.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
private ConicalFrustum _selectionFrustum;
|
||||
/// <summary>
|
||||
/// Selected items within this optional frustum will keep being selected.
|
||||
/// It should be wider than the selection frustum.
|
||||
/// </summary>
|
||||
[SerializeField, Optional]
|
||||
private ConicalFrustum _deselectionFrustum;
|
||||
/// <summary>
|
||||
/// When provided, items need to be also inside this frustum as well as the
|
||||
/// selection one to be selected.
|
||||
/// e.g. Selection frustum coming out from the hand, and aid frustum from the
|
||||
/// head so items need to be pointed at with the gaze and hand.
|
||||
/// </summary>
|
||||
[SerializeField, Optional]
|
||||
private ConicalFrustum _aidFrustum;
|
||||
/// <summary>
|
||||
/// Blends the scores between the Selection and Aid frustums.
|
||||
/// At 0 items centered in the selection frustum are given preference, at one
|
||||
/// items centered at the aid frustum are given preference.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
[Range(0f, 1f)]
|
||||
private float _aidBlending;
|
||||
|
||||
public ConicalFrustum SelectionFrustum => _selectionFrustum;
|
||||
public ConicalFrustum DeselectionFrustum => _deselectionFrustum;
|
||||
public ConicalFrustum AidFrustum => _aidFrustum;
|
||||
public float AidBlending => _aidBlending;
|
||||
|
||||
public DistantPointDetectorFrustums(ConicalFrustum selection,
|
||||
ConicalFrustum deselection, ConicalFrustum aid, float blend)
|
||||
{
|
||||
_selectionFrustum = selection;
|
||||
_deselectionFrustum = deselection;
|
||||
_aidFrustum = aid;
|
||||
_aidBlending = blend;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class contains the logic for finding the best candidate among a series of colliders.
|
||||
/// It uses up to three conical frustums from DistantPointDetectorFrustums for selection and deselection.
|
||||
/// </summary>
|
||||
public class DistantPointDetector
|
||||
{
|
||||
private DistantPointDetectorFrustums _frustums;
|
||||
|
||||
public DistantPointDetector(DistantPointDetectorFrustums frustums)
|
||||
{
|
||||
_frustums = frustums;
|
||||
}
|
||||
|
||||
public bool ComputeIsPointing(Collider[] colliders, bool isSelecting, out float bestScore, out Vector3 bestHitPoint)
|
||||
{
|
||||
ConicalFrustum searchFrustrum = (isSelecting || _frustums.DeselectionFrustum == null) ?
|
||||
_frustums.SelectionFrustum : _frustums.DeselectionFrustum;
|
||||
bestHitPoint = Vector3.zero;
|
||||
bestScore = float.NegativeInfinity;
|
||||
bool anyHit = false;
|
||||
|
||||
foreach (Collider collider in colliders)
|
||||
{
|
||||
float score = 0f;
|
||||
if (!searchFrustrum.HitsCollider(collider, out score, out Vector3 hitPoint))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_frustums.AidFrustum != null)
|
||||
{
|
||||
if (!_frustums.AidFrustum.HitsCollider(collider, out float headScore, out Vector3 headPosition))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
score = score * (1f - _frustums.AidBlending) + headScore * _frustums.AidBlending;
|
||||
}
|
||||
|
||||
if (score > bestScore)
|
||||
{
|
||||
bestHitPoint = hitPoint;
|
||||
bestScore = score;
|
||||
anyHit = true;
|
||||
}
|
||||
}
|
||||
return anyHit;
|
||||
}
|
||||
|
||||
public bool IsPointingWithoutAid(Collider[] colliders, out Vector3 bestHitPoint)
|
||||
{
|
||||
if (_frustums.AidFrustum == null)
|
||||
{
|
||||
bestHitPoint = Vector3.zero;
|
||||
return false;
|
||||
}
|
||||
return !IsPointingAtColliders(colliders, _frustums.AidFrustum, out bestHitPoint)
|
||||
&& IsWithinDeselectionRange(colliders);
|
||||
}
|
||||
|
||||
public bool IsWithinDeselectionRange(Collider[] colliders)
|
||||
{
|
||||
return IsPointingAtColliders(colliders, _frustums.DeselectionFrustum)
|
||||
|| IsPointingAtColliders(colliders, _frustums.SelectionFrustum);
|
||||
}
|
||||
|
||||
private bool IsPointingAtColliders(Collider[] colliders, ConicalFrustum frustum)
|
||||
{
|
||||
if (frustum == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (Collider collider in colliders)
|
||||
{
|
||||
if (frustum.HitsCollider(collider, out float score, out Vector3 point))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsPointingAtColliders(Collider[] colliders, ConicalFrustum frustum,
|
||||
out Vector3 bestHitPoint)
|
||||
{
|
||||
bestHitPoint = Vector3.zero;
|
||||
float bestScore = float.NegativeInfinity;
|
||||
bool isPointing = false;
|
||||
if (frustum == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foreach (Collider collider in colliders)
|
||||
{
|
||||
if (frustum.HitsCollider(collider, out float score, out Vector3 point))
|
||||
{
|
||||
isPointing = true;
|
||||
if (score > bestScore)
|
||||
{
|
||||
bestScore = score;
|
||||
bestHitPoint = point;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isPointing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05fe4263bfbefbf47aefffbb9ebe67af
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public interface IDistanceInteractor : IInteractorView
|
||||
{
|
||||
Pose Origin { get; }
|
||||
Vector3 HitPoint { get; }
|
||||
IRelativeToRef DistanceInteractable { get; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a8d861252f63724a86051cd634ae779
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d632d200a811f14ebf827e030b6e2a2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public class DistantInteractionLineRendererVisual : DistantInteractionLineVisual
|
||||
{
|
||||
[SerializeField]
|
||||
private LineRenderer _lineRenderer;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
this.AssertField(_lineRenderer, nameof(_lineRenderer));
|
||||
_lineRenderer.positionCount = NumLinePoints;
|
||||
}
|
||||
|
||||
protected override void RenderLine(Vector3[] linePoints)
|
||||
{
|
||||
_lineRenderer.SetPositions(linePoints);
|
||||
_lineRenderer.enabled = true;
|
||||
}
|
||||
|
||||
protected override void HideLine()
|
||||
{
|
||||
_lineRenderer.enabled = false;
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllDistantInteractionLineRendererVisual(IDistanceInteractor interactor,
|
||||
LineRenderer lineRenderer)
|
||||
{
|
||||
InjectDistanceInteractor(interactor);
|
||||
InjectLineRenderer(lineRenderer);
|
||||
}
|
||||
|
||||
public void InjectLineRenderer(LineRenderer lineRenderer)
|
||||
{
|
||||
_lineRenderer = lineRenderer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49d2336476ec912499a3c0aff23be4e4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public abstract class DistantInteractionLineVisual : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Interface(typeof(IDistanceInteractor))]
|
||||
private UnityEngine.Object _distanceInteractor;
|
||||
public IDistanceInteractor DistanceInteractor { get; protected set; }
|
||||
|
||||
[SerializeField]
|
||||
private float _visualOffset = 0.07f;
|
||||
public float VisualOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
return _visualOffset;
|
||||
}
|
||||
set
|
||||
{
|
||||
_visualOffset = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3[] _linePoints;
|
||||
|
||||
[SerializeField]
|
||||
private bool _visibleDuringNormal;
|
||||
private IReticleData _target;
|
||||
|
||||
[SerializeField]
|
||||
private int _numLinePoints = 20;
|
||||
protected int NumLinePoints => _numLinePoints;
|
||||
|
||||
[SerializeField]
|
||||
private float _targetlessLength = 0.5f;
|
||||
protected float TargetlessLength => _targetlessLength;
|
||||
|
||||
protected bool _started;
|
||||
private bool _shouldDrawLine;
|
||||
private DummyPointReticle _dummyTarget = new DummyPointReticle();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
DistanceInteractor = _distanceInteractor as IDistanceInteractor;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(DistanceInteractor, nameof(DistanceInteractor));
|
||||
_linePoints = new Vector3[NumLinePoints];
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
DistanceInteractor.WhenStateChanged += HandleStateChanged;
|
||||
DistanceInteractor.WhenPostprocessed += HandlePostProcessed;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
DistanceInteractor.WhenStateChanged -= HandleStateChanged;
|
||||
DistanceInteractor.WhenPostprocessed -= HandlePostProcessed;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleStateChanged(InteractorStateChangeArgs args)
|
||||
{
|
||||
switch (args.NewState)
|
||||
{
|
||||
case InteractorState.Normal:
|
||||
if (args.PreviousState != InteractorState.Disabled)
|
||||
{
|
||||
InteractableUnset();
|
||||
}
|
||||
|
||||
break;
|
||||
case InteractorState.Hover:
|
||||
if (args.PreviousState == InteractorState.Normal)
|
||||
{
|
||||
InteractableSet(DistanceInteractor.DistanceInteractable);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (args.NewState == InteractorState.Select
|
||||
|| args.NewState == InteractorState.Disabled
|
||||
|| args.PreviousState == InteractorState.Disabled)
|
||||
{
|
||||
_shouldDrawLine = false;
|
||||
}
|
||||
else if (args.NewState == InteractorState.Hover)
|
||||
{
|
||||
_shouldDrawLine = true;
|
||||
}
|
||||
else if (args.NewState == InteractorState.Normal)
|
||||
{
|
||||
_shouldDrawLine = _visibleDuringNormal;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePostProcessed()
|
||||
{
|
||||
if (_shouldDrawLine)
|
||||
{
|
||||
UpdateLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
HideLine();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void InteractableSet(IRelativeToRef interactable)
|
||||
{
|
||||
Component component = interactable as Component;
|
||||
if (component == null)
|
||||
{
|
||||
_target = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!component.TryGetComponent(out _target))
|
||||
{
|
||||
_dummyTarget.Target = interactable.RelativeTo;
|
||||
_target = _dummyTarget;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void InteractableUnset()
|
||||
{
|
||||
_target = null;
|
||||
}
|
||||
|
||||
private void UpdateLine()
|
||||
{
|
||||
Vector3 direction = DistanceInteractor.Origin.forward;
|
||||
Vector3 origin = DistanceInteractor.Origin.position;
|
||||
Vector3 start = origin + direction * VisualOffset;
|
||||
Vector3 end = TargetHit(DistanceInteractor.HitPoint);
|
||||
Vector3 middle = start + direction * Vector3.Distance(start, end) * 0.5f;
|
||||
|
||||
for (int i = 0; i < NumLinePoints; i++)
|
||||
{
|
||||
float t = i / (NumLinePoints - 1f);
|
||||
Vector3 point = EvaluateBezier(start, middle, end, t);
|
||||
_linePoints[i] = point;
|
||||
}
|
||||
|
||||
RenderLine(_linePoints);
|
||||
}
|
||||
|
||||
protected abstract void RenderLine(Vector3[] linePoints);
|
||||
protected abstract void HideLine();
|
||||
|
||||
protected Vector3 TargetHit(Vector3 hitPoint)
|
||||
{
|
||||
if (_target != null)
|
||||
{
|
||||
return _target.ProcessHitPoint(hitPoint);
|
||||
}
|
||||
|
||||
return DistanceInteractor.Origin.position
|
||||
+ DistanceInteractor.Origin.forward * _targetlessLength;
|
||||
}
|
||||
|
||||
protected static Vector3 EvaluateBezier(Vector3 start, Vector3 middle, Vector3 end, float t)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
float oneMinusT = 1f - t;
|
||||
return (oneMinusT * oneMinusT * start)
|
||||
+ (2f * oneMinusT * t * middle)
|
||||
+ (t * t * end);
|
||||
}
|
||||
|
||||
private class DummyPointReticle : IReticleData
|
||||
{
|
||||
public Transform Target { get; set; }
|
||||
|
||||
public Vector3 ProcessHitPoint(Vector3 hitPoint)
|
||||
{
|
||||
return Target.position;
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllDistantInteractionLineVisual(IDistanceInteractor interactor)
|
||||
{
|
||||
InjectDistanceInteractor(interactor);
|
||||
}
|
||||
|
||||
public void InjectDistanceInteractor(IDistanceInteractor interactor)
|
||||
{
|
||||
_distanceInteractor = interactor as UnityEngine.Object;
|
||||
DistanceInteractor = interactor;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63dcf920b7ab4c74c816cf2144feb287
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public class DistantInteractionPolylineVisual : DistantInteractionLineVisual
|
||||
{
|
||||
[SerializeField]
|
||||
private Color _color = Color.white;
|
||||
|
||||
public Color Color
|
||||
{
|
||||
get
|
||||
{
|
||||
return _color;
|
||||
}
|
||||
set
|
||||
{
|
||||
_color = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float _lineWidth = 0.02f;
|
||||
public float LineWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
return _lineWidth;
|
||||
}
|
||||
set
|
||||
{
|
||||
_lineWidth = value;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Vector4> _linePointsVec4;
|
||||
|
||||
[SerializeField]
|
||||
private Material _lineMaterial;
|
||||
|
||||
private PolylineRenderer _polylineRenderer;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
this.AssertField(_lineMaterial, nameof(_lineMaterial));
|
||||
_polylineRenderer = new PolylineRenderer(_lineMaterial);
|
||||
_linePointsVec4 = new List<Vector4>(new Vector4[NumLinePoints]);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_polylineRenderer.Cleanup();
|
||||
}
|
||||
|
||||
protected override void RenderLine(Vector3[] linePoints)
|
||||
{
|
||||
for (int i = 0; i < linePoints.Length; i++)
|
||||
{
|
||||
Vector3 p = linePoints[i];
|
||||
_linePointsVec4[i] = new Vector4(p.x, p.y, p.z, _lineWidth);
|
||||
}
|
||||
_polylineRenderer.SetLines(_linePointsVec4, _color);
|
||||
_polylineRenderer.RenderLines();
|
||||
}
|
||||
|
||||
protected override void HideLine()
|
||||
{
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllDistantInteractionPolylineVisual(IDistanceInteractor interactor,
|
||||
Color color, Material material)
|
||||
{
|
||||
InjectDistanceInteractor(interactor);
|
||||
InjectLineColor(color);
|
||||
InjectLineMaterial(material);
|
||||
}
|
||||
|
||||
public void InjectLineColor(Color color)
|
||||
{
|
||||
_color = color;
|
||||
}
|
||||
|
||||
public void InjectLineMaterial(Material material)
|
||||
{
|
||||
_lineMaterial = material;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec4a79a4dec34914e91ea89f5cd85cdf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public class DistantInteractionTubeVisual : DistantInteractionLineVisual
|
||||
{
|
||||
[SerializeField]
|
||||
private TubeRenderer _tubeRenderer;
|
||||
|
||||
private TubePoint[] _tubePoints;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
Assert.IsNotNull(_tubeRenderer);
|
||||
}
|
||||
|
||||
protected override void RenderLine(Vector3[] linePoints)
|
||||
{
|
||||
InitializeArcPoints(linePoints);
|
||||
_tubeRenderer.RenderTube(_tubePoints, Space.World);
|
||||
}
|
||||
|
||||
protected override void HideLine()
|
||||
{
|
||||
_tubeRenderer.Hide();
|
||||
}
|
||||
|
||||
private void InitializeArcPoints(Vector3[] linePoints)
|
||||
{
|
||||
if (_tubePoints == null
|
||||
|| _tubePoints.Length < linePoints.Length)
|
||||
{
|
||||
_tubePoints = new TubePoint[linePoints.Length];
|
||||
}
|
||||
|
||||
float totalLength = 0f;
|
||||
for (int i = 1; i < linePoints.Length; i++)
|
||||
{
|
||||
totalLength += (linePoints[i] - linePoints[i - 1]).magnitude;
|
||||
}
|
||||
|
||||
for (int i = 0; i < linePoints.Length; i++)
|
||||
{
|
||||
Vector3 difference = i == 0 ? linePoints[i + 1] - linePoints[i]
|
||||
: linePoints[i] - linePoints[i - 1];
|
||||
_tubePoints[i].position = linePoints[i];
|
||||
_tubePoints[i].rotation = Quaternion.LookRotation(difference);
|
||||
_tubePoints[i].relativeLength = i == 0 ? 0f
|
||||
: _tubePoints[i - 1].relativeLength + (difference.magnitude / totalLength);
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllDistantInteractionPolylineVisual(IDistanceInteractor interactor)
|
||||
{
|
||||
InjectDistanceInteractor(interactor);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50f211aff3d26554691f0499eaa219e5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public interface IReticleData
|
||||
{
|
||||
Vector3 ProcessHitPoint(Vector3 hitPoint);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02f66d477a24d6846bb998939cfc493e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public abstract class InteractorReticle<TReticleData> : MonoBehaviour
|
||||
where TReticleData : class, IReticleData
|
||||
{
|
||||
[SerializeField]
|
||||
private bool _visibleDuringSelect = false;
|
||||
private bool VisibleDuringSelect
|
||||
{
|
||||
get
|
||||
{
|
||||
return _visibleDuringSelect;
|
||||
}
|
||||
set
|
||||
{
|
||||
_visibleDuringSelect = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool _started;
|
||||
protected TReticleData _targetData;
|
||||
private bool _drawn;
|
||||
|
||||
protected abstract IInteractorView Interactor { get; set; }
|
||||
protected abstract Component InteractableComponent { get; }
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(Interactor, nameof(Interactor));
|
||||
Hide();
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Interactor.WhenStateChanged += HandleStateChanged;
|
||||
Interactor.WhenPostprocessed += HandlePostProcessed;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Interactor.WhenStateChanged -= HandleStateChanged;
|
||||
Interactor.WhenPostprocessed -= HandlePostProcessed;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleStateChanged(InteractorStateChangeArgs args)
|
||||
{
|
||||
if (args.NewState == InteractorState.Normal
|
||||
|| args.NewState == InteractorState.Disabled)
|
||||
{
|
||||
InteractableUnset();
|
||||
}
|
||||
else if (args.NewState == InteractorState.Hover
|
||||
&& args.PreviousState != InteractorState.Select)
|
||||
{
|
||||
InteractableSet(InteractableComponent);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePostProcessed()
|
||||
{
|
||||
if (_targetData != null
|
||||
&& (Interactor.State == InteractorState.Hover
|
||||
|| (Interactor.State == InteractorState.Select && _visibleDuringSelect)))
|
||||
{
|
||||
if (!_drawn)
|
||||
{
|
||||
_drawn = true;
|
||||
Draw(_targetData);
|
||||
}
|
||||
Align(_targetData);
|
||||
}
|
||||
else if (_drawn)
|
||||
{
|
||||
_drawn = false;
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void InteractableSet(Component interactable)
|
||||
{
|
||||
if (interactable != null
|
||||
&& interactable.TryGetComponent(out _targetData))
|
||||
{
|
||||
_drawn = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_targetData = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void InteractableUnset()
|
||||
{
|
||||
if (_drawn)
|
||||
{
|
||||
_drawn = false;
|
||||
Hide();
|
||||
}
|
||||
_targetData = default(TReticleData);
|
||||
}
|
||||
|
||||
#region Drawing
|
||||
protected abstract void Draw(TReticleData data);
|
||||
protected abstract void Align(TReticleData data);
|
||||
protected abstract void Hide();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2bf09818682cd64fbb06b54214d446e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public class ReticleDataGhost : MonoBehaviour, IReticleData
|
||||
{
|
||||
[SerializeField, Optional]
|
||||
private Transform _targetPoint;
|
||||
|
||||
public Vector3 ProcessHitPoint(Vector3 hitPoint)
|
||||
{
|
||||
return _targetPoint != null ? _targetPoint.position
|
||||
: this.transform.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4fed8706ac2c0e44c8a0abc84a62a563
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public class ReticleDataIcon : MonoBehaviour, IReticleData
|
||||
{
|
||||
[SerializeField, Optional]
|
||||
private MeshRenderer _renderer;
|
||||
|
||||
[SerializeField, Optional]
|
||||
private Texture _customIcon;
|
||||
public Texture CustomIcon
|
||||
{
|
||||
get
|
||||
{
|
||||
return _customIcon;
|
||||
}
|
||||
set
|
||||
{
|
||||
_customIcon = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
[Range(0f, 1f)]
|
||||
private float _snappiness;
|
||||
public float Snappiness
|
||||
{
|
||||
get
|
||||
{
|
||||
return _snappiness;
|
||||
}
|
||||
set
|
||||
{
|
||||
_snappiness = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 GetTargetSize()
|
||||
{
|
||||
if (_renderer != null)
|
||||
{
|
||||
return _renderer.bounds.size;
|
||||
}
|
||||
return this.transform.localScale;
|
||||
}
|
||||
|
||||
public Vector3 ProcessHitPoint(Vector3 hitPoint)
|
||||
{
|
||||
return Vector3.Lerp(hitPoint, this.transform.position, _snappiness);
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectOptionalRenderer(MeshRenderer renderer)
|
||||
{
|
||||
_renderer = renderer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0529a9671a3b6a74fab00c4d05312c64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public class ReticleDataMesh : MonoBehaviour, IReticleData
|
||||
{
|
||||
[SerializeField]
|
||||
private MeshFilter _filter;
|
||||
public MeshFilter Filter
|
||||
{
|
||||
get
|
||||
{
|
||||
return _filter;
|
||||
}
|
||||
set
|
||||
{
|
||||
_filter = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Transform Target => _filter.transform;
|
||||
|
||||
public Vector3 ProcessHitPoint(Vector3 hitPoint)
|
||||
{
|
||||
return _filter.transform.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 830a777c9bbd60441b589b654b94b2c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Grab;
|
||||
using Oculus.Interaction.HandGrab;
|
||||
using Oculus.Interaction.Input;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public class ReticleGhostDrawer : InteractorReticle<ReticleDataGhost>
|
||||
{
|
||||
[FormerlySerializedAs("_handGrabber")]
|
||||
[SerializeField, Interface(typeof(IHandGrabInteractor), typeof(IInteractorView))]
|
||||
private UnityEngine.Object _handGrabInteractor;
|
||||
private IHandGrabInteractor HandGrabInteractor { get; set; }
|
||||
|
||||
[FormerlySerializedAs("_modifier")]
|
||||
[SerializeField]
|
||||
private SyntheticHand _syntheticHand;
|
||||
|
||||
[SerializeField, Interface(typeof(IHandVisual))]
|
||||
[FormerlySerializedAs("_visualHand")]
|
||||
private UnityEngine.Object _handVisual;
|
||||
|
||||
private IHandVisual HandVisual;
|
||||
|
||||
private bool _areFingersFree = true;
|
||||
private bool _isWristFree = true;
|
||||
|
||||
protected override IInteractorView Interactor { get; set; }
|
||||
protected override Component InteractableComponent => HandGrabInteractor.TargetInteractable as Component;
|
||||
|
||||
private ITrackingToWorldTransformer Transformer;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
HandVisual = _handVisual as IHandVisual;
|
||||
HandGrabInteractor = _handGrabInteractor as IHandGrabInteractor;
|
||||
Interactor = _handGrabInteractor as IInteractorView;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
this.BeginStart(ref _started, () => base.Start());
|
||||
this.AssertField(HandGrabInteractor, nameof(_handGrabInteractor));
|
||||
this.AssertField(Interactor, nameof(_handGrabInteractor));
|
||||
this.AssertField(HandVisual, nameof(HandVisual));
|
||||
this.AssertField(_syntheticHand, nameof(_syntheticHand));
|
||||
Transformer = _syntheticHand.GetData().Config.TrackingToWorldTransformer;
|
||||
|
||||
Hide();
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
private void UpdateHandPose(IHandGrabState snapper)
|
||||
{
|
||||
HandGrabTarget snap = snapper.HandGrabTarget;
|
||||
|
||||
if (snap == null)
|
||||
{
|
||||
FreeFingers();
|
||||
FreeWrist();
|
||||
return;
|
||||
}
|
||||
|
||||
if (snap.HandPose != null)
|
||||
{
|
||||
UpdateFingers(snap.HandPose, snapper.GrabbingFingers());
|
||||
_areFingersFree = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeFingers();
|
||||
}
|
||||
|
||||
Pose wristLocalPose = snapper.GetVisualWristPose();
|
||||
Pose wristPose = Transformer != null
|
||||
? Transformer.ToTrackingPose(wristLocalPose)
|
||||
: wristLocalPose;
|
||||
_syntheticHand.LockWristPose(wristPose, 1f);
|
||||
_isWristFree = false;
|
||||
}
|
||||
|
||||
private void UpdateFingers(HandPose handPose, HandFingerFlags grabbingFingers)
|
||||
{
|
||||
Quaternion[] desiredRotations = handPose.JointRotations;
|
||||
_syntheticHand.OverrideAllJoints(desiredRotations, 1f);
|
||||
|
||||
for (int fingerIndex = 0; fingerIndex < Constants.NUM_FINGERS; fingerIndex++)
|
||||
{
|
||||
int fingerFlag = 1 << fingerIndex;
|
||||
JointFreedom fingerFreedom = handPose.FingersFreedom[fingerIndex];
|
||||
if (fingerFreedom == JointFreedom.Constrained
|
||||
&& ((int)grabbingFingers & fingerFlag) != 0)
|
||||
{
|
||||
fingerFreedom = JointFreedom.Locked;
|
||||
}
|
||||
_syntheticHand.SetFingerFreedom((HandFinger)fingerIndex, fingerFreedom);
|
||||
}
|
||||
}
|
||||
|
||||
private bool FreeFingers()
|
||||
{
|
||||
if (!_areFingersFree)
|
||||
{
|
||||
_syntheticHand.FreeAllJoints();
|
||||
_areFingersFree = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool FreeWrist()
|
||||
{
|
||||
if (!_isWristFree)
|
||||
{
|
||||
_syntheticHand.FreeWrist();
|
||||
_isWristFree = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void Align(ReticleDataGhost data)
|
||||
{
|
||||
UpdateHandPose(HandGrabInteractor);
|
||||
_syntheticHand.MarkInputDataRequiresUpdate();
|
||||
}
|
||||
|
||||
protected override void Draw(ReticleDataGhost data)
|
||||
{
|
||||
HandVisual.ForceOffVisibility = false;
|
||||
}
|
||||
|
||||
protected override void Hide()
|
||||
{
|
||||
HandVisual.ForceOffVisibility = true;
|
||||
_syntheticHand.MarkInputDataRequiresUpdate();
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllReticleGhostDrawer(IHandGrabInteractor handGrabInteractor,
|
||||
SyntheticHand syntheticHand, IHandVisual visualHand)
|
||||
{
|
||||
InjectHandGrabInteractor(handGrabInteractor);
|
||||
InjectSyntheticHand(syntheticHand);
|
||||
InjectVisualHand(visualHand);
|
||||
}
|
||||
|
||||
public void InjectHandGrabInteractor(IHandGrabInteractor handGrabInteractor)
|
||||
{
|
||||
_handGrabInteractor = handGrabInteractor as UnityEngine.Object;
|
||||
HandGrabInteractor = handGrabInteractor;
|
||||
Interactor = handGrabInteractor as IInteractorView;
|
||||
}
|
||||
|
||||
public void InjectSyntheticHand(SyntheticHand syntheticHand)
|
||||
{
|
||||
_syntheticHand = syntheticHand;
|
||||
}
|
||||
|
||||
public void InjectVisualHand(IHandVisual visualHand)
|
||||
{
|
||||
_handVisual = visualHand as UnityEngine.Object;
|
||||
HandVisual = visualHand;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c6aa09f2d128af4d93fb9fc29ecec33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public class ReticleIconDrawer : InteractorReticle<ReticleDataIcon>
|
||||
{
|
||||
[SerializeField, Interface(typeof(IDistanceInteractor))]
|
||||
private UnityEngine.Object _distanceInteractor;
|
||||
private IDistanceInteractor DistanceInteractor { get; set; }
|
||||
|
||||
[SerializeField]
|
||||
private MeshRenderer _renderer;
|
||||
|
||||
[SerializeField]
|
||||
private Transform _centerEye;
|
||||
|
||||
[SerializeField]
|
||||
private Texture _defaultIcon;
|
||||
public Texture DefaultIcon
|
||||
{
|
||||
get
|
||||
{
|
||||
return _defaultIcon;
|
||||
}
|
||||
set
|
||||
{
|
||||
_defaultIcon = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool _constantScreenSize;
|
||||
public bool ConstantScreenSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return _constantScreenSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
_constantScreenSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 _originalScale;
|
||||
|
||||
protected override IInteractorView Interactor { get; set; }
|
||||
protected override Component InteractableComponent => DistanceInteractor.DistanceInteractable as Component;
|
||||
|
||||
#region Editor events
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.sharedMaterial.mainTexture = _defaultIcon;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
DistanceInteractor = _distanceInteractor as IDistanceInteractor;
|
||||
Interactor = DistanceInteractor;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
this.BeginStart(ref _started, () => base.Start());
|
||||
this.AssertField(_renderer, nameof(_renderer));
|
||||
this.AssertField(_centerEye, nameof(_centerEye));
|
||||
_originalScale = this.transform.localScale;
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected override void Draw(ReticleDataIcon dataIcon)
|
||||
{
|
||||
if (dataIcon != null
|
||||
&& dataIcon.CustomIcon != null)
|
||||
{
|
||||
_renderer.material.mainTexture = dataIcon.CustomIcon;
|
||||
}
|
||||
else
|
||||
{
|
||||
_renderer.material.mainTexture = _defaultIcon;
|
||||
}
|
||||
|
||||
if (!_constantScreenSize)
|
||||
{
|
||||
_renderer.transform.localScale = _originalScale * dataIcon.GetTargetSize().magnitude;
|
||||
}
|
||||
_renderer.enabled = true;
|
||||
}
|
||||
|
||||
protected override void Align(ReticleDataIcon data)
|
||||
{
|
||||
this.transform.position = data.ProcessHitPoint(DistanceInteractor.HitPoint);
|
||||
|
||||
if (_renderer.enabled)
|
||||
{
|
||||
Vector3 dirToTarget = (_centerEye.position - transform.position).normalized;
|
||||
transform.LookAt(transform.position - dirToTarget, Vector3.up);
|
||||
|
||||
if (_constantScreenSize)
|
||||
{
|
||||
float distance = Vector3.Distance(transform.position, _centerEye.position);
|
||||
_renderer.transform.localScale = _originalScale * distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Hide()
|
||||
{
|
||||
_renderer.enabled = false;
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllReticleIconDrawer(IDistanceInteractor distanceInteractor,
|
||||
Transform centerEye, MeshRenderer renderer)
|
||||
{
|
||||
InjectDistanceInteractor(distanceInteractor);
|
||||
InjectCenterEye(centerEye);
|
||||
InjectRenderer(renderer);
|
||||
}
|
||||
|
||||
public void InjectDistanceInteractor(IDistanceInteractor distanceInteractor)
|
||||
{
|
||||
_distanceInteractor = distanceInteractor as UnityEngine.Object;
|
||||
DistanceInteractor = distanceInteractor;
|
||||
Interactor = distanceInteractor;
|
||||
}
|
||||
|
||||
public void InjectCenterEye(Transform centerEye)
|
||||
{
|
||||
_centerEye = centerEye;
|
||||
}
|
||||
|
||||
public void InjectRenderer(MeshRenderer renderer)
|
||||
{
|
||||
_renderer = renderer;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f026be10edaad42429c52aa2200881dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.HandGrab;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction.DistanceReticles
|
||||
{
|
||||
public class ReticleMeshDrawer : InteractorReticle<ReticleDataMesh>
|
||||
{
|
||||
[FormerlySerializedAs("_handGrabber")]
|
||||
[SerializeField, Interface(typeof(IHandGrabInteractor), typeof(IInteractorView))]
|
||||
private UnityEngine.Object _handGrabInteractor;
|
||||
private IHandGrabInteractor HandGrabInteractor { get; set; }
|
||||
|
||||
[SerializeField]
|
||||
private MeshFilter _filter;
|
||||
|
||||
[SerializeField]
|
||||
private MeshRenderer _renderer;
|
||||
|
||||
[SerializeField]
|
||||
private PoseTravelData _travelData = PoseTravelData.FAST;
|
||||
public PoseTravelData TravelData
|
||||
{
|
||||
get
|
||||
{
|
||||
return _travelData;
|
||||
}
|
||||
set
|
||||
{
|
||||
_travelData = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override IInteractorView Interactor { get; set; }
|
||||
protected override Component InteractableComponent => HandGrabInteractor.TargetInteractable as Component;
|
||||
|
||||
private Tween _tween;
|
||||
|
||||
protected virtual void Reset()
|
||||
{
|
||||
_filter = this.GetComponent<MeshFilter>();
|
||||
_renderer = this.GetComponent<MeshRenderer>();
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
HandGrabInteractor = _handGrabInteractor as IHandGrabInteractor;
|
||||
Interactor = _handGrabInteractor as IInteractorView;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
this.BeginStart(ref _started, () => base.Start());
|
||||
this.AssertField(Interactor, nameof(_handGrabInteractor));
|
||||
this.AssertField(HandGrabInteractor, nameof(_handGrabInteractor));
|
||||
this.AssertField(_filter, nameof(_filter));
|
||||
this.AssertField(_renderer, nameof(_renderer));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected override void Draw(ReticleDataMesh dataMesh)
|
||||
{
|
||||
_filter.sharedMesh = dataMesh.Filter.sharedMesh;
|
||||
_filter.transform.localScale = dataMesh.Filter.transform.lossyScale;
|
||||
_renderer.enabled = true;
|
||||
|
||||
Pose target = DestinationPose(dataMesh, HandGrabInteractor.GetTargetGrabPose());
|
||||
_tween = _travelData.CreateTween(dataMesh.Target.GetPose(), target);
|
||||
}
|
||||
|
||||
protected override void Hide()
|
||||
{
|
||||
_tween = null;
|
||||
_renderer.enabled = false;
|
||||
}
|
||||
|
||||
protected override void Align(ReticleDataMesh data)
|
||||
{
|
||||
Pose target = DestinationPose(data, HandGrabInteractor.GetTargetGrabPose());
|
||||
_tween.UpdateTarget(target);
|
||||
|
||||
|
||||
_tween.Tick();
|
||||
_filter.transform.SetPose(_tween.Pose);
|
||||
}
|
||||
|
||||
private Pose DestinationPose(ReticleDataMesh data, Pose worldSnapPose)
|
||||
{
|
||||
Pose targetOffset = PoseUtils.Delta(worldSnapPose, data.Target.GetPose());
|
||||
HandGrabInteractor.HandGrabApi.Hand.GetRootPose(out Pose pose);
|
||||
pose.Premultiply(HandGrabInteractor.WristToGrabPoseOffset);
|
||||
pose.Premultiply(targetOffset);
|
||||
|
||||
return pose;
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllReticleMeshDrawer(IHandGrabInteractor handGrabInteractor,
|
||||
MeshFilter filter, MeshRenderer renderer)
|
||||
{
|
||||
InjectHandGrabInteractor(handGrabInteractor);
|
||||
InjectFilter(filter);
|
||||
InjectRenderer(renderer);
|
||||
}
|
||||
|
||||
public void InjectHandGrabInteractor(IHandGrabInteractor handGrabInteractor)
|
||||
{
|
||||
_handGrabInteractor = handGrabInteractor as UnityEngine.Object;
|
||||
HandGrabInteractor = handGrabInteractor;
|
||||
Interactor = handGrabInteractor as IInteractorView;
|
||||
}
|
||||
|
||||
public void InjectFilter(MeshFilter filter)
|
||||
{
|
||||
_filter = filter;
|
||||
}
|
||||
|
||||
public void InjectRenderer(MeshRenderer renderer)
|
||||
{
|
||||
_renderer = renderer;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d9d81be1c5cd1942a7572d99a4fca60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,507 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Unity.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public struct TubePoint
|
||||
{
|
||||
public Vector3 position;
|
||||
public Quaternion rotation;
|
||||
public float relativeLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and renders a tube mesh from sequence of points.
|
||||
/// </summary>
|
||||
public class TubeRenderer : MonoBehaviour
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct VertexLayout
|
||||
{
|
||||
public Vector3 pos;
|
||||
public Color32 color;
|
||||
public Vector2 uv;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private MeshFilter _filter;
|
||||
[SerializeField]
|
||||
private MeshRenderer _renderer;
|
||||
[SerializeField]
|
||||
private int _divisions = 6;
|
||||
[SerializeField]
|
||||
private int _bevel = 4;
|
||||
[SerializeField]
|
||||
private int _renderQueue = -1;
|
||||
public int RenderQueue
|
||||
{
|
||||
get
|
||||
{
|
||||
return _renderQueue;
|
||||
}
|
||||
set
|
||||
{
|
||||
_renderQueue = value;
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
private Vector2 _renderOffset = Vector2.zero;
|
||||
public Vector2 RenderOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
return _renderOffset;
|
||||
}
|
||||
set
|
||||
{
|
||||
_renderOffset = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float _radius = 0.005f;
|
||||
public float Radius
|
||||
{
|
||||
get
|
||||
{
|
||||
return _radius;
|
||||
}
|
||||
set
|
||||
{
|
||||
_radius = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Gradient _gradient;
|
||||
public Gradient Gradient
|
||||
{
|
||||
get
|
||||
{
|
||||
return _gradient;
|
||||
}
|
||||
set
|
||||
{
|
||||
_gradient = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color _tint = Color.white;
|
||||
public Color Tint
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tint;
|
||||
}
|
||||
set
|
||||
{
|
||||
_tint = value;
|
||||
}
|
||||
}
|
||||
[SerializeField, Range(0f, 1f)]
|
||||
private float _progressFade = 0.2f;
|
||||
public float ProgressFade
|
||||
{
|
||||
get
|
||||
{
|
||||
return _progressFade;
|
||||
}
|
||||
set
|
||||
{
|
||||
_progressFade = value;
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
private float _startFadeThresold = 0.2f;
|
||||
public float StartFadeThresold
|
||||
{
|
||||
get
|
||||
{
|
||||
return _startFadeThresold;
|
||||
}
|
||||
set
|
||||
{
|
||||
_startFadeThresold = value;
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
private float _endFadeThresold = 0.2f;
|
||||
public float EndFadeThresold
|
||||
{
|
||||
get
|
||||
{
|
||||
return _endFadeThresold;
|
||||
}
|
||||
set
|
||||
{
|
||||
_endFadeThresold = value;
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
private bool _invertThreshold = false;
|
||||
public bool InvertThreshold
|
||||
{
|
||||
get
|
||||
{
|
||||
return _invertThreshold;
|
||||
}
|
||||
set
|
||||
{
|
||||
_invertThreshold = value;
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
private float _feather = 0.2f;
|
||||
public float Feather
|
||||
{
|
||||
get
|
||||
{
|
||||
return _feather;
|
||||
}
|
||||
set
|
||||
{
|
||||
_feather = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool _mirrorTexture;
|
||||
public bool MirrorTexture
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mirrorTexture;
|
||||
}
|
||||
set
|
||||
{
|
||||
_mirrorTexture = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float Progress { get; set; } = 0f;
|
||||
public float TotalLength => _totalLength;
|
||||
|
||||
private VertexAttributeDescriptor[] _dataLayout;
|
||||
private NativeArray<VertexLayout> _vertsData;
|
||||
private VertexLayout _layout = new VertexLayout();
|
||||
private Mesh _mesh;
|
||||
private int[] _tris;
|
||||
private int _initializedSteps = -1;
|
||||
private int _vertsCount;
|
||||
|
||||
private float _totalLength = 0f;
|
||||
|
||||
private static readonly int _fadeLimitsShaderID = Shader.PropertyToID("_FadeLimit");
|
||||
private static readonly int _fadeSignShaderID = Shader.PropertyToID("_FadeSign");
|
||||
private static readonly int _offsetFactorShaderPropertyID = Shader.PropertyToID("_OffsetFactor");
|
||||
private static readonly int _offsetUnitsShaderPropertyID = Shader.PropertyToID("_OffsetUnits");
|
||||
|
||||
#region Editor events
|
||||
|
||||
protected virtual void Reset()
|
||||
{
|
||||
_filter = this.GetComponent<MeshFilter>();
|
||||
_renderer = this.GetComponent<MeshRenderer>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
_renderer.enabled = true;
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
_renderer.enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the mesh data for the tube with the specified points
|
||||
/// </summary>
|
||||
/// <param name="points">The points that the tube must follow</param>
|
||||
/// <param name="space">Indicates if the points are specified in local space or world space</param>
|
||||
public void RenderTube(TubePoint[] points, Space space = Space.Self)
|
||||
{
|
||||
int steps = points.Length;
|
||||
if (steps != _initializedSteps)
|
||||
{
|
||||
InitializeMeshData(steps);
|
||||
_initializedSteps = steps;
|
||||
}
|
||||
_vertsData = new NativeArray<VertexLayout>(_vertsCount, Allocator.Temp);
|
||||
UpdateMeshData(points, space);
|
||||
_renderer.enabled = enabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides the renderer of the tube
|
||||
/// </summary>
|
||||
public void Hide()
|
||||
{
|
||||
_renderer.enabled = false;
|
||||
}
|
||||
|
||||
private void InitializeMeshData(int steps)
|
||||
{
|
||||
_dataLayout = new VertexAttributeDescriptor[]
|
||||
{
|
||||
new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
|
||||
new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.UNorm8, 4),
|
||||
new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2),
|
||||
};
|
||||
|
||||
_vertsCount = SetVertexCount(steps, _divisions, _bevel);
|
||||
SubMeshDescriptor submeshDesc = new SubMeshDescriptor(0, _tris.Length, MeshTopology.Triangles);
|
||||
|
||||
_mesh = new Mesh();
|
||||
_mesh.SetVertexBufferParams(_vertsCount, _dataLayout);
|
||||
_mesh.SetIndexBufferParams(_tris.Length, IndexFormat.UInt32);
|
||||
_mesh.SetIndexBufferData(_tris, 0, 0, _tris.Length);
|
||||
_mesh.subMeshCount = 1;
|
||||
_mesh.SetSubMesh(0, submeshDesc);
|
||||
|
||||
_filter.mesh = _mesh;
|
||||
}
|
||||
|
||||
private void UpdateMeshData(TubePoint[] points, Space space)
|
||||
{
|
||||
int steps = points.Length;
|
||||
float totalLength = 0f;
|
||||
Vector3 prevPoint = Vector3.zero;
|
||||
Pose pose = Pose.identity;
|
||||
Pose start = Pose.identity;
|
||||
Pose end = Pose.identity;
|
||||
|
||||
Pose rootPose = this.transform.GetPose(Space.World);
|
||||
Quaternion inverseRootRotation = Quaternion.Inverse(rootPose.rotation);
|
||||
Vector3 rootPositionScaled = new Vector3(
|
||||
rootPose.position.x / this.transform.lossyScale.x,
|
||||
rootPose.position.y / this.transform.lossyScale.y,
|
||||
rootPose.position.z / this.transform.lossyScale.z);
|
||||
float uniformScale = space == Space.World ? this.transform.lossyScale.x : 1f;
|
||||
|
||||
TransformPose(points[0], ref start);
|
||||
TransformPose(points[points.Length - 1], ref end);
|
||||
|
||||
BevelCap(start, false, 0);
|
||||
|
||||
for (int i = 0; i < steps; i++)
|
||||
{
|
||||
TransformPose(points[i], ref pose);
|
||||
Vector3 point = pose.position;
|
||||
Quaternion rotation = pose.rotation;
|
||||
|
||||
float progress = points[i].relativeLength;
|
||||
Color color = Gradient.Evaluate(progress) * _tint;
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
totalLength += Vector3.Distance(point, prevPoint);
|
||||
}
|
||||
prevPoint = point;
|
||||
|
||||
if (i / (steps - 1f) < Progress)
|
||||
{
|
||||
color.a *= ProgressFade;
|
||||
}
|
||||
|
||||
_layout.color = color;
|
||||
|
||||
WriteCircle(point, rotation, _radius, i + _bevel, progress);
|
||||
}
|
||||
|
||||
BevelCap(end, true, _bevel + steps);
|
||||
|
||||
_mesh.bounds = new Bounds(
|
||||
(start.position + end.position) * 0.5f,
|
||||
end.position - start.position);
|
||||
_mesh.SetVertexBufferData(_vertsData, 0, 0, _vertsData.Length, 0, MeshUpdateFlags.DontRecalculateBounds);
|
||||
|
||||
_totalLength = totalLength * uniformScale;
|
||||
|
||||
RedrawFadeThresholds();
|
||||
|
||||
void TransformPose(in TubePoint tubePoint, ref Pose pose)
|
||||
{
|
||||
if (space == Space.Self)
|
||||
{
|
||||
pose.position = tubePoint.position;
|
||||
pose.rotation = tubePoint.rotation;
|
||||
return;
|
||||
}
|
||||
|
||||
pose.position = inverseRootRotation * (tubePoint.position - rootPositionScaled);
|
||||
pose.rotation = inverseRootRotation * tubePoint.rotation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resubmits the fading thresholds data to the material without re-generating the mesh
|
||||
/// </summary>
|
||||
public void RedrawFadeThresholds()
|
||||
{
|
||||
float originFadeIn = StartFadeThresold / _totalLength;
|
||||
float originFadeOut = (StartFadeThresold + Feather) / _totalLength;
|
||||
float endFadeIn = (_totalLength - EndFadeThresold) / _totalLength;
|
||||
float endFadeOut = (_totalLength - EndFadeThresold - Feather) / _totalLength;
|
||||
|
||||
_renderer.material.SetVector(_fadeLimitsShaderID, new Vector4(
|
||||
_invertThreshold ? originFadeOut : originFadeIn,
|
||||
_invertThreshold ? originFadeIn : originFadeOut,
|
||||
endFadeOut,
|
||||
endFadeIn));
|
||||
_renderer.material.SetFloat(_fadeSignShaderID, _invertThreshold ? -1 : 1);
|
||||
_renderer.material.renderQueue = _renderQueue;
|
||||
|
||||
_renderer.material.SetFloat(_offsetFactorShaderPropertyID, _renderOffset.x);
|
||||
_renderer.material.SetFloat(_offsetUnitsShaderPropertyID, _renderOffset.y);
|
||||
}
|
||||
|
||||
private void BevelCap(in Pose pose, bool end, int indexOffset)
|
||||
{
|
||||
Vector3 origin = pose.position;
|
||||
Quaternion rotation = pose.rotation;
|
||||
for (int i = 0; i < _bevel; i++)
|
||||
{
|
||||
float radiusFactor = Mathf.InverseLerp(-1, _bevel + 1, i);
|
||||
if (end)
|
||||
{
|
||||
radiusFactor = 1 - radiusFactor;
|
||||
}
|
||||
float positionFactor = Mathf.Sqrt(1 - radiusFactor * radiusFactor);
|
||||
Vector3 point = origin + (end ? 1 : -1) * (rotation * Vector3.forward) * _radius * positionFactor;
|
||||
WriteCircle(point, rotation, _radius * radiusFactor, i + indexOffset, end ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteCircle(Vector3 point, Quaternion rotation, float width, int index, float progress)
|
||||
{
|
||||
Color color = Gradient.Evaluate(progress) * _tint;
|
||||
if (progress < Progress)
|
||||
{
|
||||
color.a *= ProgressFade;
|
||||
}
|
||||
_layout.color = color;
|
||||
|
||||
for (int j = 0; j <= _divisions; j++)
|
||||
{
|
||||
float radius = 2 * Mathf.PI * j / _divisions;
|
||||
Vector3 circle = new Vector3(Mathf.Sin(radius), Mathf.Cos(radius), 0);
|
||||
Vector3 normal = rotation * circle;
|
||||
|
||||
_layout.pos = point + normal * width;
|
||||
if (_mirrorTexture)
|
||||
{
|
||||
float x = (j / (float)_divisions) * 2f;
|
||||
if (j >= _divisions * 0.5f)
|
||||
{
|
||||
x = 2 - x;
|
||||
}
|
||||
_layout.uv = new Vector2(x, progress);
|
||||
}
|
||||
else
|
||||
{
|
||||
_layout.uv = new Vector2(j / (float)_divisions, progress);
|
||||
}
|
||||
int vertIndex = index * (_divisions + 1) + j;
|
||||
_vertsData[vertIndex] = _layout;
|
||||
}
|
||||
}
|
||||
|
||||
private int SetVertexCount(int positionCount, int divisions, int bevelCap)
|
||||
{
|
||||
bevelCap = bevelCap * 2;
|
||||
int vertsPerPosition = divisions + 1;
|
||||
int vertCount = (positionCount + bevelCap) * vertsPerPosition;
|
||||
|
||||
int tubeTriangles = (positionCount - 1 + bevelCap) * divisions * 6;
|
||||
int capTriangles = (divisions - 2) * 3;
|
||||
int triangleCount = tubeTriangles + capTriangles * 2;
|
||||
_tris = new int[triangleCount];
|
||||
|
||||
// handle triangulation
|
||||
for (int i = 0; i < positionCount - 1 + bevelCap; i++)
|
||||
{
|
||||
// add faces
|
||||
for (int j = 0; j < divisions; j++)
|
||||
{
|
||||
int vert0 = i * vertsPerPosition + j;
|
||||
int vert1 = (i + 1) * vertsPerPosition + j;
|
||||
int t = (i * divisions + j) * 6;
|
||||
_tris[t] = vert0;
|
||||
_tris[t + 1] = _tris[t + 4] = vert1;
|
||||
_tris[t + 2] = _tris[t + 3] = vert0 + 1;
|
||||
_tris[t + 5] = vert1 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// triangulate the ends
|
||||
Cap(tubeTriangles, 0, divisions - 1, true);
|
||||
Cap(tubeTriangles + capTriangles, vertCount - divisions, vertCount - 1);
|
||||
|
||||
void Cap(int t, int firstVert, int lastVert, bool clockwise = false)
|
||||
{
|
||||
for (int i = firstVert + 1; i < lastVert; i++)
|
||||
{
|
||||
_tris[t++] = firstVert;
|
||||
_tris[t++] = clockwise ? i : i + 1;
|
||||
_tris[t++] = clockwise ? i + 1 : i;
|
||||
}
|
||||
}
|
||||
|
||||
return vertCount;
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllTubeRenderer(MeshFilter filter,
|
||||
MeshRenderer renderer, int divisions, int bevel)
|
||||
{
|
||||
InjectFilter(filter);
|
||||
InjectRenderer(renderer);
|
||||
InjectDivisions(divisions);
|
||||
InjectBevel(bevel);
|
||||
}
|
||||
public void InjectFilter(MeshFilter filter)
|
||||
{
|
||||
_filter = filter;
|
||||
}
|
||||
public void InjectRenderer(MeshRenderer renderer)
|
||||
{
|
||||
_renderer = renderer;
|
||||
}
|
||||
public void InjectDivisions(int divisions)
|
||||
{
|
||||
_divisions = divisions;
|
||||
}
|
||||
public void InjectBevel(int bevel)
|
||||
{
|
||||
_bevel = bevel;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 038d627f7b72e904382b2b0f8fb30c29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91c22619060901949b0d01586cff533e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// Non-allocating HashSet extension methods mirroring MS implementation
|
||||
/// https://referencesource.microsoft.com/#system.core/system/Collections/Generic/HashSet.cs
|
||||
/// </summary>
|
||||
public static class HashSetExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Take the union of this HashSet with other. Modifies this set.
|
||||
/// </summary>
|
||||
/// <param name="other">HashSet with items to add</param>
|
||||
public static void UnionWithNonAlloc<T>(this HashSet<T> hashSetToModify, HashSet<T> other)
|
||||
{
|
||||
if (hashSetToModify == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hashSetToModify));
|
||||
}
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
foreach (T item in other)
|
||||
{
|
||||
hashSetToModify.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take the union of this HashSet with other. Modifies this set.
|
||||
/// </summary>
|
||||
/// <param name="other">IList with items to add</param>
|
||||
public static void UnionWithNonAlloc<T>(this HashSet<T> hashSetToModify, IList<T> other)
|
||||
{
|
||||
if (hashSetToModify == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hashSetToModify));
|
||||
}
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
for (int i = 0; i < other.Count; ++i)
|
||||
{
|
||||
hashSetToModify.Add(other[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove items in other from this set. Modifies this set
|
||||
/// </summary>
|
||||
/// <param name="other">HashSet with items to remove</param>
|
||||
public static void ExceptWithNonAlloc<T>(this HashSet<T> hashSetToModify, HashSet<T> other)
|
||||
{
|
||||
if (hashSetToModify == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hashSetToModify));
|
||||
}
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
if (hashSetToModify.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (other == hashSetToModify)
|
||||
{
|
||||
hashSetToModify.Clear();
|
||||
return;
|
||||
}
|
||||
foreach (T element in other)
|
||||
{
|
||||
hashSetToModify.Remove(element);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove items in other from this set. Modifies this set
|
||||
/// </summary>
|
||||
/// <param name="other">IList with items to remove</param>
|
||||
public static void ExceptWithNonAlloc<T>(this HashSet<T> hashSetToModify, IList<T> other)
|
||||
{
|
||||
if (hashSetToModify == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hashSetToModify));
|
||||
}
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
if (hashSetToModify.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < other.Count; ++i)
|
||||
{
|
||||
hashSetToModify.Remove(other[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this set overlaps other (i.e. they share at least one item)
|
||||
/// </summary>
|
||||
/// <param name="other">HashSet to check overlap against</param>
|
||||
/// <returns>true if these have at least one common element; false if disjoint</returns>
|
||||
public static bool OverlapsNonAlloc<T>(this HashSet<T> hashSetToCheck, HashSet<T> other)
|
||||
{
|
||||
if (hashSetToCheck == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hashSetToCheck));
|
||||
}
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
if (hashSetToCheck.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foreach (T element in other)
|
||||
{
|
||||
if (hashSetToCheck.Contains(element))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this set overlaps other (i.e. they share at least one item)
|
||||
/// </summary>
|
||||
/// <param name="other">IList to check overlap against</param>
|
||||
/// <returns>true if these have at least one common element; false if disjoint</returns>
|
||||
public static bool OverlapsNonAlloc<T>(this HashSet<T> hashSetToCheck, IList<T> other)
|
||||
{
|
||||
if (hashSetToCheck == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hashSetToCheck));
|
||||
}
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
if (hashSetToCheck.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < other.Count; ++i)
|
||||
{
|
||||
if (hashSetToCheck.Contains(other[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09d0907b32d460349ae989d67adb26ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// By adding BeginStart and EndStart at the beginning and end of Start, MonoBehaviours with
|
||||
/// OnEnable and OnDisable logic can wrap their contents within a _started flag and effectively
|
||||
/// skip over logic in those methods until after Start has been invoked.
|
||||
///
|
||||
/// To not bypass the Unity Lifecycle, the enabled property is used to disable the most derived
|
||||
/// MonoBehaviour, invoke Start up the hierarchy chain, and finally re-enable the MonoBehaviour.
|
||||
/// </summary>
|
||||
public static class MonoBehaviourStartExtensions
|
||||
{
|
||||
public static void BeginStart(this MonoBehaviour monoBehaviour, ref bool started,
|
||||
Action baseStart = null)
|
||||
{
|
||||
if (!started)
|
||||
{
|
||||
monoBehaviour.enabled = false;
|
||||
started = true;
|
||||
baseStart?.Invoke();
|
||||
started = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
baseStart?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public static void EndStart(this MonoBehaviour monoBehaviour, ref bool started)
|
||||
{
|
||||
if (!started)
|
||||
{
|
||||
started = true;
|
||||
monoBehaviour.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e905110e6125b442b02d7a80ba72015
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public static class TransformExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Transforms position from world space to local space
|
||||
/// </summary>
|
||||
public static Vector3 InverseTransformPointUnscaled(this Transform transform, Vector3 position)
|
||||
{
|
||||
Matrix4x4 worldToLocal = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one).inverse;
|
||||
return worldToLocal.MultiplyPoint3x4(position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms position from local space to world space
|
||||
/// </summary>
|
||||
public static Vector3 TransformPointUnscaled(this Transform transform, Vector3 position)
|
||||
{
|
||||
Matrix4x4 localToWorld = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
|
||||
return localToWorld.MultiplyPoint3x4(position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform a bounding box from local to world space.
|
||||
/// </summary>
|
||||
/// <param name="transform">Transfrom that
|
||||
/// <paramref name="bounds"/> is local to</param>
|
||||
/// <param name="bounds">The bounds to transform, in local space</param>
|
||||
/// <returns>The bounding box in world space</returns>
|
||||
public static Bounds TransformBounds(this Transform transform, in Bounds bounds)
|
||||
{
|
||||
Bounds worldBounds = new Bounds();
|
||||
|
||||
Vector3 boundsMin = bounds.min;
|
||||
Vector3 boundsMax = bounds.max;
|
||||
Vector3 min = transform.position;
|
||||
Vector3 max = transform.position;
|
||||
Matrix4x4 m = transform.localToWorldMatrix;
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
float e = m[i, j] * boundsMin[j];
|
||||
float f = m[i, j] * boundsMax[j];
|
||||
min[i] += (e < f) ? e : f;
|
||||
max[i] += (e < f) ? f : e;
|
||||
}
|
||||
}
|
||||
|
||||
worldBounds.SetMinMax(min, max);
|
||||
return worldBounds;
|
||||
}
|
||||
|
||||
public static Transform FindChildRecursive(this Transform parent, string name)
|
||||
{
|
||||
foreach (Transform child in parent)
|
||||
{
|
||||
if (child.name.Contains(name))
|
||||
return child;
|
||||
|
||||
var result = child.FindChildRecursive(name);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0efedf2e13db9f24c88029611537cf97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public static class VectorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Are two points within <see cref="Vector3.kEpsilon"/>
|
||||
/// distance of each other
|
||||
/// </summary>
|
||||
public static bool Approximately(this Vector3 a, Vector3 b, float epsilon = Vector3.kEpsilon)
|
||||
{
|
||||
return (a - b).sqrMagnitude <= epsilon * epsilon;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08b019013fb2a7143befda25a24a2ac0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Oculus/Interaction/Runtime/Scripts/Grab.meta
Normal file
8
Assets/Oculus/Interaction/Runtime/Scripts/Grab.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6531a4f842518049961d156ddc174be
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Input;
|
||||
using Oculus.Interaction.PoseDetection;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Oculus.Interaction.GrabAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// This Finger API uses the curl value of the fingers to detect if they are grabbing
|
||||
/// </summary>
|
||||
public class FingerPalmGrabAPI : IFingerAPI
|
||||
{
|
||||
// Temporary structure used to pass data to and from native components
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class HandData
|
||||
{
|
||||
private const int NumHandJoints = 24;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = NumHandJoints * 7, ArraySubType = UnmanagedType.R4)]
|
||||
private float[] jointValues;
|
||||
private float _rootRotX;
|
||||
private float _rootRotY;
|
||||
private float _rootRotZ;
|
||||
private float _rootRotW;
|
||||
private float _rootPosX;
|
||||
private float _rootPosY;
|
||||
private float _rootPosZ;
|
||||
private int _handedness;
|
||||
|
||||
public HandData()
|
||||
{
|
||||
jointValues = new float[NumHandJoints * 7];
|
||||
}
|
||||
|
||||
public void SetData(IReadOnlyList<Pose> joints, Pose root, Handedness handedness)
|
||||
{
|
||||
Assert.AreEqual(NumHandJoints, joints.Count);
|
||||
int jointValueIndex = 0;
|
||||
for (int jointIndex = 0; jointIndex < NumHandJoints; jointIndex++)
|
||||
{
|
||||
Pose joint = joints[jointIndex];
|
||||
jointValues[jointValueIndex++] = joint.rotation.x;
|
||||
jointValues[jointValueIndex++] = joint.rotation.y;
|
||||
jointValues[jointValueIndex++] = joint.rotation.z;
|
||||
jointValues[jointValueIndex++] = joint.rotation.w;
|
||||
jointValues[jointValueIndex++] = joint.position.x;
|
||||
jointValues[jointValueIndex++] = joint.position.y;
|
||||
jointValues[jointValueIndex++] = joint.position.z;
|
||||
}
|
||||
this._rootRotX = root.rotation.x;
|
||||
this._rootRotY = root.rotation.y;
|
||||
this._rootRotZ = root.rotation.z;
|
||||
this._rootRotW = root.rotation.w;
|
||||
this._rootPosX = root.position.x;
|
||||
this._rootPosY = root.position.y;
|
||||
this._rootPosZ = root.position.z;
|
||||
this._handedness = (int)handedness;
|
||||
}
|
||||
}
|
||||
|
||||
#region DLLImports
|
||||
enum ReturnValue { Success = 0, Failure = -1 };
|
||||
|
||||
|
||||
[DllImport("InteractionSdk")]
|
||||
private static extern int isdk_FingerPalmGrabAPI_Create();
|
||||
|
||||
[DllImport("InteractionSdk")]
|
||||
private static extern ReturnValue isdk_FingerPalmGrabAPI_UpdateHandData(int handle, [In] HandData data);
|
||||
|
||||
[DllImport("InteractionSdk")]
|
||||
private static extern ReturnValue isdk_FingerPalmGrabAPI_GetFingerIsGrabbing(int handle, HandFinger finger, out bool grabbing);
|
||||
|
||||
[DllImport("InteractionSdk")]
|
||||
private static extern ReturnValue isdk_FingerPalmGrabAPI_GetFingerIsGrabbingChanged(int handle, HandFinger finger, bool targetGrabState, out bool changed);
|
||||
|
||||
[DllImport("InteractionSdk")]
|
||||
private static extern ReturnValue isdk_FingerPalmGrabAPI_GetFingerGrabScore(int handle, HandFinger finger, out float score);
|
||||
|
||||
[DllImport("InteractionSdk")]
|
||||
private static extern ReturnValue isdk_FingerPalmGrabAPI_GetCenterOffset(int handle, out Vector3 score);
|
||||
|
||||
#endregion
|
||||
|
||||
private int apiHandle_ = -1;
|
||||
private HandData handData_;
|
||||
|
||||
public FingerPalmGrabAPI()
|
||||
{
|
||||
handData_ = new HandData();
|
||||
}
|
||||
|
||||
private int GetHandle()
|
||||
{
|
||||
|
||||
if (apiHandle_ == -1)
|
||||
{
|
||||
apiHandle_ = isdk_FingerPalmGrabAPI_Create();
|
||||
Debug.Assert(apiHandle_ != -1, "FingerPalmGrabAPI: isdk_FingerPalmGrabAPI_Create failed");
|
||||
}
|
||||
|
||||
return apiHandle_;
|
||||
}
|
||||
|
||||
public bool GetFingerIsGrabbing(HandFinger finger)
|
||||
{
|
||||
ReturnValue rv = isdk_FingerPalmGrabAPI_GetFingerIsGrabbing(GetHandle(), finger, out bool grabbing);
|
||||
Debug.Assert(rv != ReturnValue.Failure, "FingerPalmGrabAPI: isdk_FingerPalmGrabAPI_GetFingerIsGrabbing failed");
|
||||
return grabbing;
|
||||
}
|
||||
|
||||
public bool GetFingerIsGrabbingChanged(HandFinger finger, bool targetGrabState)
|
||||
{
|
||||
ReturnValue rv = isdk_FingerPalmGrabAPI_GetFingerIsGrabbingChanged(GetHandle(), finger, targetGrabState, out bool grabbing);
|
||||
Debug.Assert(rv != ReturnValue.Failure, "FingerPalmGrabAPI: isdk_FingerPalmGrabAPI_GetFingerIsGrabbingChanged failed");
|
||||
return grabbing;
|
||||
}
|
||||
|
||||
public float GetFingerGrabScore(HandFinger finger)
|
||||
{
|
||||
ReturnValue rv = isdk_FingerPalmGrabAPI_GetFingerGrabScore(GetHandle(), finger, out float score);
|
||||
Debug.Assert(rv != ReturnValue.Failure, "FingerPalmGrabAPI: isdk_FingerPalmGrabAPI_GetFingerGrabScore failed");
|
||||
return score;
|
||||
}
|
||||
|
||||
public void Update(IHand hand)
|
||||
{
|
||||
if (!hand.GetRootPose(out Pose rootPose))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hand.GetJointPosesFromWrist(out ReadOnlyHandJointPoses poses))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
handData_.SetData(poses, rootPose, hand.Handedness);
|
||||
ReturnValue rv = isdk_FingerPalmGrabAPI_UpdateHandData(GetHandle(), handData_);
|
||||
Debug.Assert(rv != ReturnValue.Failure, "FingerPalmGrabAPI: isdk_FingerPalmGrabAPI_UpdateHandData failed");
|
||||
}
|
||||
|
||||
public Vector3 GetWristOffsetLocal()
|
||||
{
|
||||
ReturnValue rv = isdk_FingerPalmGrabAPI_GetCenterOffset(GetHandle(), out Vector3 center);
|
||||
Debug.Assert(rv != ReturnValue.Failure, "FingerPalmGrabAPI: isdk_FingerPalmGrabAPI_GetCenterOffset failed");
|
||||
return center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df4b2f3ffa2d8a04eb0badc7bfd33a41
|
||||
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