clean project

This commit is contained in:
Helar Jaadla
2022-03-07 17:52:41 +02:00
parent a174b45bd2
commit cbeb10ec35
5100 changed files with 837159 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,113 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
public class ActiveStateGroup : MonoBehaviour, IActiveState
{
public enum ActiveStateGroupLogicOperator
{
AND = 0,
OR = 1,
XOR = 2
}
[SerializeField, Interface(typeof(IActiveState))]
private List<MonoBehaviour> _activeStates;
private List<IActiveState> ActiveStates;
[SerializeField]
private ActiveStateGroupLogicOperator _logicOperator = ActiveStateGroupLogicOperator.AND;
protected virtual void Awake()
{
ActiveStates = _activeStates.ConvertAll(mono => mono as IActiveState);
}
protected virtual void Start()
{
foreach (IActiveState activeState in ActiveStates)
{
Assert.IsNotNull(activeState);
}
}
public bool Active
{
get
{
if (ActiveStates == null)
{
return false;
}
switch(_logicOperator)
{
case ActiveStateGroupLogicOperator.AND:
foreach(IActiveState activeState in ActiveStates)
{
if(!activeState.Active) return false;
}
return true;
case ActiveStateGroupLogicOperator.OR:
foreach(IActiveState activeState in ActiveStates)
{
if(activeState.Active) return true;
}
return false;
case ActiveStateGroupLogicOperator.XOR:
bool foundActive = false;
foreach(IActiveState activeState in ActiveStates)
{
if(activeState.Active)
{
if(foundActive) return false;
foundActive = true;
}
}
return foundActive;
default:
return false;
}
}
}
#region Inject
public void InjectAllActiveStateGroup(List<IActiveState> activeStates)
{
InjectActiveStates(activeStates);
}
public void InjectActiveStates(List<IActiveState> activeStates)
{
ActiveStates = activeStates;
_activeStates = activeStates.ConvertAll(activeState => activeState as MonoBehaviour);
}
public void InjectOptionalLogicOperator(ActiveStateGroupLogicOperator logicOperator)
{
_logicOperator = logicOperator;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,53 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
public class ActiveStateNot : MonoBehaviour, IActiveState
{
[SerializeField, Interface(typeof(IActiveState))]
private MonoBehaviour _activeState;
private IActiveState ActiveState;
protected virtual void Awake()
{
ActiveState = _activeState as IActiveState;;
}
protected virtual void Start()
{
Assert.IsNotNull(ActiveState);
}
public bool Active => !ActiveState.Active;
#region Inject
public void InjectAllActiveStateNot(IActiveState activeState)
{
InjectActiveState(activeState);
}
public void InjectActiveState(IActiveState activeState)
{
_activeState = activeState as MonoBehaviour;
ActiveState = activeState;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,71 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
public class ActiveStateSelector : MonoBehaviour, ISelector
{
[SerializeField, Interface(typeof(IActiveState))]
private MonoBehaviour _activeState;
private IActiveState ActiveState;
private bool _selecting = false;
public event Action WhenSelected = delegate { };
public event Action WhenUnselected = delegate { };
protected virtual void Awake()
{
ActiveState = _activeState as IActiveState;
}
protected virtual void Start()
{
Assert.IsNotNull(ActiveState);
}
protected virtual void Update()
{
if (_selecting != ActiveState.Active)
{
_selecting = ActiveState.Active;
if (_selecting)
{
WhenSelected();
}
else
{
WhenUnselected();
}
}
}
#region Inject
public void InjectAllActiveStateSelector(IActiveState activeState)
{
InjectActiveState(activeState);
}
public void InjectActiveState(IActiveState activeState)
{
_activeState = activeState as MonoBehaviour;
ActiveState = activeState;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,120 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
/// <summary>
/// Modifies the `active` state of a list of GameObjects, as well as the `enabled` state of a
/// list of components, from the `Active` field of the given IActiveState.
/// The component will only activate/enable dependants that were active/enabled during Start()
/// lifecycle phase.
/// </summary>
/// These need to be updated in batch or else we could get inconsistent behaviour when
/// multiple of these are in a scene.
[DefaultExecutionOrder(1)]
public class ActiveStateTracker : MonoBehaviour
{
[SerializeField, Interface(typeof(IActiveState))]
private MonoBehaviour _activeState;
private IActiveState ActiveState;
[Header("Active state dependents")]
[SerializeField]
private bool _includeChildrenAsDependents = false;
[SerializeField, Optional]
[Tooltip("Sets the `active` field on whole GameObjects")]
private List<GameObject> _gameObjects;
[SerializeField, Optional]
[Tooltip("Sets the `enabled` field on individual components")]
private List<MonoBehaviour> _monoBehaviours;
protected virtual void Awake()
{
ActiveState = _activeState as IActiveState;
}
private bool _active = false;
protected virtual void Start()
{
Assert.IsNotNull(ActiveState);
if (_includeChildrenAsDependents)
{
for(int i = 0; i < transform.childCount; i ++)
{
_gameObjects.Add(transform.GetChild(i).gameObject);
}
}
SetDependentsActive(false);
}
protected virtual void Update()
{
if (_active == ActiveState.Active) return;
_active = ActiveState.Active;
SetDependentsActive(ActiveState.Active);
}
private void SetDependentsActive(bool active)
{
for (int i = 0; i < _gameObjects.Count; ++i)
{
_gameObjects[i].SetActive(active);
}
for (int i = 0; i < _monoBehaviours.Count; ++i)
{
_monoBehaviours[i].enabled = active;
}
}
#region Inject
public void InjectAllActiveStateTracker(IActiveState activeState)
{
InjectActiveState(activeState);
}
public void InjectActiveState(IActiveState activeState)
{
_activeState = activeState as MonoBehaviour;
ActiveState = activeState;
}
public void InjectOptionalIncludeChildrenAsDependents(bool includeChildrenAsDependents)
{
_includeChildrenAsDependents = includeChildrenAsDependents;
}
public void InjectOptionalGameObjects(List<GameObject> gameObjects)
{
_gameObjects = gameObjects;
}
public void InjectOptionalMonoBehaviours(List<MonoBehaviour> monoBehaviours)
{
_monoBehaviours = monoBehaviours;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,105 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Oculus.Interaction
{
/// <summary>
/// The CollisionsInteractableRegistry maintains a collision map for any Rigidbody-Interactables
/// pair that utilizes Unity Colliders for overlap checks
/// </summary>
public class CollisionInteractionRegistry<TInteractor, TInteractable> :
InteractableRegistry<TInteractor, TInteractable>
where TInteractor : class, IInteractor<TInteractable>, IRigidbodyRef
where TInteractable : class, IInteractable<TInteractor>, IRigidbodyRef
{
private Dictionary<Rigidbody, HashSet<TInteractable>> _rigidbodyCollisionMap;
private Dictionary<TInteractable, InteractableTriggerBroadcaster> _broadcasters;
public CollisionInteractionRegistry() : base()
{
_rigidbodyCollisionMap = new Dictionary<Rigidbody, HashSet<TInteractable>>();
_broadcasters = new Dictionary<TInteractable, InteractableTriggerBroadcaster>();
}
public override void Register(TInteractable interactable)
{
base.Register(interactable);
GameObject triggerGameObject = interactable.Rigidbody.gameObject;
InteractableTriggerBroadcaster broadcaster;
if (!_broadcasters.TryGetValue(interactable, out broadcaster))
{
broadcaster = triggerGameObject.AddComponent<InteractableTriggerBroadcaster>();
broadcaster.InjectAllInteractableTriggerBroadcaster(interactable);
_broadcasters.Add(interactable, broadcaster);
broadcaster.OnTriggerEntered += MarkCollision;
broadcaster.OnTriggerExited += UnmarkCollision;
}
}
public override void Unregister(TInteractable interactable)
{
base.Unregister(interactable);
InteractableTriggerBroadcaster broadcaster;
if (_broadcasters.TryGetValue(interactable, out broadcaster))
{
broadcaster.enabled = false;
broadcaster.OnTriggerEntered -= MarkCollision;
broadcaster.OnTriggerExited -= UnmarkCollision;
_broadcasters.Remove(interactable);
Object.Destroy(broadcaster);
}
}
private void MarkCollision(IInteractable interactable, Rigidbody rigidbody)
{
TInteractable typedInteractable = interactable as TInteractable;
if (!_rigidbodyCollisionMap.ContainsKey(rigidbody))
{
_rigidbodyCollisionMap.Add(rigidbody, new HashSet<TInteractable>());
}
HashSet<TInteractable> interactables = _rigidbodyCollisionMap[rigidbody];
interactables.Add(typedInteractable);
}
private void UnmarkCollision(IInteractable interactable, Rigidbody rigidbody)
{
TInteractable typedInteractable = interactable as TInteractable;
HashSet<TInteractable> interactables = _rigidbodyCollisionMap[rigidbody];
interactables.Remove(typedInteractable);
if (interactables.Count == 0)
{
_rigidbodyCollisionMap.Remove(rigidbody);
}
}
public override IEnumerable<TInteractable> List(TInteractor interactor)
{
HashSet<TInteractable> colliding;
if (_rigidbodyCollisionMap.TryGetValue(interactor.Rigidbody, out colliding))
{
return PruneInteractables(colliding, interactor);
}
return _empty;
}
private static readonly List<TInteractable> _empty = new List<TInteractable>();
}
}

View File

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

View File

@@ -0,0 +1,54 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
public class ControllerActiveState : MonoBehaviour, IActiveState
{
[SerializeField, Interface(typeof(IController))]
MonoBehaviour _controller;
private IController Controller;
public bool Active => Controller.IsConnected;
protected virtual void Awake()
{
Controller = _controller as IController;
}
protected virtual void Start()
{
Assert.IsNotNull(Controller);
}
#region Inject
public void InjectAllControllerActiveState(IController controller)
{
InjectController(controller);
}
public void InjectController(IController controller)
{
_controller = controller as MonoBehaviour;
Controller = controller;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,63 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
public class GameObjectActiveState : MonoBehaviour, IActiveState
{
[SerializeField]
private GameObject _sourceGameObject;
[SerializeField]
private bool _sourceActiveSelf;
public bool SourceActiveSelf
{
get
{
return _sourceActiveSelf;
}
set
{
_sourceActiveSelf = value;
}
}
protected virtual void Start()
{
Assert.IsNotNull(_sourceGameObject);
}
public bool Active => _sourceActiveSelf
? _sourceGameObject.activeSelf
: _sourceGameObject.activeInHierarchy;
#region Inject
public void InjectAllGameObjectActiveState(GameObject sourceGameObject)
{
InjectSourceGameObject(sourceGameObject);
}
public void InjectSourceGameObject(GameObject sourceGameObject)
{
_sourceGameObject = sourceGameObject;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,53 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
public class HandActiveState : MonoBehaviour, IActiveState
{
[SerializeField, Interface(typeof(IHand))]
MonoBehaviour _hand;
private IHand Hand;
public bool Active => Hand.IsConnected;
protected virtual void Awake()
{
Hand = _hand as IHand;
}
protected virtual void Start()
{
Assert.IsNotNull(Hand);
}
#region Inject
public void InjectAllHandActiveState(IHand hand)
{
InjectHand(hand);
}
public void InjectHand(IHand hand)
{
_hand = hand as MonoBehaviour;
Hand = hand;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,19 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
namespace Oculus.Interaction
{
public interface IActiveState
{
bool Active { get; }
}
}

View File

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

View File

@@ -0,0 +1,91 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Oculus.Interaction
{
public struct InteractableStateChangeArgs
{
public InteractableState PreviousState;
public InteractableState NewState;
}
public interface IComponent
{
GameObject gameObject { get; }
}
/// <summary>
/// An IInteractableView defines the view for an object that can be
/// interacted with.
/// </summary>
public interface IInteractableView : IComponent
{
InteractableState State { get; }
event Action<InteractableStateChangeArgs> WhenStateChanged;
int MaxInteractors { get; }
int MaxSelectingInteractors { get; }
int InteractorsCount { get; }
int SelectingInteractorsCount { get; }
event Action WhenInteractorsCountUpdated;
event Action WhenSelectingInteractorsCountUpdated;
}
/// <summary>
/// An object that can be interacted with, an IInteractable can, in addition to
/// an IInteractableView, be enabled or disabled.
/// </summary>
public interface IInteractable : IInteractableView
{
void Enable();
void Disable();
void RemoveInteractorById(int id);
new int MaxInteractors { get; set; }
new int MaxSelectingInteractors { get; set; }
}
/// <summary>
/// An IInteractableView{out TInteractor} defines additional members for IInteractableView
/// that expose the concrete types interacting with this object.
/// </summary>
public interface IInteractableView<out TInteractor> : IInteractableView
{
IEnumerable<TInteractor> Interactors { get; }
IEnumerable<TInteractor> SelectingInteractors { get; }
MAction<TInteractor> WhenInteractorAdded { get; }
MAction<TInteractor> WhenInteractorRemoved { get; }
MAction<TInteractor> WhenSelectingInteractorAdded { get; }
MAction<TInteractor> WhenSelectingInteractorRemoved { get; }
}
/// <summary>
/// An Interactable{TInteractor} can have its set of Concrete Interactors
/// modified by an external party.
/// </summary>
public interface IInteractable<TInteractor> : IInteractable, IInteractableView<TInteractor>
{
bool IsPotentialCandidateFor(TInteractor interactor);
bool HasInteractor(TInteractor interactor);
void AddInteractor(TInteractor interactor);
void RemoveInteractor(TInteractor interactor);
bool HasSelectingInteractor(TInteractor interactor);
void AddSelectingInteractor(TInteractor interactor);
void RemoveSelectingInteractor(TInteractor interactor);
}
}

View File

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

View File

@@ -0,0 +1,19 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
namespace Oculus.Interaction
{
public interface IInteractableFilter
{
bool FilterInteractable(IInteractable interactable);
}
}

View File

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

View File

@@ -0,0 +1,28 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections.Generic;
namespace Oculus.Interaction
{
/// <summary>
/// A interface for a registry that houses a set of concrete Interactables.
/// </summary>
public interface IInteractableRegistry<TInteractor, TInteractable>
where TInteractable : IInteractable<TInteractor>
{
void Register(TInteractable interactable);
void Unregister(TInteractable interactable);
IEnumerable<TInteractable> List();
IEnumerable<TInteractable> List(TInteractor interactor);
}
}

View File

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

View File

@@ -0,0 +1,79 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
namespace Oculus.Interaction
{
public struct InteractorStateChangeArgs
{
public InteractorState PreviousState;
public InteractorState NewState;
}
/// <summary>
/// IInteractorView defines the view for an object that can interact with other objects.
/// </summary>
public interface IInteractorView
{
int Identifier { get; }
bool HasInteractable { get; }
bool HasSelectedInteractable { get; }
InteractorState State { get; }
event Action<InteractorStateChangeArgs> WhenStateChanged;
event Action WhenInteractorUpdated;
}
/// <summary>
/// IInteractor defines an object that can interact with other objects
/// and can handle selection events to change its state.
/// </summary>
public interface IInteractor : IInteractorView
{
void Enable();
void Disable();
void UpdateInteractor();
void Hover();
void Select();
void Unselect();
bool HasCandidate { get; }
bool ShouldSelect { get; }
bool ShouldUnselect { get; }
}
/// <summary>
/// IInteractorView{out TInteractable} defines an InteractorView with concretely typed
/// Interactable members.
/// </summary>
public interface IInteractorView<out TInteractable> : IInteractorView
{
MAction<TInteractable> WhenInteractableSet { get; }
MAction<TInteractable> WhenInteractableUnset { get; }
MAction<TInteractable> WhenInteractableSelected { get; }
MAction<TInteractable> WhenInteractableUnselected { get; }
TInteractable Candidate { get; }
TInteractable Interactable { get; }
TInteractable SelectedInteractable { get; }
}
/// <summary>
/// IInteractor{out TInteractable} defines an IInteractor with concretely typed
/// Interactable members.
/// </summary>
public interface IInteractor<TInteractable> : IInteractor, IInteractorView<TInteractable>
{
bool IsFilterPassedBy(TInteractable interactable);
}
}

View File

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

View File

@@ -0,0 +1,40 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction
{
/// <summary>
/// IInteractionDriver drives the update loop of an Interaction.
/// </summary>
public interface IInteractorDriver
{
// This flag determines whether this driver should controls its own update loop
bool IsRootInteractorDriver { get; set; }
bool HasCandidate { get; }
bool ShouldSelect { get; }
bool IsHovering { get; }
bool IsSelecting { get; }
bool IsSelectingInteractable { get; }
void UpdateInteraction();
void UpdateHover();
void UpdateSelection(bool selectionCanBeEmpty);
void Enable();
void Disable();
}
}

View File

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

View File

@@ -0,0 +1,21 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using UnityEngine;
namespace Oculus.Interaction
{
public interface IRigidbodyRef
{
Rigidbody Rigidbody { get; }
}
}

View File

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

View File

@@ -0,0 +1,26 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
namespace Oculus.Interaction
{
/// <summary>
/// ISelector defines an input abstraction that can broadcast
/// select and release events
/// </summary>
public interface ISelector
{
event Action WhenSelected;
event Action WhenUnselected;
}
}

View File

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

View File

@@ -0,0 +1,314 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
/// <summary>
/// Interactable provides a base template for any kind of interactable object.
/// An Interactable can have Hover and HandleSelected Interactor(s) acting on it.
/// Concrete Interactables can define whether they have a One-to-One or
/// One-to-Many relationship with their associated concrete Interactors.
/// </summary>
public abstract class Interactable<TInteractor, TInteractable> : MonoBehaviour, IInteractable<TInteractor>
where TInteractor : class, IInteractor<TInteractable>
where TInteractable : Interactable<TInteractor, TInteractable>
{
/// <summary>
/// The max interactors and max selecting Interactors that this Interactable can
/// have acting on it.
/// -1 signifies NO limit (can have infinite interactors)
/// </summary>
[SerializeField]
private int _maxInteractors = -1;
[SerializeField]
private int _maxSelectingInteractors = -1;
public int MaxInteractors
{
get
{
return _maxInteractors;
}
set
{
_maxInteractors = value;
}
}
public int MaxSelectingInteractors
{
get
{
return _maxSelectingInteractors;
}
set
{
_maxSelectingInteractors = value;
}
}
private HashSet<TInteractor> _interactors = new HashSet<TInteractor>();
private HashSet<TInteractor> _selectingInteractors = new HashSet<TInteractor>();
public event Action WhenInteractorsCountUpdated = delegate { };
public event Action WhenSelectingInteractorsCountUpdated = delegate { };
private InteractableState _state = InteractableState.Disabled;
public event Action<InteractableStateChangeArgs> WhenStateChanged = delegate { };
private MultiAction<TInteractor> _whenInteractorAdded = new MultiAction<TInteractor>();
private MultiAction<TInteractor> _whenInteractorRemoved = new MultiAction<TInteractor>();
private MultiAction<TInteractor> _whenSelectingInteractorAdded = new MultiAction<TInteractor>();
private MultiAction<TInteractor> _whenSelectingInteractorRemoved = new MultiAction<TInteractor>();
public MAction<TInteractor> WhenInteractorAdded => _whenInteractorAdded;
public MAction<TInteractor> WhenInteractorRemoved => _whenInteractorRemoved;
public MAction<TInteractor> WhenSelectingInteractorAdded => _whenSelectingInteractorAdded;
public MAction<TInteractor> WhenSelectingInteractorRemoved => _whenSelectingInteractorRemoved;
public InteractableState State
{
get
{
return _state;
}
private set
{
if (_state == value) return;
InteractableState previousState = _state;
_state = value;
WhenStateChanged(new InteractableStateChangeArgs
{
PreviousState = previousState,
NewState = _state
});
}
}
private static IInteractableRegistry<TInteractor, TInteractable> _registry =
new InteractableRegistry<TInteractor, TInteractable>();
public static IInteractableRegistry<TInteractor, TInteractable> Registry => _registry;
protected virtual void InteractorAdded(TInteractor interactor)
{
_whenInteractorAdded.Invoke(interactor);
}
protected virtual void InteractorRemoved(TInteractor interactor)
{
_whenInteractorRemoved.Invoke(interactor);
}
protected virtual void SelectingInteractorAdded(TInteractor interactor)
{
_whenSelectingInteractorAdded.Invoke(interactor);
}
protected virtual void SelectingInteractorRemoved(TInteractor interactor)
{
_whenSelectingInteractorRemoved.Invoke(interactor);
}
public int InteractorsCount => _interactors.Count;
public int SelectingInteractorsCount => _selectingInteractors.Count;
public IEnumerable<TInteractor> Interactors => _interactors;
public IEnumerable<TInteractor> SelectingInteractors => _selectingInteractors;
public void AddInteractor(TInteractor interactor)
{
_interactors.Add(interactor);
WhenInteractorsCountUpdated();
InteractorAdded(interactor);
UpdateInteractableState();
}
public void RemoveInteractor(TInteractor interactor)
{
if (!_interactors.Remove(interactor))
{
return;
}
WhenInteractorsCountUpdated();
InteractorRemoved(interactor);
UpdateInteractableState();
}
public void AddSelectingInteractor(TInteractor interactor)
{
_selectingInteractors.Add(interactor);
WhenSelectingInteractorsCountUpdated();
SelectingInteractorAdded(interactor);
UpdateInteractableState();
}
public void RemoveSelectingInteractor(TInteractor interactor)
{
if (!_selectingInteractors.Remove(interactor))
{
return;
}
WhenSelectingInteractorsCountUpdated();
SelectingInteractorRemoved(interactor);
UpdateInteractableState();
}
private void UpdateInteractableState()
{
if (State == InteractableState.Disabled) return;
if (SelectingInteractorsCount > 0)
{
State = InteractableState.Select;
}
else if (InteractorsCount > 0)
{
State = InteractableState.Hover;
}
else
{
State = InteractableState.Normal;
}
}
public bool IsPotentialCandidateFor(TInteractor interactor)
{
if (State == InteractableState.Disabled)
{
return false;
}
if (MaxSelectingInteractors >= 0 &&
SelectingInteractorsCount == MaxSelectingInteractors)
{
return false;
}
if (MaxInteractors >= 0 &&
InteractorsCount == MaxInteractors &&
!_interactors.Contains(interactor))
{
return false;
}
return true;
}
public bool HasInteractor(TInteractor interactor)
{
return _interactors.Contains(interactor);
}
public bool HasSelectingInteractor(TInteractor interactor)
{
return _selectingInteractors.Contains(interactor);
}
public void Enable()
{
if (State != InteractableState.Disabled) return;
_registry.Register((TInteractable)this);
State = InteractableState.Normal;
}
public void Disable()
{
if (State == InteractableState.Disabled) return;
List<TInteractor> selectingInteractorsCopy = new List<TInteractor>(_selectingInteractors);
foreach (TInteractor selectingInteractor in selectingInteractorsCopy)
{
RemoveSelectingInteractor(selectingInteractor);
}
List<TInteractor> interactorsCopy = new List<TInteractor>(_interactors);
foreach (TInteractor interactor in interactorsCopy)
{
RemoveInteractor(interactor);
}
State = InteractableState.Disabled;
_registry.Unregister((TInteractable)this);
}
public void RemoveInteractorById(int id)
{
if (State != InteractableState.Select)
{
return;
}
TInteractor foundInteractor = null;
foreach (TInteractor selectingInteractor in _selectingInteractors)
{
if (selectingInteractor.Identifier == id)
{
foundInteractor = selectingInteractor;
break;
}
}
if (foundInteractor == null)
{
return;
}
RemoveSelectingInteractor(foundInteractor);
foundInteractor = null;
foreach (TInteractor interactor in _interactors)
{
if (interactor.Identifier == id)
{
foundInteractor = interactor;
break;
}
}
if (foundInteractor == null)
{
return;
}
RemoveInteractor(foundInteractor);
}
protected virtual void OnEnable()
{
Enable();
}
protected virtual void OnDisable()
{
Disable();
}
protected virtual void SetRegistry(IInteractableRegistry<TInteractor, TInteractable> registry)
{
if (registry == _registry) return;
IEnumerable<TInteractable> interactables = _registry.List();
foreach (TInteractable interactable in interactables)
{
registry.Register(interactable);
_registry.Unregister(interactable);
}
_registry = registry;
}
}
}

View File

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

View File

@@ -0,0 +1,170 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
/// <summary>
/// This class implements higher level logic to share the number of max
/// max interactors acting on this group of interactors at a time.
/// </summary>
public class InteractableGroup : MonoBehaviour
{
[SerializeField, Interface(typeof(IInteractable))]
private List<MonoBehaviour> _interactables;
private List<IInteractable> Interactables;
private List<InteractableLimits> _limits;
private struct InteractableLimits
{
public int MaxInteractors;
public int MaxSelectingInteractors;
}
[SerializeField]
private int _maxInteractors;
[SerializeField]
private int _maxSelectingInteractors;
private int _interactors;
private int _selectInteractors;
protected bool _started = false;
protected virtual void Awake()
{
Interactables = _interactables.ConvertAll(mono => mono as IInteractable);
}
protected virtual void Start()
{
this.BeginStart(ref _started);
foreach (IInteractable interactable in Interactables)
{
Assert.IsNotNull(interactable);
}
Assert.IsTrue(_interactables != null && _interactables.Count > 0);
_limits = new List<InteractableLimits>();
foreach (IInteractable interactable in Interactables)
{
_limits.Add(new InteractableLimits()
{
MaxInteractors = interactable.MaxInteractors,
MaxSelectingInteractors = interactable.MaxSelectingInteractors
});
}
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
foreach (IInteractable interactable in Interactables)
{
interactable.WhenInteractorsCountUpdated += CountWhenInteractors;
interactable.WhenSelectingInteractorsCountUpdated += CountWhenSelectingInteractors;
}
CountWhenInteractors();
CountWhenSelectingInteractors();
}
}
protected virtual void OnDisable()
{
if (_started)
{
foreach (IInteractable interactable in Interactables)
{
interactable.WhenInteractorsCountUpdated -= CountWhenInteractors;
interactable.WhenSelectingInteractorsCountUpdated -= CountWhenSelectingInteractors;
}
CountWhenInteractors();
CountWhenSelectingInteractors();
}
}
private void CountWhenInteractors()
{
_interactors = 0;
foreach (IInteractable interactable in Interactables)
{
_interactors += interactable.InteractorsCount;
}
UpdateMaxInteractors();
}
private void UpdateMaxInteractors()
{
if (_maxInteractors == -1) return;
int remainingInteractors = Mathf.Max(0, _maxInteractors - _interactors);
for (int i = 0; i < Interactables.Count; i++)
{
Interactables[i].MaxInteractors = (_limits[i].MaxInteractors == -1
? remainingInteractors
: Mathf.Max(0, _limits[i].MaxInteractors - _interactors)) +
Interactables[i].InteractorsCount;
}
}
private void CountWhenSelectingInteractors()
{
_selectInteractors = 0;
foreach (IInteractable interactable in Interactables)
{
_selectInteractors += interactable.SelectingInteractorsCount;
}
UpdateMaxActive();
}
private void UpdateMaxActive()
{
if (_maxSelectingInteractors == -1) return;
int remainingActive = Mathf.Max(0, _maxSelectingInteractors - _selectInteractors);
for (int i = 0; i < Interactables.Count; i++)
{
Interactables[i].MaxSelectingInteractors = (_limits[i].MaxSelectingInteractors == -1
? remainingActive
: Mathf.Max(0, _limits[i].MaxSelectingInteractors - _selectInteractors)) +
Interactables[i].SelectingInteractorsCount;
}
}
#region Inject
public void InjectAllInteractableGroup(List<IInteractable> interactables)
{
InjectInteractables(interactables);
}
public void InjectInteractables(List<IInteractable> interactables)
{
Interactables = interactables;
_interactables =
interactables.ConvertAll(interactable => interactable as MonoBehaviour);
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,183 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
/// <summary>
/// This class implements higher level logic to forward the highest IInteractable
/// state of any of the interactables in its list
/// </summary>
public class InteractableGroupView : MonoBehaviour, IInteractableView
{
[SerializeField, Interface(typeof(IInteractable))]
private List<MonoBehaviour> _interactables;
private List<IInteractable> Interactables;
public int InteractorsCount
{
get
{
int count = 0;
foreach (IInteractable interactable in Interactables)
{
count += interactable.InteractorsCount;
}
return count;
}
}
public int SelectingInteractorsCount
{
get
{
int count = 0;
foreach (IInteractable interactable in Interactables)
{
count += interactable.SelectingInteractorsCount;
}
return count;
}
}
public int MaxInteractors
{
get
{
int max = 0;
foreach (IInteractable interactable in Interactables)
{
max = Mathf.Max(interactable.MaxInteractors, max);
}
return max;
}
}
public int MaxSelectingInteractors
{
get
{
int max = 0;
foreach (IInteractable interactable in Interactables)
{
max = Mathf.Max(interactable.MaxSelectingInteractors, max);
}
return max;
}
}
public event Action WhenInteractorsCountUpdated = delegate { };
public event Action WhenSelectingInteractorsCountUpdated = delegate { };
public event Action<InteractableStateChangeArgs> WhenStateChanged = delegate { };
private InteractableState _state = InteractableState.Normal;
public InteractableState State
{
get
{
return _state;
}
set
{
if (_state == value) return;
InteractableState previousState = _state;
_state = value;
WhenStateChanged(new InteractableStateChangeArgs { PreviousState = previousState, NewState = _state });
}
}
private void UpdateState()
{
if (SelectingInteractorsCount > 0)
{
State = InteractableState.Select;
return;
}
if (InteractorsCount > 0)
{
State = InteractableState.Hover;
return;
}
State = InteractableState.Normal;
}
protected virtual void Awake()
{
Interactables = _interactables.ConvertAll(mono => mono as IInteractable);
}
protected bool _started = false;
protected virtual void Start()
{
this.BeginStart(ref _started);
foreach (IInteractable interactable in Interactables)
{
Assert.IsNotNull(interactable);
}
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
foreach (IInteractable interactable in Interactables)
{
interactable.WhenStateChanged += HandleStateChange;
}
}
}
protected virtual void OnDisable()
{
if (_started)
{
foreach (IInteractable interactable in Interactables)
{
interactable.WhenStateChanged -= HandleStateChange;
}
}
}
private void HandleStateChange(InteractableStateChangeArgs args)
{
UpdateState();
}
#region Inject
public void InjectAllInteractableGroupView(List<IInteractable> interactables)
{
InjectInteractables(interactables);
}
public void InjectInteractables(List<IInteractable> interactables)
{
Interactables = interactables;
_interactables =
Interactables.ConvertAll(interactable => interactable as MonoBehaviour);
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,91 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections.Generic;
namespace Oculus.Interaction
{
/// <summary>
/// A registry that houses a set of concrete Interactables.
/// </summary>
public class InteractableRegistry<TInteractor, TInteractable> :
IInteractableRegistry<TInteractor, TInteractable>
where TInteractor:IInteractor<TInteractable>
where TInteractable : IInteractable<TInteractor>
{
private static List<TInteractable> _interactables;
private List<TInteractable> _interactableEnumeratorList;
public InteractableRegistry()
{
_interactables = new List<TInteractable>();
_interactableEnumeratorList = new List<TInteractable>();
}
public virtual void Register(TInteractable interactable) => _interactables.Add(interactable);
public virtual void Unregister(TInteractable interactable) => _interactables.Remove(interactable);
protected IEnumerable<TInteractable> PruneInteractables(IEnumerable<TInteractable> interactables,
TInteractor interactor)
{
int interactableCount = 0;
foreach (TInteractable interactable in interactables)
{
if (!interactor.IsFilterPassedBy(interactable))
{
continue;
}
if (!interactable.IsPotentialCandidateFor(interactor))
{
continue;
}
if (interactableCount == _interactableEnumeratorList.Count)
{
_interactableEnumeratorList.Add(interactable);
}
else
{
_interactableEnumeratorList[interactableCount] = interactable;
}
interactableCount++;
}
return GetRange(_interactableEnumeratorList, 0, interactableCount);
}
public virtual IEnumerable<TInteractable> List(TInteractor interactor)
{
return PruneInteractables(_interactables, interactor);
}
public virtual IEnumerable<TInteractable> List()
{
return _interactables;
}
private IEnumerable<T> GetRange<T>(IEnumerable<T> source, int start, int end)
{
using (IEnumerator<T> e = source.GetEnumerator())
{
int i = 0;
while (i < start && e.MoveNext()) { i++; }
while (i < end && e.MoveNext())
{
yield return e.Current;
i++;
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,22 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
namespace Oculus.Interaction
{
public enum InteractableState
{
Normal,
Hover,
Select,
Disabled
};
}

View File

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

View File

@@ -0,0 +1,128 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Oculus.Interaction
{
/// <summary>
/// Acts as a forwarder of Trigger events for Rigidbody overlaps. Used in conjunction with
/// CollisionInteractionRegistry.
///
/// Note: If Physics.autoSimulation is false, ForceGlobalUpdateTrigger should be called
/// after every call to Physics.Simulate
/// </summary>
public class InteractableTriggerBroadcaster : MonoBehaviour
{
public Action<IInteractable, Rigidbody> OnTriggerEntered = delegate { };
public Action<IInteractable, Rigidbody> OnTriggerExited = delegate { };
private IInteractable _interactable;
private Dictionary<Rigidbody, bool> _rigidbodyTriggers;
private static HashSet<InteractableTriggerBroadcaster> _broadcasters =
new HashSet<InteractableTriggerBroadcaster>();
protected bool _started = false;
protected virtual void Start()
{
this.BeginStart(ref _started);
_rigidbodyTriggers = new Dictionary<Rigidbody, bool>();
this.EndStart(ref _started);
}
protected virtual void OnTriggerStay(Collider collider)
{
Rigidbody rigidbody = collider.attachedRigidbody;
if (rigidbody == null) return;
if (!_rigidbodyTriggers.ContainsKey(rigidbody))
{
OnTriggerEntered(_interactable, rigidbody);
_rigidbodyTriggers.Add(rigidbody, true);
}
else
{
_rigidbodyTriggers[rigidbody] = true;
}
}
protected virtual void OnEnable()
{
if (_started)
{
_broadcasters.Add(this);
}
}
protected virtual void FixedUpdate()
{
if (!Physics.autoSimulation)
{
return;
}
UpdateTriggers();
}
private void UpdateTriggers()
{
List<Rigidbody> rigidbodys = new List<Rigidbody>(_rigidbodyTriggers.Keys);
foreach (Rigidbody rigidbody in rigidbodys)
{
if (_rigidbodyTriggers[rigidbody] == false)
{
_rigidbodyTriggers.Remove(rigidbody);
OnTriggerExited(_interactable, rigidbody);
}
else
{
_rigidbodyTriggers[rigidbody] = false;
}
}
}
protected virtual void OnDisable()
{
if (_started)
{
// Clean up any remaining active triggers
foreach (Rigidbody rigidbody in _rigidbodyTriggers.Keys)
{
OnTriggerExited(_interactable, rigidbody);
}
_broadcasters.Remove(this);
}
}
public static void ForceGlobalUpdateTriggers()
{
foreach (InteractableTriggerBroadcaster broadcaster in _broadcasters)
{
broadcaster.UpdateTriggers();
}
}
#region Inject
public void InjectAllInteractableTriggerBroadcaster(IInteractable interactable)
{
InjectInteractable(interactable);
}
public void InjectInteractable(IInteractable interactable)
{
_interactable = interactable;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,361 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
/// <summary>
/// Interactor provides a base template for any kind of interaction.
/// Interactions can be wholly defined by three things: the concrete Interactor,
/// the concrete Interactable, and the logic governing their coordination.
///
/// Subclasses are responsible for implementing that coordination logic via template
/// methods that operate on the concrete interactor and interactable classes.
/// </summary>
public abstract class Interactor<TInteractor, TInteractable> : MonoBehaviour, IInteractor<TInteractable>
where TInteractor : class, IInteractor<TInteractable>
where TInteractable : class, IInteractable<TInteractor>
{
[SerializeField, Interface(typeof(IInteractableFilter)), Optional]
private List<MonoBehaviour> _interactableFilters;
private List<IInteractableFilter> InteractableFilters = null;
protected virtual void DoEveryUpdate() { }
protected virtual void DoNormalUpdate() { }
protected virtual void DoHoverUpdate() { }
protected virtual void DoSelectUpdate(TInteractable interactable = null) { }
public virtual bool ShouldSelect { get; protected set; }
public virtual bool ShouldUnselect { get; protected set; }
private InteractorState _state = InteractorState.Normal;
public event Action<InteractorStateChangeArgs> WhenStateChanged = delegate { };
public event Action WhenInteractorUpdated = delegate { };
public InteractorState State
{
get
{
return _state;
}
private set
{
if (_state == value) return;
InteractorState previousState = _state;
_state = value;
WhenStateChanged(new InteractorStateChangeArgs
{
PreviousState = previousState,
NewState = _state
});
}
}
protected TInteractable _candidate;
protected TInteractable _interactable;
protected TInteractable _selectedInteractable;
public TInteractable Candidate => _candidate;
public TInteractable Interactable => _interactable;
public TInteractable SelectedInteractable => _selectedInteractable;
public bool HasCandidate => _candidate != null;
public bool HasInteractable => _interactable != null;
public bool HasSelectedInteractable => _selectedInteractable != null;
private MultiAction<TInteractable> _whenInteractableSet = new MultiAction<TInteractable>();
private MultiAction<TInteractable> _whenInteractableUnset = new MultiAction<TInteractable>();
private MultiAction<TInteractable> _whenInteractableSelected = new MultiAction<TInteractable>();
private MultiAction<TInteractable> _whenInteractableUnselected = new MultiAction<TInteractable>();
public MAction<TInteractable> WhenInteractableSet => _whenInteractableSet;
public MAction<TInteractable> WhenInteractableUnset => _whenInteractableUnset;
public MAction<TInteractable> WhenInteractableSelected => _whenInteractableSelected;
public MAction<TInteractable> WhenInteractableUnselected => _whenInteractableUnselected;
protected virtual void InteractableSet(TInteractable interactable)
{
_whenInteractableSet.Invoke(interactable);
}
protected virtual void InteractableUnset(TInteractable interactable)
{
_whenInteractableUnset.Invoke(interactable);
}
protected virtual void InteractableSelected(TInteractable interactable)
{
_whenInteractableSelected.Invoke(interactable);
}
protected virtual void InteractableUnselected(TInteractable interactable)
{
_whenInteractableUnselected.Invoke(interactable);
}
private UniqueIdentifier _identifier;
public int Identifier => _identifier.ID;
protected virtual void Awake()
{
_identifier = UniqueIdentifier.Generate();
InteractableFilters =
_interactableFilters.ConvertAll(mono => mono as IInteractableFilter);
}
protected virtual void Start()
{
foreach (IInteractableFilter filter in InteractableFilters)
{
Assert.IsNotNull(filter);
}
}
protected virtual void OnEnable()
{
Enable();
}
protected virtual void OnDisable()
{
Disable();
}
protected virtual void OnDestroy()
{
UniqueIdentifier.Release(_identifier);
}
private void CandidateUpdate()
{
if (State == InteractorState.Select) return;
if (State == InteractorState.Disabled)
{
UnsetInteractable();
return;
}
_candidate = ComputeCandidate();
}
public void UpdateInteractor()
{
if (State == InteractorState.Disabled) return;
InteractableChangesUpdate();
DoEveryUpdate();
NormalUpdate();
CandidateUpdate();
HoverUpdate();
SelectUpdate();
WhenInteractorUpdated();
}
private void InteractableChangesUpdate()
{
if (State == InteractorState.Select)
{
if (_selectedInteractable != null &&
!_selectedInteractable.HasSelectingInteractor(this as TInteractor))
{
TInteractable interactable = _selectedInteractable;
_selectedInteractable = null;
InteractableUnselected(interactable);
}
if (_interactable != null &&
!_interactable.HasInteractor(this as TInteractor))
{
TInteractable interactable = _interactable;
_interactable = null;
InteractableUnset(interactable);
}
}
if(State == InteractorState.Hover &&
_interactable != null &&
!_interactable.HasInteractor(this as TInteractor))
{
TInteractable interactable = _interactable;
_interactable = null;
InteractableUnset(interactable);
State = InteractorState.Normal;
}
}
public virtual void Select()
{
ShouldSelect = false;
if (State == InteractorState.Select) return;
TInteractable interactable = _interactable;
if (interactable != null)
{
if (interactable.IsPotentialCandidateFor(this as TInteractor))
{
SelectInteractable(interactable);
}
else
{
State = InteractorState.Normal;
}
}
else
{
// Selected with no interactable
State = InteractorState.Select;
}
SelectUpdate();
}
public virtual void Unselect()
{
ShouldUnselect = false;
if (State != InteractorState.Select) return;
UnselectInteractable();
UpdateInteractor();
}
// Returns the best interactable for selection or null
protected abstract TInteractable ComputeCandidate();
public virtual bool IsFilterPassedBy(TInteractable interactable)
{
if (InteractableFilters == null)
{
return true;
}
foreach (IInteractableFilter interactableFilter in InteractableFilters)
{
if (!interactableFilter.FilterInteractable(interactable))
{
return false;
}
}
return true;
}
public void Hover()
{
if (_candidate != null)
{
SetInteractable(_candidate);
HoverUpdate();
}
else
{
UnsetInteractable();
}
}
private void NormalUpdate()
{
if (State != InteractorState.Normal)
{
return;
}
DoNormalUpdate();
}
private void HoverUpdate()
{
if (State != InteractorState.Hover)
{
return;
}
DoHoverUpdate();
}
private void SelectUpdate()
{
if (State != InteractorState.Select)
{
return;
}
DoSelectUpdate(_selectedInteractable);
}
private void SetInteractable(TInteractable interactable)
{
if (_interactable == interactable) return;
UnsetInteractable();
_interactable = interactable;
interactable.AddInteractor(this as TInteractor);
InteractableSet(interactable);
State = InteractorState.Hover;
}
private void UnsetInteractable()
{
TInteractable interactable = _interactable;
if (interactable == null) return;
_interactable = null;
interactable.RemoveInteractor(this as TInteractor);
InteractableUnset(interactable);
State = InteractorState.Normal;
}
private void SelectInteractable(TInteractable interactable)
{
UnselectInteractable();
_selectedInteractable = interactable;
interactable.AddSelectingInteractor(this as TInteractor);
InteractableSelected(interactable);
State = InteractorState.Select;
}
private void UnselectInteractable()
{
TInteractable interactable = _selectedInteractable;
if (interactable == null)
{
State = InteractorState.Normal;
return;
}
interactable.RemoveSelectingInteractor(this as TInteractor);
_selectedInteractable = null;
InteractableUnselected(interactable);
State = InteractorState.Hover;
}
public void Enable()
{
if (State != InteractorState.Disabled) return;
State = InteractorState.Normal;
}
public void Disable()
{
if (State == InteractorState.Disabled) return;
UnselectInteractable();
UnsetInteractable();
State = InteractorState.Disabled;
}
#region Inject
public void InjectOptionalInteractableFilters(List<IInteractableFilter> interactableFilters)
{
InteractableFilters = interactableFilters;
_interactableFilters = interactableFilters.ConvertAll(interactableFilter =>
interactableFilter as MonoBehaviour);
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,101 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
public class InteractorActiveState : MonoBehaviour, IActiveState
{
[System.Flags]
public enum InteractorProperty
{
HasCandidate = 1 << 0,
HasInteractable = 1 << 1,
IsSelecting = 1 << 2,
HasSelectedInteractable = 1 << 3,
}
[SerializeField, Interface(typeof(IInteractor))]
private MonoBehaviour _interactor;
private IInteractor Interactor;
[SerializeField]
private InteractorProperty _property;
public InteractorProperty Property
{
get
{
return _property;
}
set
{
_property = value;
}
}
public bool Active
{
get
{
if((_property & InteractorProperty.HasCandidate) != 0
&& Interactor.HasCandidate)
{
return true;
}
if((_property & InteractorProperty.HasInteractable) != 0
&& Interactor.HasInteractable)
{
return true;
}
if((_property & InteractorProperty.IsSelecting) != 0
&& Interactor.State == InteractorState.Select)
{
return true;
}
if((_property & InteractorProperty.HasSelectedInteractable) != 0
&& Interactor.HasSelectedInteractable)
{
return true;
}
return false;
}
}
protected virtual void Awake()
{
Interactor = _interactor as IInteractor;
}
protected virtual void Start()
{
Assert.IsNotNull(Interactor);
}
#region Inject
public void InjectAllInteractorActiveState(IInteractor interactor)
{
InjectInteractor(interactor);
}
public void InjectInteractor(IInteractor interactor)
{
_interactor = interactor as MonoBehaviour;
Interactor = interactor;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,225 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
/// <summary>
/// InteractorDriver provides a means to drive the update loop of an Interactor.
/// Optionally can be provided a Selector to drive selection of the Interactor.
/// Optionally can be provided an ActiveState to enable or disable the Interactor.
/// </summary>
public class InteractorDriver : MonoBehaviour, IInteractorDriver
{
[SerializeField, Interface(typeof(IInteractor))]
private MonoBehaviour _interactor;
public IInteractor Interactor;
[SerializeField, Interface(typeof(ISelector)), Optional]
private MonoBehaviour _selector;
private ISelector Selector = null;
[SerializeField, Interface(typeof(IActiveState)), Optional]
private MonoBehaviour _activeState;
private IActiveState ActiveState = null;
public bool IsRootInteractorDriver { get; set; } = true;
public bool IsSelectingInteractable => Interactor.HasSelectedInteractable;
public bool IsHovering => Interactor.State == InteractorState.Hover;
public bool IsSelecting => Interactor.State == InteractorState.Select;
public bool HasCandidate => Interactor.HasCandidate;
public bool ShouldSelect => _performSelect || Interactor.ShouldSelect;
private bool _performSelect = false;
private bool _performUnselect = false;
private bool _selected = false;
protected bool _started = false;
protected virtual void Awake()
{
Interactor = _interactor as IInteractor;
Selector = _selector as ISelector;
ActiveState = _activeState as IActiveState;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
Assert.IsNotNull(Interactor);
if (_selector != null)
{
Assert.IsNotNull(Selector);
}
if (_activeState != null)
{
Assert.IsNotNull(ActiveState);
}
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
if (Selector != null)
{
Selector.WhenSelected += HandleSelected;
Selector.WhenUnselected += HandleUnselected;
}
}
}
protected virtual void OnDisable()
{
if (_started)
{
if (Selector != null)
{
Selector.WhenSelected -= HandleSelected;
Selector.WhenUnselected -= HandleUnselected;
_performSelect = _performUnselect = false;
if (_selected)
{
_selected = false;
Interactor.Unselect();
}
}
}
}
protected virtual void Update()
{
if (!IsRootInteractorDriver) return;
UpdateInteraction();
UpdateHover();
UpdateSelection(true);
}
private bool UpdateActiveState()
{
if (ActiveState == null || ActiveState.Active)
{
return true;
}
_performSelect = _performUnselect = false;
Interactor.Disable();
return false;
}
public void UpdateInteraction()
{
if (!UpdateActiveState())
{
return;
}
Interactor.Enable();
Interactor.UpdateInteractor();
}
public void UpdateHover()
{
if (!UpdateActiveState())
{
return;
}
Interactor.Enable();
Interactor.Hover();
}
public void UpdateSelection(bool selectionCanBeEmpty)
{
if (!UpdateActiveState())
{
return;
}
Interactor.Enable();
_performSelect |= Interactor.ShouldSelect;
if (_performSelect)
{
_selected = true;
if (selectionCanBeEmpty || Interactor.HasInteractable)
{
Interactor.Select();
}
}
_performUnselect |= Interactor.ShouldUnselect;
if (_performUnselect)
{
_selected = false;
Interactor.Unselect();
}
_performSelect = _performUnselect = false;
}
public void HandleSelected()
{
_performSelect = true;
}
public void HandleUnselected()
{
_performUnselect = true;
}
public void Enable()
{
if (ActiveState != null && !ActiveState.Active) return;
Interactor.Enable();
}
public void Disable()
{
_performSelect = _performUnselect = false;
Interactor.Disable();
}
#region Inject
public void InjectAllInteractorDriver(IInteractor interactor)
{
InjectInteractor(interactor);
}
public void InjectInteractor(IInteractor interactor)
{
_interactor = interactor as MonoBehaviour;
Interactor = interactor;
}
public void InjectOptionalSelector(ISelector selector)
{
_selector = selector as MonoBehaviour;
Selector = selector;
}
public void InjectOptionalActiveState(IActiveState activeState)
{
_activeState = activeState as MonoBehaviour;
ActiveState = activeState;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,335 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
/// <summary>
/// The InteractorDriverGroup coordinates between multiple InteractorDrivers to determine
/// which InteractorDriver(s) should be enabled at a time.
/// Passed in InteractorDrivers are prioritized in list order (first = highest priority).
public class InteractorDriverGroup : MonoBehaviour, IInteractorDriver
{
[SerializeField, Interface(typeof(IInteractorDriver))]
private List<MonoBehaviour> _interactorDrivers;
private List<IInteractorDriver> InteractorDrivers;
// The HoverStrategy defines how this InteractorDriverGroup coordinates hovering
// between InteractorDrivers when no InteractorDriver is yet selected.
// Upon selection, all other drivers are disabled.
public enum InteractorDriverGroupStrategy
{
// Keep checking all drivers for hover in priority order, disabling all others
PRIORITY = 0,
// When any driver is hovered, disable all other drivers until this driver is no longer hovering
FIRST = 1,
// Allow for multiple hovered drivers at a time
MULTIPLE = 2
}
[SerializeField]
private InteractorDriverGroupStrategy _interactorDriverGroupStrategy =
InteractorDriverGroupStrategy.PRIORITY;
public bool IsRootInteractorDriver { get; set; } = true;
private IInteractorDriver _currentDriver = null;
private IInteractorDriver _selectingDriver = null;
[SerializeField]
private bool _selectionCanBeEmpty = true;
protected virtual void Awake()
{
InteractorDrivers = _interactorDrivers.ConvertAll(mono => mono as IInteractorDriver);
}
protected virtual void Start()
{
foreach (IInteractorDriver interactorDriver in InteractorDrivers)
{
Assert.IsNotNull(interactorDriver);
}
foreach (IInteractorDriver interactorDriver in InteractorDrivers)
{
interactorDriver.IsRootInteractorDriver = false;
}
}
protected virtual void Update()
{
if (!IsRootInteractorDriver) return;
UpdateInteraction();
UpdateHover();
UpdateSelection(_selectionCanBeEmpty);
}
public void UpdateInteraction()
{
if (_selectingDriver != null)
{
_selectingDriver.UpdateInteraction();
return;
}
switch (_interactorDriverGroupStrategy)
{
case InteractorDriverGroupStrategy.PRIORITY:
PriorityHoverStrategy();
break;
case InteractorDriverGroupStrategy.FIRST:
FirstHoverStrategy();
break;
case InteractorDriverGroupStrategy.MULTIPLE:
MultiHoverStrategy();
break;
}
}
public void UpdateHover()
{
if (_selectingDriver != null || _currentDriver == null)
{
return;
}
_currentDriver.UpdateHover();
switch (_interactorDriverGroupStrategy)
{
case InteractorDriverGroupStrategy.PRIORITY:
case InteractorDriverGroupStrategy.FIRST:
if (_currentDriver != null)
{
_currentDriver.UpdateHover();
}
break;
case InteractorDriverGroupStrategy.MULTIPLE:
foreach(IInteractorDriver driver in InteractorDrivers)
{
if (!driver.HasCandidate)
{
continue;
}
driver.UpdateHover();
}
break;
}
}
public void UpdateSelection(bool selectionCanBeEmpty)
{
if (_selectingDriver != null)
{
_selectingDriver.UpdateSelection(selectionCanBeEmpty);
if (_selectingDriver.IsSelecting)
{
return;
}
_selectingDriver = null;
UpdateInteraction();
UpdateHover();
}
switch (_interactorDriverGroupStrategy)
{
case InteractorDriverGroupStrategy.PRIORITY:
case InteractorDriverGroupStrategy.FIRST:
if (_currentDriver != null)
{
_currentDriver.UpdateSelection(selectionCanBeEmpty);
if (_currentDriver.IsSelecting)
{
SelectDriver(_currentDriver);
}
}
break;
case InteractorDriverGroupStrategy.MULTIPLE:
foreach(IInteractorDriver driver in InteractorDrivers)
{
driver.UpdateSelection(selectionCanBeEmpty);
if (driver.IsSelecting)
{
_currentDriver = driver;
SelectDriver(_currentDriver);
return;
}
}
break;
}
}
private void PriorityHoverStrategy()
{
_currentDriver = null;
for (int i = 0; i < InteractorDrivers.Count; i++)
{
IInteractorDriver driver = InteractorDrivers[i];
driver.Enable();
driver.UpdateInteraction();
// If we are in a hover state, we want to make the hover interactor visible
if (driver.HasCandidate)
{
_currentDriver = driver;
DisableAllDriversExcept(_currentDriver);
return;
}
else if (i != InteractorDrivers.Count - 1)
{
// when we only allow only one hovering interactor,
// all but the least prioritized interactor
// should be hidden
driver.Disable();
}
}
if (_currentDriver == null)
{
_currentDriver = InteractorDrivers[InteractorDrivers.Count - 1];
}
}
private void FirstHoverStrategy()
{
if (_currentDriver != null)
{
_currentDriver.UpdateInteraction();
if (_currentDriver.HasCandidate)
{
return;
}
}
_currentDriver = null;
for (int i = 0; i < InteractorDrivers.Count; i++)
{
IInteractorDriver driver = InteractorDrivers[i];
driver.Enable();
driver.UpdateInteraction();
// If we are in a hover state, we want to make the hover interactor visible
if (driver.HasCandidate)
{
_currentDriver = driver;
DisableAllDriversExcept(_currentDriver);
return;
}
}
}
private void MultiHoverStrategy()
{
_currentDriver = null;
foreach (IInteractorDriver driver in InteractorDrivers)
{
driver.Enable();
driver.UpdateInteraction();
// If we are in a hover state, we want to make the hover interactor visible
if (driver.HasCandidate)
{
_currentDriver = driver;
}
}
}
private void SelectDriver(IInteractorDriver selectedDriver)
{
// Mark this as the selected driver
_currentDriver = selectedDriver;
_selectingDriver = selectedDriver;
DisableAllDriversExcept(_selectingDriver);
}
private void DisableAllDriversExcept(IInteractorDriver enabledDriver)
{
foreach (IInteractorDriver driver in InteractorDrivers)
{
if (driver == enabledDriver) continue;
driver.Disable();
}
}
public bool IsSelectingInteractable => _selectingDriver != null && _selectingDriver.IsSelectingInteractable;
public bool IsSelecting => _selectingDriver != null;
public bool HasCandidate => _currentDriver != null && _currentDriver.HasCandidate;
public bool ShouldSelect =>
_currentDriver != null && _currentDriver.ShouldSelect;
public bool IsHovering => _currentDriver != null && _currentDriver.IsHovering;
public void Enable()
{
foreach (IInteractorDriver interactorDriver in InteractorDrivers)
{
interactorDriver.Enable();
}
}
public void Disable()
{
foreach (IInteractorDriver interactorDriver in InteractorDrivers)
{
interactorDriver.Disable();
}
}
public void AddInteractorDriver(IInteractorDriver interactorDriver)
{
InteractorDrivers.Add(interactorDriver);
_interactorDrivers.Add(interactorDriver as MonoBehaviour);
interactorDriver.IsRootInteractorDriver = false;
}
public void RemoveInteractorDriver(IInteractorDriver interactorDriver)
{
InteractorDrivers.Remove(interactorDriver);
_interactorDrivers.Remove(interactorDriver as MonoBehaviour);
interactorDriver.IsRootInteractorDriver = true;
}
#region Inject
public void InjectAllInteractorDriverGroup(List<IInteractorDriver> interactorDrivers)
{
InjectInteractorDrivers(interactorDrivers);
}
public void InjectInteractorDrivers(List<IInteractorDriver> interactorDrivers)
{
InteractorDrivers = interactorDrivers;
_interactorDrivers = interactorDrivers.ConvertAll(interactorDriver =>
interactorDriver as MonoBehaviour);
}
public void InjectOptionalInteractorDriverGroupStrategy(
InteractorDriverGroupStrategy interactorDriverGroupStrategy)
{
_interactorDriverGroupStrategy = interactorDriverGroupStrategy;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,22 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
namespace Oculus.Interaction
{
public enum InteractorState
{
Normal,
Hover,
Select,
Disabled
};
}

View File

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

View File

@@ -0,0 +1,58 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
namespace Oculus.Interaction
{
/// <summary>
/// MAction can be used in place of Action. This allows
/// for interfaces with Actions of generic covariant types
/// to be subscribed to by multiple types of delegates.
/// </summary>
public interface MAction<out T>
{
event Action<T> Action;
}
/// <summary>
/// Classes that implement an interface that has MActions
/// can use MultiAction as their MAction implementation to
/// allow for multiple types of delegates to subscribe to the
/// generic type.
/// </summary>
public class MultiAction<T> : MAction<T>
{
protected HashSet<Action<T>> actions = new HashSet<Action<T>>();
public event Action<T> Action
{
add
{
actions.Add(value);
}
remove
{
actions.Remove(value);
}
}
public void Invoke(T t)
{
foreach (Action<T> action in actions)
{
action(t);
}
}
}
}

View File

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

View File

@@ -0,0 +1,96 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections.Generic;
using UnityEngine;
namespace Oculus.Interaction
{
/// <summary>
/// An Interactable Filter that uses tags to filter Interactables
/// </summary>
public class TagInteractableFilter : MonoBehaviour, IInteractableFilter
{
/// If there is at least one required tag, an interactable must meet one of them
[SerializeField, Optional]
private string[] _requireTags;
/// An interactable must not meet any of the avoid tags
[SerializeField, Optional]
private string[] _avoidTags;
private HashSet<string> _requireTagSet;
private HashSet<string> _avoidTagSet;
protected virtual void Start()
{
_requireTagSet = new HashSet<string>();
_avoidTagSet = new HashSet<string>();
foreach (string requireTag in _requireTags)
{
_requireTagSet.Add(requireTag);
}
foreach (string avoidTag in _avoidTags)
{
_avoidTagSet.Add(avoidTag);
}
}
public bool FilterInteractable(IInteractable interactable)
{
GameObject gameObject = interactable.gameObject;
TagSet tagSet = gameObject.GetComponent<TagSet>();
if (tagSet == null && _requireTagSet.Count > 0)
{
return false;
}
foreach (string tag in _requireTagSet)
{
if (!tagSet.ContainsTag(tag))
{
return false;
}
}
if (tagSet == null)
{
return true;
}
foreach (string tag in _avoidTagSet)
{
if (tagSet.ContainsTag(tag))
{
return false;
}
}
return true;
}
#region Inject
public void InjectOptionalRequireTags(string[] requireTags)
{
_requireTags = requireTags;
}
public void InjectOptionalAvoidTags(string[] avoidTags)
{
_avoidTags = avoidTags;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,53 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction
{
/// <summary>
/// A Tag Set that can be added to a GameObject with an Interactable to be filtered against.
/// </summary>
public class TagSet : MonoBehaviour
{
[SerializeField]
private List<string> _tags;
private HashSet<string> _tagSet;
protected virtual void Start()
{
_tagSet = new HashSet<string>();
foreach (string tag in _tags)
{
_tagSet.Add(tag);
}
}
public bool ContainsTag(string tag) => _tagSet.Contains(tag);
public void AddTag(string tag) => _tagSet.Add(tag);
public void RemoveTag(string tag) => _tagSet.Remove(tag);
#region Inject
public void InjectOptionalTags(List<string> tags)
{
_tags = tags;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,47 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
namespace Oculus.Interaction
{
public class UniqueIdentifier
{
public int ID { get; private set; }
private UniqueIdentifier(int identifier)
{
ID = identifier;
}
private static System.Random Random = new System.Random();
private static HashSet<int> _identifierSet = new HashSet<int>();
public static UniqueIdentifier Generate()
{
while (true)
{
int identifier = Random.Next(Int32.MaxValue);
if (_identifierSet.Contains(identifier)) continue;
_identifierSet.Add(identifier);
return new UniqueIdentifier(identifier);
}
}
public static void Release(UniqueIdentifier identifier)
{
_identifierSet.Remove(identifier.ID);
}
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,135 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
public class GrabInteractable : Interactable<GrabInteractor, GrabInteractable>,
IPointable, IRigidbodyRef
{
private Collider[] _colliders;
public Collider[] Colliders => _colliders;
[SerializeField]
Rigidbody _rigidbody;
public Rigidbody Rigidbody => _rigidbody;
[SerializeField]
private float _releaseDistance = 0f;
public float ReleaseDistance => _releaseDistance;
[SerializeField, Optional]
private PhysicsTransformable _physicsObject = null;
private static CollisionInteractionRegistry<GrabInteractor, GrabInteractable> _grabRegistry = null;
public event Action<PointerArgs> OnPointerEvent = delegate { };
private PointableDelegate<GrabInteractor> _pointableDelegate;
protected bool _started = false;
protected virtual void Awake()
{
if (_grabRegistry == null)
{
_grabRegistry = new CollisionInteractionRegistry<GrabInteractor, GrabInteractable>();
SetRegistry(_grabRegistry);
}
}
protected virtual void Start()
{
this.BeginStart(ref _started);
Assert.IsNotNull(Rigidbody);
_colliders = Rigidbody.GetComponentsInChildren<Collider>();
Assert.IsTrue(Colliders.Length > 0,
"The associated Rigidbody must have at least one Collider.");
_pointableDelegate =
new PointableDelegate<GrabInteractor>(this, ComputePointer);
this.EndStart(ref _started);
}
public void ApplyVelocities(Vector3 linearVelocity, Vector3 angularVelocity)
{
if (_physicsObject == null)
{
return;
}
_physicsObject.ApplyVelocities(linearVelocity, angularVelocity);
}
private void ComputePointer(GrabInteractor interactor, out Vector3 position, out Quaternion rotation)
{
position = interactor.GrabPosition;
rotation = interactor.GrabRotation;
}
protected override void OnEnable()
{
base.OnEnable();
if (_started)
{
_pointableDelegate.OnPointerEvent += InvokeOnPointerEvent;
}
}
protected override void OnDisable()
{
if (_started)
{
_pointableDelegate.OnPointerEvent -= InvokeOnPointerEvent;
}
base.OnDisable();
}
private void InvokeOnPointerEvent(PointerArgs args)
{
OnPointerEvent(args);
}
protected virtual void OnDestroy()
{
_pointableDelegate = null;
}
#region Inject
public void InjectAllGrabInteractable(Rigidbody rigidbody)
{
InjectRigidbody(rigidbody);
}
public void InjectRigidbody(Rigidbody rigidbody)
{
_rigidbody = rigidbody;
}
public void InjectOptionalReleaseDistance(float releaseDistance)
{
_releaseDistance = releaseDistance;
}
public void InjectOptionalPhysicsObject(PhysicsTransformable physicsObject)
{
_physicsObject = physicsObject;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,196 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Throw;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
public class GrabInteractor : Interactor<GrabInteractor, GrabInteractable>, IRigidbodyRef
{
[SerializeField]
private Transform _targetTransform;
[SerializeField]
private Rigidbody _rigidbody;
public Rigidbody Rigidbody => _rigidbody;
[SerializeField, Optional]
private Transform _grabAnchorTransform;
[SerializeField, Interface(typeof(IVelocityCalculator)), Optional]
private MonoBehaviour _velocityCalculator;
public IVelocityCalculator VelocityCalculator { get; set; }
private Collider[] _colliders;
public float BestInteractableWeight { get; private set; } = float.MaxValue;
public Vector3 GrabPosition => _grabAnchorTransform.position;
public Quaternion GrabRotation => _grabAnchorTransform.rotation;
protected override void Awake()
{
base.Awake();
VelocityCalculator = _velocityCalculator as IVelocityCalculator;
}
protected override void Start()
{
Assert.IsNotNull(_targetTransform);
Assert.IsNotNull(Rigidbody);
if (_grabAnchorTransform == null)
{
_grabAnchorTransform = _targetTransform;
}
Assert.IsNotNull(Rigidbody);
_colliders = Rigidbody.GetComponentsInChildren<Collider>();
Assert.IsTrue(_colliders.Length > 0,
"The associated Rigidbody must have at least one Collider.");
foreach (Collider collider in _colliders)
{
Assert.IsTrue(collider.isTrigger,
"Associated Colliders must be marked as Triggers.");
}
if (_velocityCalculator != null)
{
Assert.IsNotNull(VelocityCalculator);
}
}
protected override void DoEveryUpdate()
{
base.DoEveryUpdate();
transform.position = _targetTransform.position;
transform.rotation = _targetTransform.rotation;
// Any collider on the Interactor may need updating for
// future collision checks this frame
Physics.SyncTransforms();
}
protected override GrabInteractable ComputeCandidate()
{
GrabInteractable closestInteractable = null;
float bestWeight = float.MinValue;
float weight = bestWeight;
IEnumerable<GrabInteractable> interactables = GrabInteractable.Registry.List(this);
foreach (GrabInteractable interactable in interactables)
{
Collider[] colliders = interactable.Colliders;
foreach (Collider collider in colliders)
{
if (Collisions.IsPointWithinCollider(Rigidbody.transform.position, collider))
{
// Points within a collider are always weighted better than those outside
float sqrDistanceFromCenter =
(Rigidbody.transform.position - collider.bounds.center).magnitude;
weight = float.MaxValue - sqrDistanceFromCenter;
}
else
{
var position = Rigidbody.transform.position;
Vector3 closestPointOnInteractable = collider.ClosestPoint(position);
weight = -1f * (position - closestPointOnInteractable).magnitude;
}
if (weight > bestWeight)
{
bestWeight = weight;
closestInteractable = interactable;
}
}
}
BestInteractableWeight = bestWeight;
return closestInteractable;
}
protected override void InteractableUnselected(GrabInteractable interactable)
{
base.InteractableUnselected(interactable);
ReleaseVelocityInformation throwVelocity = VelocityCalculator != null ?
VelocityCalculator.CalculateThrowVelocity(interactable.transform) :
new ReleaseVelocityInformation(Vector3.zero, Vector3.zero, Vector3.zero);
interactable.ApplyVelocities(throwVelocity.LinearVelocity, throwVelocity.AngularVelocity);
}
protected override void DoSelectUpdate(GrabInteractable interactable)
{
base.DoSelectUpdate();
if (interactable == null)
{
return;
}
if (interactable.ReleaseDistance > 0.0f)
{
float closestSqrDist = float.MaxValue;
Collider[] colliders = interactable.Colliders;
foreach (Collider collider in colliders)
{
float sqrDistanceFromCenter =
(collider.bounds.center - Rigidbody.transform.position).sqrMagnitude;
closestSqrDist = Mathf.Min(closestSqrDist, sqrDistanceFromCenter);
}
float sqrReleaseDistance = interactable.ReleaseDistance * interactable.ReleaseDistance;
if (closestSqrDist > sqrReleaseDistance)
{
ShouldUnselect = true;
}
}
}
#region Inject
public void InjectAllGrabInteractor(Transform targetTransform, Rigidbody rigidbody)
{
InjectTargetTransform(targetTransform);
InjectRigidbodyRef(rigidbody);
}
public void InjectTargetTransform(Transform targetTransform)
{
_targetTransform = targetTransform;
}
public void InjectRigidbodyRef(Rigidbody rigidbody)
{
_rigidbody = rigidbody;
}
public void InjectOptionalGrabAnchorTransform(Transform grabAnchorTransform)
{
_grabAnchorTransform = grabAnchorTransform;
}
public void InjectOptionalVelocityCalculator(IVelocityCalculator velocityCalculator)
{
_velocityCalculator = velocityCalculator as MonoBehaviour;
VelocityCalculator = velocityCalculator;
}
#endregion
}
}

View File

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

View File

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

View File

@@ -0,0 +1,180 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using System.Collections.Generic;
using UnityEngine;
namespace Oculus.Interaction.HandPosing
{
/// <summary>
/// Utility class used by the grab interactors to find the best matching
/// pose from a provided list of HandGrabPoints in an object.
/// </summary>
public class GrabPointsPoseFinder
{
private List<HandGrabPoint> _handGrabPoints;
private Transform _fallbackPoint;
private InterpolationCache _interpolationCache = new InterpolationCache();
public GrabPointsPoseFinder(List<HandGrabPoint> handGrabPoints, Transform fallbackPoint = null)
{
_handGrabPoints = handGrabPoints;
_fallbackPoint = fallbackPoint;
}
public bool UsesHandPose()
{
return _handGrabPoints.Count > 0 && _handGrabPoints[0].HandPose != null;
}
/// <summary>
/// Finds the best valid hand-pose at this HandGrabInteractable.
/// Remember that a HandGrabPoint can actually have a whole surface the user can snap to.
/// </summary>
/// <param name="userPose">Pose to compare to the snap point in world coordinates.</param>
/// <param name="handScale">The scale of the tracked hand.</param>
/// <param name="handedness">The handedness of the tracked hand.</param>
/// <param name="bestHandPose">The most similar valid HandPose at this HandGrabInteractable.</param>
/// <param name="bestSnapPoint">The point of the valid snap.</param>
/// <param name="scoringModifier">Parameters indicating how to score the different poses.</param>
/// <param name="usesHandPose">True if the resultHandPose was populated.</param>
/// <param name="score">The score of the best pose found.</param>
/// <returns>True if a good pose was found</returns>
public bool FindBestPose(Pose userPose, float handScale, Handedness handedness,
ref HandPose bestHandPose, ref Pose bestSnapPoint, in PoseMeasureParameters scoringModifier, out bool usesHandPose, out float score)
{
if (_handGrabPoints.Count == 1)
{
return _handGrabPoints[0].CalculateBestPose(userPose, handedness,
ref bestHandPose, ref bestSnapPoint, scoringModifier, out usesHandPose, out score);
}
else if (_handGrabPoints.Count > 1)
{
return CalculateBestScaleInterpolatedPose(userPose, handedness, handScale,
ref bestHandPose, ref bestSnapPoint, scoringModifier, out usesHandPose, out score);
}
else if (_fallbackPoint != null)
{
usesHandPose = false;
score = PoseUtils.Similarity(userPose, _fallbackPoint.GetPose(), scoringModifier);
return true;
}
else
{
usesHandPose = false;
score = float.NaN;
return false;
}
}
private bool CalculateBestScaleInterpolatedPose(Pose userPose, Handedness handedness, float handScale,
ref HandPose result, ref Pose snapPoint, in PoseMeasureParameters scoringModifier, out bool usesHandPose, out float score)
{
usesHandPose = false;
score = float.NaN;
FindInterpolationRange(handScale, _handGrabPoints, out HandGrabPoint under, out HandGrabPoint over, out float t);
bool underFound = under.CalculateBestPose(userPose, handedness,
ref _interpolationCache.underHandPose, ref _interpolationCache.underSnapPoint, scoringModifier,
out bool underPoseWritten, out float underScore);
bool overFound = over.CalculateBestPose(userPose, handedness,
ref _interpolationCache.overHandPose, ref _interpolationCache.overSnapPoint, scoringModifier,
out bool overPoseWritten, out float overScore);
if (underPoseWritten && overPoseWritten)
{
usesHandPose = true;
result.CopyFrom(_interpolationCache.underHandPose);
HandPose.Lerp(_interpolationCache.underHandPose, _interpolationCache.overHandPose, t, ref result);
PoseUtils.Lerp(_interpolationCache.underSnapPoint, _interpolationCache.overSnapPoint, t, ref snapPoint);
}
else if (underPoseWritten)
{
usesHandPose = true;
result.CopyFrom(_interpolationCache.underHandPose);
snapPoint.CopyFrom(_interpolationCache.underSnapPoint);
}
else if (overPoseWritten)
{
usesHandPose = true;
result.CopyFrom(_interpolationCache.overHandPose);
snapPoint.CopyFrom(_interpolationCache.overSnapPoint);
}
if (underFound && overFound)
{
score = Mathf.Lerp(underScore, overScore, t);
return true;
}
if (underFound)
{
score = underScore;
return true;
}
if (overFound)
{
score = overScore;
return true;
}
return false;
}
/// <summary>
/// Finds the two nearest HandGrabPoints to interpolate from given a scale.
/// The result can require an unclamped interpolation (t can be bigger than 1 or smaller than 0).
/// </summary>
/// <param name="scale">The user scale</param>
/// <param name="grabPoints">The list of grabpoints to interpolate from</param>
/// <param name="from">The HandGrabInteractable with nearest scale recorder that is smaller than the provided one</param>
/// <param name="to">The HandGrabInteractable with nearest scale recorder that is bigger than the provided one</param>
/// <param name="t">The progress between from and to variables at which the desired scale resides</param>
/// <returns>The HandGrabPoint near under and over the scale, and the interpolation factor between them.</returns>
public static void FindInterpolationRange(float scale, List<HandGrabPoint> grabPoints, out HandGrabPoint from, out HandGrabPoint to, out float t)
{
from = grabPoints[0];
to = grabPoints[1];
for (int i = 2; i < grabPoints.Count; i++)
{
HandGrabPoint point = grabPoints[i];
if (point.Scale <= scale
&& point.Scale > from.Scale)
{
from = point;
}
else if (point.Scale >= scale
&& point.Scale < to.Scale)
{
to = point;
}
}
t = (scale - from.Scale) / (to.Scale - from.Scale);
}
private class InterpolationCache
{
public HandPose underHandPose = new HandPose();
public HandPose overHandPose = new HandPose();
public Pose underSnapPoint = new Pose();
public Pose overSnapPoint = new Pose();
}
}
}

View File

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

View File

@@ -0,0 +1,358 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Grab;
using Oculus.Interaction.GrabAPI;
using Oculus.Interaction.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction.HandPosing
{
/// <summary>
/// Serializable data-only version of the HandGrabInteractable so it can be stored when they
/// are generated at Play-Mode (where Hand-tracking works).
/// </summary>
[Serializable]
public struct HandGrabInteractableData
{
public List<HandGrabPointData> points;
public SnapType snapType;
public GrabTypeFlags grabType;
public float travelSpeed;
public PoseMeasureParameters scoringModifier;
}
/// <summary>
/// A HandGrabInteractable indicates the properties about how a hand can snap to an object.
/// The most important is the position/rotation and finger rotations for the hand,
/// but it can also contain extra information like a valid holding surface (instead of just
/// a single point) or a visual representation (using a hand-ghost)
/// </summary>
[Serializable]
public class HandGrabInteractable : Interactable<HandGrabInteractor, HandGrabInteractable>,
IPointable, ISnappable, IRigidbodyRef, IHandGrabInteractable
{
[Header("Grab")]
/// <summary>
/// The transform of the object this HandGrabInteractable refers to.
/// Typically the parent.
/// </summary>
[SerializeField]
private Transform _relativeTo;
[SerializeField]
private Rigidbody _rigidbody;
public Rigidbody Rigidbody => _rigidbody;
[SerializeField, Optional]
private float _releaseDistance = 0f;
[SerializeField, Optional]
private PhysicsTransformable _physicsObject = null;
[SerializeField]
private PoseMeasureParameters _scoringModifier = new PoseMeasureParameters(0.1f, 0f);
[Space]
[SerializeField]
private GrabTypeFlags _supportedGrabTypes = GrabTypeFlags.All;
[SerializeField]
private GrabbingRule _pinchGrabRules = GrabbingRule.DefaultPinchRule;
[SerializeField]
private GrabbingRule _palmGrabRules = GrabbingRule.DefaultPalmRule;
[Header("Snap")]
/// <summary>
/// How the snap will occur.
/// For example the hand can artificially move to perfectly wrap the object, or the object can move to align with the hand.
/// </summary>
[Tooltip("How the snap will occur, for example the hand can artificially move to perfectly wrap the object, or the object can move to align with the hand")]
[SerializeField]
private SnapType _snapType;
/// <summary>
/// When attracting the object, indicates how many seconds it will take for the object to realign with the hand after a grab
/// </summary>
[Tooltip("When attracting the object, indicates the speed (in m/s) for the object to realign with the hand after a grab.")]
[SerializeField]
private float _travelSpeed = 1f;
[SerializeField, Optional]
private List<HandGrabPoint> _handGrabPoints = new List<HandGrabPoint>();
/// <summary>
/// General getter for the transform of the object this interactable refers to.
/// </summary>
public Transform RelativeTo => _relativeTo != null ? _relativeTo : this.transform.parent;
/// <summary>
/// General getter indicating how the hand and object will align for the grab.
/// </summary>
public SnapType SnapType => _snapType;
public GrabTypeFlags SupportedGrabTypes => _supportedGrabTypes;
public GrabbingRule PinchGrabRules => _pinchGrabRules;
public GrabbingRule PalmGrabRules => _palmGrabRules;
public List<HandGrabPoint> GrabPoints => _handGrabPoints;
public Collider[] Colliders { get; private set; }
public float ReleaseDistance => _releaseDistance;
public event Action<PointerArgs> OnPointerEvent = delegate { };
private GrabPointsPoseFinder _grabPointsPoseFinder;
private PointableDelegate<HandGrabInteractor> _pointableDelegate;
private static CollisionInteractionRegistry<HandGrabInteractor, HandGrabInteractable> _registry = null;
protected bool _started = false;
#region editor events
protected virtual void Reset()
{
_relativeTo = this.transform.parent;
}
#endregion
protected virtual void Awake()
{
if (_registry == null)
{
_registry = new CollisionInteractionRegistry<HandGrabInteractor, HandGrabInteractable>();
SetRegistry(_registry);
}
}
protected virtual void Start()
{
this.BeginStart(ref _started);
Assert.IsNotNull(Rigidbody);
Colliders = Rigidbody.GetComponentsInChildren<Collider>();
Assert.IsTrue(Colliders.Length > 0,
"The associated Rigidbody must have at least one Collider.");
_grabPointsPoseFinder = new GrabPointsPoseFinder(_handGrabPoints, this.transform);
_pointableDelegate = new PointableDelegate<HandGrabInteractor>(this, ComputePointer);
this.EndStart(ref _started);
}
private void ComputePointer(HandGrabInteractor interactor, out Vector3 position, out Quaternion rotation)
{
position = interactor.GrabPose.position;
rotation = interactor.GrabPose.rotation;
}
protected override void OnEnable()
{
base.OnEnable();
if (_started)
{
_pointableDelegate.OnPointerEvent += InvokeOnPointerEvent;
}
}
protected override void OnDisable()
{
if (_started)
{
_pointableDelegate.OnPointerEvent -= InvokeOnPointerEvent;
}
base.OnDisable();
}
private void InvokeOnPointerEvent(PointerArgs args)
{
OnPointerEvent.Invoke(args);
}
protected virtual void OnDestroy()
{
_pointableDelegate = null;
}
#region pose snapping
public bool CalculateBestPose(Pose userPose, float handScale, Handedness handedness,
ref HandPose result, ref Pose snapPoint, out bool usesHandPose, out float score)
{
return _grabPointsPoseFinder.FindBestPose(userPose, handScale, handedness,
ref result, ref snapPoint, _scoringModifier, out usesHandPose, out score);
}
public bool UsesHandPose()
{
return _grabPointsPoseFinder.UsesHandPose();
}
#endregion
#region generation
/// <summary>
/// Creates a new HandGrabInteractable under the given object
/// </summary>
/// <param name="parent">The relative object for the interactable</param>
/// <returns>An non-populated HandGrabInteractable</returns>
public static HandGrabInteractable Create(Transform parent)
{
GameObject go = new GameObject("HandGrabInteractable");
go.transform.SetParent(parent, false);
HandGrabInteractable record = go.AddComponent<HandGrabInteractable>();
record._relativeTo = parent;
return record;
}
public HandGrabPoint CreatePoint()
{
GameObject go = this.gameObject;
if (this.TryGetComponent(out HandGrabPoint point))
{
go = new GameObject("HandGrab Point");
go.transform.SetParent(this.transform, false);
}
HandGrabPoint record = go.AddComponent<HandGrabPoint>();
return record;
}
#endregion
#region dataSave
/// <summary>
/// Serializes the data of the HandGrabInteractable so it can be stored
/// </summary>
/// <returns>The struct data to recreate the interactable</returns>
public HandGrabInteractableData SaveData()
{
return new HandGrabInteractableData()
{
snapType = _snapType,
travelSpeed = _travelSpeed,
points = _handGrabPoints.Select(p => p.SaveData()).ToList(),
scoringModifier = _scoringModifier
};
}
/// <summary>
/// Populates the HandGrabInteractable with the serialized data version
/// </summary>
/// <param name="data">The serialized data for the HandGrabInteractable.</param>
public void LoadData(HandGrabInteractableData data)
{
_snapType = data.snapType;
_travelSpeed = data.travelSpeed;
_scoringModifier = data.scoringModifier;
foreach (HandGrabPointData pointData in data.points)
{
LoadPoint(pointData);
}
}
public HandGrabPoint LoadPoint(HandGrabPointData pointData)
{
HandGrabPoint point = CreatePoint();
point.LoadData(pointData, this.RelativeTo);
_handGrabPoints.Add(point);
return point;
}
#endregion
public void ApplyVelocities(Vector3 linearVelocity, Vector3 angularVelocity)
{
if (_physicsObject == null)
{
return;
}
_physicsObject.ApplyVelocities(linearVelocity, angularVelocity);
}
public PoseTravelData CreateTravelData(in Pose from, in Pose to)
{
return new PoseTravelData(from, to, _travelSpeed);
}
#region Inject
public void InjectAllHandGrabInteractable(Transform relativeTo, Rigidbody rigidbody,
GrabTypeFlags supportedGrabTypes, GrabbingRule pinchGrabRules, GrabbingRule palmGrabRules,
float travelSpeed, SnapType snapType)
{
InjectRelativeTo(relativeTo);
InjectRigidbody(rigidbody);
InjectTravelSpeed(travelSpeed);
InjectSnapType(snapType);
InjectSupportedGrabTypes(supportedGrabTypes);
InjectPinchGrabRules(pinchGrabRules);
InjectPalmGrabRules(palmGrabRules);
}
public void InjectRelativeTo(Transform relativeTo)
{
_relativeTo = relativeTo;
}
public void InjectRigidbody(Rigidbody rigidbody)
{
_rigidbody = rigidbody;
}
public void InjectOptionalReleaseDistance(float releaseDistance)
{
_releaseDistance = releaseDistance;
}
public void InjectSupportedGrabTypes(GrabTypeFlags supportedGrabTypes)
{
_supportedGrabTypes = supportedGrabTypes;
}
public void InjectPinchGrabRules(GrabbingRule pinchGrabRules)
{
_pinchGrabRules = pinchGrabRules;
}
public void InjectPalmGrabRules(GrabbingRule palmGrabRules)
{
_palmGrabRules = palmGrabRules;
}
public void InjectOptionalPhysicsObject(PhysicsTransformable physicsObject)
{
_physicsObject = physicsObject;
}
public void InjectSnapType(SnapType snapType)
{
_snapType = snapType;
}
public void InjectTravelSpeed(float travelSpeed)
{
_travelSpeed = travelSpeed;
}
public void InjectOptionalHandGrabPoints(List<HandGrabPoint> handGrabPoints)
{
_handGrabPoints = handGrabPoints;
}
#endregion
#region editor
protected virtual void OnDrawGizmos()
{
Gizmos.DrawIcon(this.transform.position, "sv_icon_dot10_pix16_gizmo");
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,56 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections.Generic;
using UnityEngine;
namespace Oculus.Interaction.HandPosing
{
/// <summary>
/// A collection of HandGrabInteractable Data, to be used to store the information of several HandGrabInteractable
/// so it survives Play-Mode Edit-Mode cycles.
///
/// Use this to store information once in Play-Mode (where Hand-tracking can be used)
/// and then restore it forever at Edit-time.
/// </summary>
[CreateAssetMenu(menuName = "Oculus/Interaction/SDK/Pose Authoring/HandGrabInteractable Data Collection")]
public class HandGrabInteractableDataCollection : ScriptableObject
{
/// <summary>
/// The data-only version of the HandGrabInteractable to be restored.
/// Do not modify this manually here unless you are sure of what you are doing, instead
/// reload it at Edit-Mode and use the provided tools at the HandGrabInteractable.
/// </summary>
[SerializeField]
[Tooltip("Do not modify this manually unless you are sure! Instead load the HandGrabInteractable and use the tools provided.")]
private List<HandGrabInteractableData> _interactablesData;
/// <summary>
/// General getter for the data-only version of the HandGrabInteractable to be restored.
/// </summary>
public List<HandGrabInteractableData> InteractablesData => _interactablesData;
/// <summary>
/// Register all the data into the Asset Database so it survives the Play-Mode shutdown.
/// </summary>
/// <param name="interactablesData"></param>
public void StoreInteractables(List<HandGrabInteractableData> interactablesData)
{
_interactablesData = interactablesData;
#if UNITY_EDITOR
UnityEditor.AssetDatabase.Refresh();
UnityEditor.EditorUtility.SetDirty(this);
UnityEditor.AssetDatabase.SaveAssets();
#endif
}
}
}

View File

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

View File

@@ -0,0 +1,61 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using UnityEngine;
namespace Oculus.Interaction.HandPosing
{
/// <summary>
/// Utilities class for HandGrab and DistanceHandGrab interaction common methods.
/// </summary>
public static class HandGrabInteractionUtilities
{
/// <summary>
/// Forces the release of an interactable when
/// the distance to the grabber is big enough.
/// </summary>
/// <param name="snappable">The object to check for release</param>
public static bool CheckReleaseDistance(ISnappable snappable, Vector3 centerPoint, float scaleModifier)
{
if (snappable == null
|| snappable.ReleaseDistance <= 0f)
{
return false;
}
float closestSqrDist = float.MaxValue;
Collider[] colliders = snappable.Colliders;
foreach (Collider collider in colliders)
{
float sqrDistanceFromCenter = (collider.bounds.center - centerPoint).sqrMagnitude;
closestSqrDist = Mathf.Min(closestSqrDist, sqrDistanceFromCenter);
}
float scaledDistance = snappable.ReleaseDistance * scaleModifier;
float sqrReleaseDistance = scaledDistance * scaledDistance;
return (closestSqrDist > sqrReleaseDistance);
}
/// <summary>
/// Calculates the normalized snapback time for a given grab event.
/// It indicates how close in time we are from the given event.
/// </summary>
/// <param name="grabEventTime">Time at which the grab event initiated.</param>
/// <param name="maxTime">Total time desired for the event. Used for normalization.</param>
/// <returns>1 at the grab moment, 0 after _snapbackTime seconds or more</returns>
public static float GetNormalizedEventTime(float grabEventTime, float maxTime)
{
return Mathf.Clamp01((Time.realtimeSinceStartup - grabEventTime) / maxTime);
}
}
}

View File

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

View File

@@ -0,0 +1,455 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Grab;
using Oculus.Interaction.GrabAPI;
using Oculus.Interaction.Input;
using Oculus.Interaction.Throw;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction.HandPosing
{
using SnapAddress = SnapAddress<HandGrabInteractable>;
/// <summary>
/// The HandGrabInteractor allows grabbing objects while having the hands snap to them
/// adopting a previously authored HandPose.
/// There are different snapping techniques available, and when None is selected it will
/// behave as a normal GrabInteractor.
/// </summary>
public class HandGrabInteractor : Interactor<HandGrabInteractor, HandGrabInteractable>
, ISnapper, IRigidbodyRef, IHandGrabInteractor
{
[SerializeField, Interface(typeof(IHand))]
private MonoBehaviour _hand;
private IHand Hand { get; set; }
[SerializeField]
private Rigidbody _rigidbody;
[SerializeField]
private HandGrabAPI _handGrabApi;
[SerializeField]
private GrabTypeFlags _supportedGrabTypes = GrabTypeFlags.All;
[SerializeField]
private HandWristOffset _gripPoint;
[SerializeField, Optional]
private Transform _pinchPoint;
[FormerlySerializedAs("_optionalVelocityProviderMono")]
[SerializeField, Interface(typeof(IVelocityCalculator)), Optional]
private MonoBehaviour _velocityCalculator;
public IVelocityCalculator VelocityCalculator { get; set; }
private SnapAddress _currentSnap = new SnapAddress();
private HandPose _cachedBestHandPose = new HandPose();
private Pose _cachedBestSnapPoint = new Pose();
protected bool _isTravelling;
private PoseTravelData _travelInformation;
private Pose _grabPose;
private Pose _wristToGripOffset;
private Pose _gripToPinchOffset;
private Pose _trackedGripPose;
private Grab.HandGrabInteractableData _lastInteractableData = new Grab.HandGrabInteractableData();
public Pose GrabPose => _grabPose;
#region IHandGrabInteractor
public HandGrabAPI HandGrabApi => _handGrabApi;
public GrabTypeFlags SupportedGrabTypes => _supportedGrabTypes;
public IHandGrabInteractable TargetInteractable => Interactable;
#endregion
#region ISnapper
public virtual bool IsSnapping => HasSelectedInteractable && !_isTravelling;
public float SnapStrength { get; private set; }
public HandFingerFlags SnappingFingers()
{
return HandGrab.GrabbingFingers(this, SelectedInteractable);
}
public Pose WristToGripOffset => _wristToGripOffset;
public ISnapData SnapData { get; private set; }
public System.Action<ISnapper> WhenSnapStarted { get; set; } = delegate { };
public System.Action<ISnapper> WhenSnapEnded { get; set; } = delegate { };
#endregion
#region IRigidbodyRef
public Rigidbody Rigidbody => _rigidbody;
#endregion
protected bool _started = false;
#region editor events
protected virtual void Reset()
{
_hand = this.GetComponentInParent<IHand>() as MonoBehaviour;
_handGrabApi = this.GetComponentInParent<HandGrabAPI>();
}
#endregion
protected override void Awake()
{
base.Awake();
Hand = _hand as IHand;
VelocityCalculator = _velocityCalculator as IVelocityCalculator;
}
protected override void Start()
{
this.BeginStart(ref _started, base.Start);
Assert.IsNotNull(Rigidbody);
Collider[] colliders = Rigidbody.GetComponentsInChildren<Collider>();
Assert.IsTrue(colliders.Length > 0,
"The associated Rigidbody must have at least one Collider.");
foreach (Collider collider in colliders)
{
Assert.IsTrue(collider.isTrigger,
"Associated Colliders must be marked as Triggers.");
}
Assert.IsNotNull(_handGrabApi);
Assert.IsNotNull(Hand);
if (_velocityCalculator != null)
{
Assert.IsNotNull(VelocityCalculator);
}
this.EndStart(ref _started);
}
#region life cycle
/// <summary>
/// During the update event, move the current interactor (containing also the
/// trigger for detecting nearby interactableS) to the tracked position of the grip.
///
/// That is the tracked wrist plus a pregenerated position and rotation offset.
/// </summary>
protected override void DoEveryUpdate()
{
base.DoEveryUpdate();
_gripPoint.GetWorldPose(ref _trackedGripPose);
_gripPoint.GetOffset(ref _wristToGripOffset);
this.transform.SetPose(_trackedGripPose);
}
/// <summary>
/// Each call while the hand is selecting/grabbing an interactable, it moves the item to the
/// new position while also attracting it towards the hand if the snapping mode requires it.
///
/// In some cases the parameter can be null, for example if the selection was interrupted
/// by another hand grabbing the object. In those cases it will come out of the release
/// state once the grabbing gesture properly finishes.
/// </summary>
/// <param name="interactable">The selected item</param>
protected override void DoSelectUpdate(HandGrabInteractable interactable)
{
base.DoSelectUpdate(interactable);
_isTravelling = false;
if (interactable == null)
{
_currentSnap.Clear();
}
Pose grabbingPoint = GrabbingPoint(_currentSnap);
if (CanTravel(_currentSnap))
{
_travelInformation.DestinationPose = grabbingPoint;
bool travelFinished = _travelInformation.CurrentTravelPose(ref _grabPose);
_isTravelling = !travelFinished;
}
else
{
_grabPose = grabbingPoint;
}
if (!_isTravelling
&& HandGrabInteractionUtilities.CheckReleaseDistance(interactable,
grabbingPoint.position, Hand.Scale))
{
ShouldUnselect = true;
}
else
{
if (interactable != null)
{
HandGrab.StoreGrabData(this, interactable, ref _lastInteractableData);
ShouldUnselect = HandGrab.ComputeShouldUnselect(this, interactable);
}
else
{
ShouldUnselect = true;
}
}
}
protected virtual bool CanTravel(SnapAddress snap)
{
return !SnapAddress.IsNullOrInvalid(snap)
&& (snap.SnapType == SnapType.HandToObjectAndReturn
|| snap.SnapType == SnapType.ObjectToHand);
}
/// <summary>
/// Each call while the interactor is hovering, it checks whether there is an interaction
/// being hovered and sets the target snapping address to it. In the HandToObject snapping
/// behaviors this is relevant as the hand can approach the object progressively even before
/// a true grab starts.
/// </summary>
protected override void DoHoverUpdate()
{
base.DoHoverUpdate();
if (_currentSnap.IsValidAddress)
{
SnapStrength = HandGrab.ComputeHoverStrength(this, Interactable,
out GrabTypeFlags hoverGrabTypes);
SnapData = _currentSnap;
}
else
{
SnapStrength = 0f;
SnapData = null;
}
if (Interactable != null)
{
ShouldSelect = HandGrab.ComputeShouldSelect(this, Interactable,
out GrabTypeFlags selectingGrabTypes);
}
}
public override void Select()
{
PrepareStartGrab(_currentSnap);
base.Select();
}
/// <summary>
/// When a new interactable is selected, start the grab at the ideal point. When snapping is
/// involved that can be a point in the interactable offset from the hand
/// which will be stored to progressively reduced it in the next updates,
/// effectively attracting the object towards the hand.
/// When no snapping is involved the point will be the grip point of the hand directly.
/// Note: ideally this code would be in InteractableSelected but it needs
/// to be called before the object is marked as active.
/// </summary>
/// <param name="snap">The selected Snap Data </param>
private void PrepareStartGrab(SnapAddress snap)
{
if (SnapAddress.IsNullOrInvalid(snap))
{
return;
}
Pose grabbingPoint = _trackedGripPose;
Pose pinchPose = _pinchPoint.GetPose();
_gripToPinchOffset = PoseUtils.RelativeOffset(pinchPose, _trackedGripPose);
if (snap.SnappedToPinch)
{
grabbingPoint = pinchPose;
}
Pose originalGrabPose = snap.Interactable.RelativeTo.GlobalPose(snap.SnapPoint);
_travelInformation = snap.Interactable.CreateTravelData(originalGrabPose, grabbingPoint);
if (CanTravel(snap))
{
_grabPose.CopyFrom(originalGrabPose);
}
else
{
_grabPose = grabbingPoint;
}
}
/// <summary>
/// When releasing an active interactable, calculate the releasing point in similar
/// fashion to InteractableSelected
/// </summary>
/// <param name="interactable">The released interactable</param>
protected override void InteractableUnselected(HandGrabInteractable interactable)
{
base.InteractableUnselected(interactable);
if (interactable == null)
{
return;
}
ReleaseVelocityInformation throwVelocity = VelocityCalculator != null ?
VelocityCalculator.CalculateThrowVelocity(interactable.transform) :
new ReleaseVelocityInformation(Vector3.zero, Vector3.zero, Vector3.zero);
interactable.ApplyVelocities(throwVelocity.LinearVelocity, throwVelocity.AngularVelocity);
}
protected override void InteractableSet(HandGrabInteractable interactable)
{
base.InteractableSet(interactable);
WhenSnapStarted.Invoke(this);
}
protected override void InteractableUnset(HandGrabInteractable interactable)
{
base.InteractableUnset(interactable);
WhenSnapEnded.Invoke(this);
}
#endregion
#region grab detection
private bool CanSnapToPinchPoint(HandGrabInteractable interactable, GrabTypeFlags grabTypes)
{
return _pinchPoint != null
&& !interactable.UsesHandPose()
&& (grabTypes & GrabTypeFlags.Pinch) != 0;
}
private Pose GrabbingPoint(SnapAddress snapAddress)
{
if (snapAddress.SnappedToPinch)
{
return PoseUtils.Multiply(_trackedGripPose, _gripToPinchOffset);
}
else
{
return _trackedGripPose;
}
}
#endregion
/// <summary>
/// Compute the best interactable to snap to. In order to do it the method measures
/// the score from the current grip pose to the closes pose in the surfaces
/// of each one of the interactables in the registry.
/// Even though it returns the best interactable, it also saves the entire SnapAddress to
/// it in which the exact pose within the surface is already recorded to avoid recalculations
/// within the same frame.
/// </summary>
/// <returns>The best interactable to snap the hand to.</returns>
protected override HandGrabInteractable ComputeCandidate()
{
ComputeBestSnapAddress(ref _currentSnap);
return _currentSnap.Interactable;
}
protected virtual void ComputeBestSnapAddress(ref SnapAddress snapAddress)
{
IEnumerable<HandGrabInteractable> interactables = HandGrabInteractable.Registry.List(this);
float bestFingerScore = -1f;
float bestPoseScore = -1f;
foreach (HandGrabInteractable interactable in interactables)
{
float fingerScore = 1.0f;
if (!HandGrab.ComputeShouldSelect(this, interactable, out GrabTypeFlags selectingGrabTypes))
{
fingerScore = HandGrab.ComputeHoverStrength(this, interactable, out selectingGrabTypes);
}
if (fingerScore < bestFingerScore)
{
continue;
}
bool usePinchPoint = CanSnapToPinchPoint(interactable, selectingGrabTypes);
Pose grabPoint = usePinchPoint ? _pinchPoint.GetPose() : _trackedGripPose;
bool poseFound = interactable.CalculateBestPose(grabPoint, Hand.Scale, Hand.Handedness,
ref _cachedBestHandPose, ref _cachedBestSnapPoint,
out bool usesHandPose, out float poseScore);
if (!poseFound)
{
continue;
}
if (fingerScore > bestFingerScore
|| poseScore > bestPoseScore)
{
bestFingerScore = fingerScore;
bestPoseScore = poseScore;
HandPose handPose = usesHandPose ? _cachedBestHandPose : null;
snapAddress.Set(interactable, handPose, _cachedBestSnapPoint, usePinchPoint);
}
}
if (bestFingerScore < 0)
{
snapAddress.Clear();
}
}
#region Inject
public void InjectAllHandGrabInteractor(HandGrabAPI handGrabApi,
IHand hand, Rigidbody rigidbody, GrabTypeFlags supportedGrabTypes, HandWristOffset gripPoint)
{
InjectHandGrabApi(handGrabApi);
InjectHand(hand);
InjectRigidbody(rigidbody);
InjectSupportedGrabTypes(supportedGrabTypes);
InjectGripPoint(gripPoint);
}
public void InjectHandGrabApi(HandGrabAPI handGrabAPI)
{
_handGrabApi = handGrabAPI;
}
public void InjectHand(IHand hand)
{
_hand = hand as MonoBehaviour;
Hand = hand;
}
public void InjectRigidbody(Rigidbody rigidbody)
{
_rigidbody = rigidbody;
}
public void InjectSupportedGrabTypes(GrabTypeFlags supportedGrabTypes)
{
_supportedGrabTypes = supportedGrabTypes;
}
public void InjectGripPoint(HandWristOffset gripPoint)
{
_gripPoint = gripPoint;
}
public void InjectOptionalPinchPoint(Transform pinchPoint)
{
_pinchPoint = pinchPoint;
}
public void InjectOptionalVelocityCalculator(IVelocityCalculator velocityCalculator)
{
_velocityCalculator = velocityCalculator as MonoBehaviour;
VelocityCalculator = velocityCalculator;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,197 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.HandPosing.SnapSurfaces;
using Oculus.Interaction.Input;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.Assertions;
namespace Oculus.Interaction.HandPosing
{
[System.Serializable]
public struct HandGrabPointData
{
public Pose gripPose;
public HandPose handPose;
public float scale;
}
/// <summary>
/// Used in conjunction with HandGrabInteractable or DistanceHandGrabInteractable,
/// the HandGrabPoint defines the local point in an object to which the grip point
/// of the hand should align. It can also contain information about the final pose
/// of the hand for perfect alignment as well as a surface that indicates the valid
/// positions for the point.
/// </summary>
public class HandGrabPoint : MonoBehaviour
{
[SerializeField]
private Transform _relativeTo;
[SerializeField, Optional, Interface(typeof(ISnapSurface))]
private MonoBehaviour _surface = null;
private ISnapSurface _snapSurface;
public ISnapSurface SnapSurface
{
get => _snapSurface ?? _surface as ISnapSurface;
private set
{
_snapSurface = value;
}
}
[SerializeField]
private bool _usesHandPose = true;
[SerializeField, Optional]
[HideInInspector]
private HandPose _handPose = new HandPose();
public HandPose HandPose => _usesHandPose ? _handPose : null;
public float Scale => this.transform.lossyScale.x;
public Transform RelativeTo { get => _relativeTo; set => _relativeTo = value; }
public Pose RelativeGrip => RelativeTo.RelativeOffset(this.transform);
#region editor events
protected virtual void Reset()
{
_relativeTo = this.GetComponentInParent<HandGrabInteractable>()?.RelativeTo;
}
#endregion
protected virtual void Start()
{
Assert.IsNotNull(_relativeTo);
}
/// <summary>
/// Applies the given position/rotation to the HandGrabPoint
/// </summary>
/// <param name="handPose">Relative hand position/rotation.</param>
/// <param name="relativeTo">Reference coordinates for the pose.</param>
public void SetPose(HandPose handPose, in Pose gripPoint, Transform relativeTo)
{
_handPose = new HandPose(handPose);
_relativeTo = relativeTo;
this.transform.SetPose(relativeTo.GlobalPose(gripPoint));
}
public HandGrabPointData SaveData()
{
HandGrabPointData data = new HandGrabPointData()
{
handPose = new HandPose(_handPose),
scale = Scale,
gripPose = _relativeTo.RelativeOffset(this.transform)
};
return data;
}
public void LoadData(HandGrabPointData data, Transform relativeTo)
{
_relativeTo = relativeTo;
this.transform.localScale = Vector3.one * data.scale;
this.transform.SetPose(_relativeTo.GlobalPose(data.gripPose));
if (data.handPose != null)
{
_handPose.CopyFrom(data.handPose);
}
}
public virtual bool CalculateBestPose(Pose userPose, Handedness handedness,
ref HandPose bestHandPose, ref Pose bestSnapPoint, in PoseMeasureParameters scoringModifier,
out bool usesHandPose, out float score)
{
usesHandPose = false;
if (HandPose != null && HandPose.Handedness != handedness)
{
score = float.NaN;
return false;
}
score = CompareNearPoses(userPose, ref bestSnapPoint, scoringModifier);
if (HandPose != null)
{
usesHandPose = true;
bestHandPose.CopyFrom(HandPose);
}
return true;
}
/// <summary>
/// Finds the most similar pose at this HandGrabInteractable to the user hand pose
/// </summary>
/// <param name="worldPoint">The user current hand pose.</param>
/// <param name="bestSnapPoint">The snap point hand pose within the surface (if any).</param>
/// <returns>The adjusted best pose at the surface.</returns>
private float CompareNearPoses(in Pose worldPoint, ref Pose bestSnapPoint, in PoseMeasureParameters scoringModifier)
{
Pose desired = worldPoint;
Pose snap = this.transform.GetPose();
float bestScore;
Pose bestPlace;
if (SnapSurface != null)
{
bestScore = SnapSurface.CalculateBestPoseAtSurface(desired, snap, out bestPlace, scoringModifier);
}
else
{
bestPlace = snap;
bestScore = PoseUtils.Similarity(desired, snap, scoringModifier);
}
_relativeTo.RelativeOffset(bestPlace, ref bestSnapPoint);
return bestScore;
}
#region Inject
public void InjectRelativeTo(Transform relativeTo)
{
_relativeTo = relativeTo;
}
public void InjectOptionalSurface(ISnapSurface surface)
{
_surface = surface as MonoBehaviour;
SnapSurface = surface;
}
public void InjectOptionalHandPose(HandPose handPose)
{
_handPose = handPose;
_usesHandPose = _handPose != null;
}
public void InjectAllHandGrabPoint(Transform relativeTo)
{
InjectRelativeTo(relativeTo);
}
#endregion
#region editor
protected virtual void OnDrawGizmos()
{
Gizmos.DrawIcon(this.transform.position, "sv_icon_dot2_sml");
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,144 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using UnityEngine;
using UnityEngine.Serialization;
namespace Oculus.Interaction.HandPosing
{
/// <summary>
/// Data for the pose of a hand for grabbing an object.
/// It contains not only the position of the hand and the fingers but
/// other relevant data to the pose like the scale or which
/// fingers are locked.
///
/// Even though this class is Serializable it is not a Component.
/// The HandPoseEditor class should be used
/// (in conjunction with the HandGrabInteractableEditor class)
/// to edit the values in the inspector.
/// </summary>
[System.Serializable]
public class HandPose
{
[SerializeField]
private Handedness _handedness;
[SerializeField]
private JointFreedom[] _fingersFreedom = FingersMetadata.DefaultFingersFreedom();
[SerializeField]
private Quaternion[] _jointRotations = new Quaternion[FingersMetadata.HAND_JOINT_IDS.Length];
/// <summary>
/// Handedness of the hand.
/// </summary>
public Handedness Handedness
{
get => _handedness;
set => _handedness = value;
}
/// <summary>
/// Collection of joints and their rotations in this hand.
/// It follows the FingersMetadata.HAND_JOINT_IDS convention
/// </summary>
public Quaternion[] JointRotations
{
get
{
if (_jointRotations == null
|| _jointRotations.Length == 0)
{
_jointRotations = new Quaternion[FingersMetadata.HAND_JOINT_IDS.Length];
}
return _jointRotations;
}
set
{
_jointRotations = value;
}
}
/// <summary>
/// Indicates which fingers can be locked, constrained or free in this
/// hand pose.
/// It follows the Hand.HandFinger order for the collection.
/// </summary>
public JointFreedom[] FingersFreedom
{
get
{
if (_fingersFreedom == null
|| _fingersFreedom.Length == 0)
{
_fingersFreedom = FingersMetadata.DefaultFingersFreedom();
}
return _fingersFreedom;
}
}
public HandPose()
{
}
public HandPose(HandPose other)
{
this.CopyFrom(other);
}
/// <summary>
/// Copies the values over to the hand pose
/// without requiring any new allocations.
///
/// This is thanks to the fact of the fingers freedom
/// and joints rotations collections being always
/// fixed size and order.
/// </summary>
/// <param name="from">The hand pose to copy the values from</param>
public void CopyFrom(HandPose from)
{
this.Handedness = from.Handedness;
for (int i = 0; i < Constants.NUM_FINGERS; i++)
{
this.FingersFreedom[i] = from.FingersFreedom[i];
}
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; i++)
{
this.JointRotations[i] = from.JointRotations[i];
}
}
/// <summary>
/// Interpolates between two HandPoses, if they have the same handedness and joints.
/// </summary>
/// <param name="from">Base HandPose to interpolate from.</param>
/// <param name="to">Target HandPose to interpolate to.</param>
/// <param name="t">Interpolation factor, 0 for the base, 1 for the target.</param>
/// <param name="result">A HandPose positioned/rotated between the base and target.</param>
public static void Lerp(in HandPose from, in HandPose to, float t, ref HandPose result)
{
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; i++)
{
result.JointRotations[i] = Quaternion.SlerpUnclamped(from.JointRotations[i], to.JointRotations[i], t);
}
HandPose dominantPose = t <= 0.5f? from : to;
result.Handedness = dominantPose.Handedness;
for (int i = 0; i < Constants.NUM_FINGERS; i++)
{
result.FingersFreedom[i] = dominantPose.FingersFreedom[i];
}
}
}
}

View File

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

View File

@@ -0,0 +1,84 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using UnityEngine;
namespace Oculus.Interaction.HandPosing
{
/// <summary>
/// Defines the strategy for aligning the object to the hand.
/// The hand can go towards the object or vice-versa.
/// </summary>
public enum SnapType
{
/// <summary>
/// Move the object towards the hand after the user starts a grab
/// </summary>
ObjectToHand,
/// <summary>
/// Move the hand towards the object and stay
/// </summary>
HandToObject,
/// <summary>
/// Move the hand towards the object and return to the original position
/// </summary>
HandToObjectAndReturn,
/// <summary>
/// Keeps both object and hand at the same distance while grabbed
/// </summary>
None
}
/// <summary>
/// Interface for interactors that allow aligning to an object.
/// Contains information to drive the HandGrabVisual moving
/// the fingers and wrist.
/// </summary>
public interface ISnapper
{
bool IsSnapping { get; }
float SnapStrength { get; }
Pose WristToGripOffset { get; }
HandFingerFlags SnappingFingers();
ISnapData SnapData { get; }
System.Action<ISnapper> WhenSnapStarted { get; set; }
System.Action<ISnapper> WhenSnapEnded { get; set; }
}
/// <summary>
/// Interface for interactables that allow
/// being visually aligned to by an interactor.
/// </summary>
public interface ISnappable
{
Transform RelativeTo { get; }
SnapType SnapType { get; }
Collider[] Colliders { get; }
float ReleaseDistance { get; }
bool UsesHandPose();
}
/// <summary>
/// Interface containing specific data regarding
/// the pose of the hand as it aligns to an ISnappable
/// </summary>
public interface ISnapData
{
SnapType SnapType { get; }
HandPose HandPose { get; }
Pose WorldSnapPose { get; }
}
}

View File

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

View File

@@ -0,0 +1,38 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using UnityEngine;
namespace Oculus.Interaction.HandPosing
{
[Serializable]
public struct PoseMeasureParameters
{
[SerializeField]
[Min(0f)]
private float _maxDistance;
[SerializeField]
[Range(0f, 1f)]
private float _positionRotationWeight;
public float MaxDistance => _maxDistance;
public float PositionRotationWeight => _positionRotationWeight;
public PoseMeasureParameters(float maxDistance, float positionRotationWeight)
{
_maxDistance = maxDistance;
_positionRotationWeight = positionRotationWeight;
}
}
}

View File

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

View File

@@ -0,0 +1,59 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using UnityEngine;
namespace Oculus.Interaction.HandPosing
{
/// <summary>
/// Utility struct containing the information and calculations
/// to move a pose in space using an AnimationCurve at a given speed.
/// Unifies code in the HandGrab and DistanceHandGrab interactors.
/// </summary>
public struct PoseTravelData
{
private const float DEGREES_TO_PERCEIVED_METERS = 0.1f / 360f;
private float _startTime;
private float _totalTime;
private Pose _sourcePose;
private AnimationCurve _easingCurve;
public Pose DestinationPose { private get; set; }
public PoseTravelData(in Pose from, in Pose to, float speed, AnimationCurve curve = null)
{
_startTime = Time.realtimeSinceStartup;
_sourcePose = from;
_easingCurve = curve;
DestinationPose = to;
Pose grabOffset = PoseUtils.RelativeOffset(from, to);
float travelDistance = Mathf.Max(grabOffset.position.magnitude,
(grabOffset.rotation.eulerAngles * DEGREES_TO_PERCEIVED_METERS).magnitude);
_totalTime = travelDistance / speed;
}
public bool CurrentTravelPose(ref Pose currentTravelPose)
{
float animationProgress = HandGrabInteractionUtilities.GetNormalizedEventTime(_startTime, _totalTime);
bool isCompleted = animationProgress >= 1f;
if (_easingCurve != null)
{
animationProgress = _easingCurve.Evaluate(animationProgress);
}
PoseUtils.Lerp(_sourcePose, DestinationPose, animationProgress, ref currentTravelPose);
return isCompleted;
}
}
}

View File

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

View File

@@ -0,0 +1,64 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using UnityEngine;
namespace Oculus.Interaction.HandPosing
{
/// <summary>
/// All the relevant data needed for a snapping position.
/// This includes the HandGrabInteractor to (which can have a volume)
/// and the best Pose within that volume if any available
/// </summary>
public class SnapAddress<TSnappable> : ISnapData
where TSnappable : class, ISnappable
{
public TSnappable Interactable { get; set; }
public ref Pose SnapPoint => ref _snapPoint;
public bool IsValidAddress => Interactable != null;
public HandPose HandPose => _isHandPoseValid ? _handPose : null;
public Pose WorldSnapPose => Interactable.RelativeTo.GlobalPose(SnapPoint);
public SnapType SnapType => Interactable != null ? Interactable.SnapType : SnapType.None;
public bool SnappedToPinch { get; private set; }
private bool _isHandPoseValid = false;
private HandPose _handPose = new HandPose();
private Pose _snapPoint;
public void Set(TSnappable snapInteractable, HandPose pose, in Pose snapPoint, bool usedPinchPoint)
{
Interactable = snapInteractable;
SnappedToPinch = usedPinchPoint;
_snapPoint.CopyFrom(snapPoint);
_isHandPoseValid = pose != null;
if (_isHandPoseValid)
{
_handPose.CopyFrom(pose);
}
}
public void Clear()
{
Interactable = null;
SnappedToPinch = false;
_isHandPoseValid = false;
}
public static bool IsNullOrInvalid(SnapAddress<TSnappable> snapAddress)
{
return snapAddress == null || !snapAddress.IsValidAddress;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,158 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
public class PokeInteractable : Interactable<PokeInteractor, PokeInteractable>, IPointable
{
[SerializeField, Interface(typeof(IProximityField))]
private MonoBehaviour _proximityField;
public IProximityField ProximityField;
[SerializeField]
// The plane "forward" direction should be towards negative Z (to maintain parity with Canvas)
private Transform _triggerPlaneTransform;
public Transform TriggerPlaneTransform => _triggerPlaneTransform;
[SerializeField]
private float _maxDistance = 0.1f;
public float MaxDistance => _maxDistance;
[SerializeField]
private float _releaseDistance = 0.25f;
public float ReleaseDistance => _releaseDistance;
[SerializeField]
private float _horizontalDragThreshold = 0.0f;
public float HorizontalDragThreshold => _horizontalDragThreshold;
[SerializeField]
private float _verticalDragThreshold = 0.0f;
public float VerticalDragThreshold => _verticalDragThreshold;
[SerializeField, Optional]
private Collider _volumeMask = null;
public Collider VolumeMask { get => _volumeMask; }
public event Action<PointerArgs> OnPointerEvent = delegate { };
private PointableDelegate<PokeInteractor> _pointableDelegate;
protected bool _started = false;
protected virtual void Awake()
{
ProximityField = _proximityField as IProximityField;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
Assert.IsNotNull(_triggerPlaneTransform);
Assert.IsNotNull(ProximityField);
_pointableDelegate = new PointableDelegate<PokeInteractor>(this, ComputePointer);
this.EndStart(ref _started);
}
protected override void OnEnable()
{
base.OnEnable();
if (_started)
{
_pointableDelegate.OnPointerEvent += InvokePointerEvent;
}
}
protected override void OnDisable()
{
if (_started)
{
_pointableDelegate.OnPointerEvent -= InvokePointerEvent;
}
base.OnDisable();
}
public Vector3 ComputeClosestPoint(Vector3 point)
{
return ProximityField.ComputeClosestPoint(point);
}
private void ComputePointer(PokeInteractor pokeInteractor, out Vector3 position, out Quaternion rotation)
{
position = pokeInteractor.TouchPoint;
rotation = _triggerPlaneTransform.rotation;
}
private void InvokePointerEvent(PointerArgs args)
{
OnPointerEvent(args);
}
protected virtual void OnDestroy()
{
_pointableDelegate = null;
}
#region Inject
public void InjectAllPokeInteractable(Transform triggerPlaneTransform,
IProximityField proximityField)
{
InjectTriggerPlaneTransform(triggerPlaneTransform);
InjectProximityField(proximityField);
}
public void InjectTriggerPlaneTransform(Transform triggerPlaneTransform)
{
_triggerPlaneTransform = triggerPlaneTransform;
}
public void InjectProximityField(IProximityField proximityField)
{
_proximityField = proximityField as MonoBehaviour;
ProximityField = proximityField;
}
public void InjectOptionalMaxDistance(float maxDistance)
{
_maxDistance = maxDistance;
}
public void InjectOptionalReleaseDistance(float releaseDistance)
{
_releaseDistance = releaseDistance;
}
public void InjectHorizontalDragThreshold(float horizontalDragThreshold)
{
_horizontalDragThreshold = horizontalDragThreshold;
}
public void InjectVerticalDragThreshold(float verticalDragThreshold)
{
_verticalDragThreshold = verticalDragThreshold;
}
public void InjectOptionalVolumeMask(Collider volumeMask)
{
_volumeMask = volumeMask;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,360 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction
{
/// <summary>
/// Defines a near-poke interaction that is driven by a near-distance
/// proximity computation and a raycast between the position
/// recorded across two frames against a target plane.
/// </summary>
public class PokeInteractor : Interactor<PokeInteractor, PokeInteractable>
{
[SerializeField]
private Transform _pointTransform;
[SerializeField]
private float _touchReleaseThreshold = 0.002f;
[SerializeField]
private ProgressCurve _dragStartCurve;
private Vector3 _prevPosition = new Vector3();
private PokeInteractable _hitInteractable = null;
public Vector3 ClosestPoint { get; private set; }
private Vector3 _currentPosition;
public Vector3 Origin => _currentPosition;
private Vector3 _touchPoint;
public Vector3 TouchPoint => _touchPoint;
private Vector3 _previousTouchPoint;
private Vector3 _capturedTouchPoint;
private bool _dragging;
private Vector3 _lateralDelta;
private Vector3 _startDragOffset;
protected override void Start()
{
base.Start();
Assert.IsNotNull(_pointTransform);
}
protected override void DoEveryUpdate()
{
_prevPosition = _currentPosition;
_currentPosition = _pointTransform.position;
}
protected override void DoHoverUpdate()
{
if (_interactable != null)
{
_touchPoint = _interactable.ComputeClosestPoint(Origin);
}
if (_hitInteractable != null)
{
_hitInteractable = null;
ShouldSelect = true;
_dragging = false;
}
}
protected override PokeInteractable ComputeCandidate()
{
// First, see if we trigger a press on any interactable
PokeInteractable closestInteractable = ComputeSelectCandidate();
if (closestInteractable != null)
{
// We have found an active hit target, so we return it
_hitInteractable = closestInteractable;
return _hitInteractable;
}
// Otherwise we have no active interactable, so we do a proximity-only check for
// closest hovered interactable (above the trigger plane)
closestInteractable = ComputeBestHoverInteractable();
return closestInteractable;
}
private PokeInteractable ComputeSelectCandidate()
{
PokeInteractable closestInteractable = null;
float closestSqrDist = float.MaxValue;
IEnumerable<PokeInteractable> interactables = PokeInteractable.Registry.List(this);
Vector3 direction = _currentPosition - _prevPosition;
float magnitude = direction.magnitude;
if (magnitude == 0f)
{
return null;
}
direction /= magnitude;
Ray ray = new Ray(_prevPosition, direction);
// Check the trigger plane first as a movement through this will
// automatically put us in a "active" state. We expect the raycast
// to happen only in one direction
foreach (PokeInteractable interactable in interactables)
{
Transform triggerPlaneTransform = interactable.TriggerPlaneTransform;
// First check that we are moving towards the trigger plane by checking
// the direction of our position delta with the forward direction of the trigger transform.
// This is to not allow presses from "behind" the trigger plane.
// We consider the plane "forward" normal to be z = -1 (to maintain parity with Canvas)
if (Vector3.Dot(direction, triggerPlaneTransform.transform.forward) > 0f)
{
// Then do a raycast against the trigger plane defined by the trigger transform
Plane triggerPlane = new Plane(-1f * triggerPlaneTransform.forward,
triggerPlaneTransform.position);
float hitDistance;
bool hit = triggerPlane.Raycast(ray, out hitDistance);
if (hit && hitDistance <= magnitude)
{
// We collided against the trigger plane and now we must rank this
// interactable versus others that also pass this test this frame
// but may be at a closer proximity. For this we use the closest
// point compute against the plane intersection point
Vector3 planePoint = ray.GetPoint(hitDistance);
// Check if our collision lies outside of the optional volume mask
if (interactable.VolumeMask != null &&
!Collisions.IsPointWithinCollider(planePoint, interactable.VolumeMask))
{
continue;
}
Vector3 closestPointToHitPoint = interactable.ComputeClosestPoint(planePoint);
float sqrDistanceFromPoint = (closestPointToHitPoint - planePoint).sqrMagnitude;
if (sqrDistanceFromPoint > interactable.MaxDistance * interactable.MaxDistance) continue;
if (sqrDistanceFromPoint < closestSqrDist)
{
closestSqrDist = sqrDistanceFromPoint;
closestInteractable = interactable;
ClosestPoint = closestPointToHitPoint;
_touchPoint = ClosestPoint;
}
}
}
}
return closestInteractable;
}
private PokeInteractable ComputeBestHoverInteractable()
{
PokeInteractable closestInteractable = null;
float closestSqrDist = float.MaxValue;
IEnumerable<PokeInteractable> interactables = PokeInteractable.Registry.List(this);
// We check that we're above the trigger plane first as we don't
// care about hovers that originate below the trigger plane
foreach (PokeInteractable interactable in interactables)
{
Transform triggerPlaneTransform = interactable.TriggerPlaneTransform;
Vector3 planeToPoint = _currentPosition - triggerPlaneTransform.position;
float magnitude = planeToPoint.magnitude;
if (magnitude != 0f)
{
// We consider the plane "forward" normal to be z = -1 (to maintain parity with Canvas)
if (Vector3.Dot(planeToPoint, triggerPlaneTransform.transform.forward) < 0f)
{
// Check if our position lies outside of the optional volume mask
if (interactable.VolumeMask != null &&
!Collisions.IsPointWithinCollider(_currentPosition, interactable.VolumeMask))
{
continue;
}
// We're above the plane so now we must rank this
// interactable versus others that also pass this test this frame
// but may be at a closer proximity.
Vector3 closestPoint = interactable.ComputeClosestPoint(_currentPosition);
float sqrDistanceFromPoint = (closestPoint - _currentPosition).sqrMagnitude;
if (sqrDistanceFromPoint > interactable.MaxDistance * interactable.MaxDistance) continue;
if (sqrDistanceFromPoint < closestSqrDist)
{
closestSqrDist = sqrDistanceFromPoint;
closestInteractable = interactable;
ClosestPoint = closestPoint;
_touchPoint = ClosestPoint;
}
}
}
}
return closestInteractable;
}
protected override void InteractableSelected(PokeInteractable interactable)
{
if (interactable != null)
{
Vector3 worldPosition = ComputePlanePosition(interactable);
_previousTouchPoint = worldPosition;
_capturedTouchPoint = worldPosition;
_lateralDelta = Vector3.zero;
}
base.InteractableSelected(interactable);
}
private Vector3 ComputePlanePosition(PokeInteractable interactable)
{
Vector3 originRelativeToPlane = Origin - interactable.TriggerPlaneTransform.position;
Vector3 planePosition = Vector3.ProjectOnPlane(originRelativeToPlane, -1f * interactable.TriggerPlaneTransform.forward);
return planePosition + interactable.TriggerPlaneTransform.position;
}
private float ComputeDepth(PokeInteractable interactable, Vector3 loc)
{
Vector3 originRelativeToPlane = loc - interactable.TriggerPlaneTransform.position;
return Vector3.Project(originRelativeToPlane,
interactable.TriggerPlaneTransform.forward).magnitude *
(Vector3.Dot(originRelativeToPlane, interactable.TriggerPlaneTransform.forward) > 0
? 1.0f
: 0.0f);
}
protected override void DoSelectUpdate(PokeInteractable interactable)
{
if (interactable == null)
{
ShouldUnselect = true;
return;
}
Transform triggerPlaneTransform = interactable.TriggerPlaneTransform;
Vector3 triggerToInteractor = _currentPosition - triggerPlaneTransform.position;
// Unselect our interactor if it is above the plane of the trigger collider by at least releaseDistancePadding
if (Vector3.Dot(triggerToInteractor, triggerPlaneTransform.forward) < -1f * _touchReleaseThreshold)
{
ShouldUnselect = true;
}
Vector3 worldPosition = ComputePlanePosition(interactable);
float planarDelta = (worldPosition - _previousTouchPoint).magnitude;
float depthDelta = Mathf.Abs(ComputeDepth(interactable, Origin) -
ComputeDepth(interactable, _prevPosition));
Vector3 frameDelta = worldPosition - _previousTouchPoint;
bool outsideDelta = false;
if (!_dragging && planarDelta > depthDelta)
{
_lateralDelta += frameDelta;
while (!outsideDelta)
{
float horizontalDelta =
Vector3.Project(_lateralDelta, interactable.TriggerPlaneTransform.right).magnitude;
if (horizontalDelta > _selectedInteractable.HorizontalDragThreshold)
{
outsideDelta = true;
break;
}
float verticalDelta =
Vector3.Project(_lateralDelta, interactable.TriggerPlaneTransform.up).magnitude;
if (verticalDelta > _selectedInteractable.VerticalDragThreshold)
{
outsideDelta = true;
break;
}
break;
}
if (outsideDelta)
{
_dragStartCurve.Start();
_startDragOffset = _capturedTouchPoint - worldPosition;
_dragging = true;
}
}
if(!_dragging)
{
_touchPoint = _capturedTouchPoint;
}
else
{
float deltaEase = _dragStartCurve.Progress();
Vector3 offset = Vector3.Lerp(_startDragOffset, Vector3.zero, deltaEase);
_touchPoint = worldPosition + offset;
}
_previousTouchPoint = worldPosition;
Vector3 closestPoint = interactable.ComputeClosestPoint(_currentPosition);
float distanceFromPoint = (closestPoint - _currentPosition).magnitude;
if (interactable.ReleaseDistance > 0.0f)
{
if (distanceFromPoint > interactable.ReleaseDistance)
{
ShouldUnselect = true;
}
}
}
#region Inject
public void InjectAllPokeInteractor(Transform pointTransform)
{
InjectPointTransform(pointTransform);
}
public void InjectPointTransform(Transform pointTransform)
{
_pointTransform = pointTransform;
}
public void InjectOptionalTouchReleaseThreshold(float touchReleaseThreshold)
{
_touchReleaseThreshold = touchReleaseThreshold;
}
public void InjectOptionDragStartCurve(ProgressCurve dragStartCurve)
{
_dragStartCurve = dragStartCurve;
}
#endregion
}
}

View File

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

View File

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

View File

@@ -0,0 +1,163 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using UnityEngine;
using UnityEngine.Assertions;
using Oculus.Interaction.Input;
using UnityEngine.Serialization;
namespace Oculus.Interaction
{
/// <summary>
/// HandPokeInteractorVisual forwards the finger state of an associated
/// HandPokeInteractor to a HandGrabModifier to lock and unlock
/// finger joints in the modifier's target hand data.
/// </summary>
public class HandPokeLimiterVisual : MonoBehaviour
{
[SerializeField, Interface(typeof(IHand))]
private MonoBehaviour _hand;
private IHand Hand;
[FormerlySerializedAs("_interactor")]
[SerializeField]
private PokeInteractor _pokeInteractor;
[FormerlySerializedAs("_modifier")]
[SerializeField]
private SyntheticHandModifier _syntheticHand;
[SerializeField]
private float _maxDistanceFromTouchPoint = 0.1f;
private bool _isTouching;
private Vector3 _initialTouchPoint;
private float _maxDeltaFromTouchPoint;
protected bool _started = false;
protected virtual void Awake()
{
Hand = _hand as IHand;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
Assert.IsNotNull(Hand);
Assert.IsNotNull(_pokeInteractor);
Assert.IsNotNull(_syntheticHand);
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
_pokeInteractor.WhenInteractableSelected.Action += HandleLock;
_pokeInteractor.WhenInteractableUnselected.Action += HandleUnlock;
}
}
protected virtual void OnDisable()
{
if (_started)
{
if (_isTouching)
{
HandleUnlock(_pokeInteractor.SelectedInteractable);
}
_pokeInteractor.WhenInteractableSelected.Action -= HandleLock;
_pokeInteractor.WhenInteractableUnselected.Action -= HandleUnlock;
}
}
protected virtual void LateUpdate()
{
UpdateWrist();
}
private void HandleLock(PokeInteractable pokeInteractable)
{
_isTouching = true;
_initialTouchPoint = _pokeInteractor.TouchPoint;
}
private void HandleUnlock(PokeInteractable pokeInteractable)
{
_syntheticHand.FreeWrist();
_isTouching = false;
}
private Vector3 ComputePlanePosition(Vector3 point, PokeInteractable interactable)
{
Vector3 planeToPoint = point - interactable.TriggerPlaneTransform.position;
Vector3 projectOnNormal = Vector3.Project(planeToPoint, -1f * interactable.TriggerPlaneTransform.forward);
return point - projectOnNormal;
}
private void UpdateWrist()
{
if (!_isTouching) return;
if (!Hand.GetRootPose(out Pose rootPose))
{
return;
}
Vector3 planePosition = ComputePlanePosition(_pokeInteractor.Origin, _pokeInteractor.SelectedInteractable);
_maxDeltaFromTouchPoint = Mathf.Max((planePosition - _initialTouchPoint).magnitude, _maxDeltaFromTouchPoint);
float deltaAsPercent =
Mathf.Clamp01(_maxDeltaFromTouchPoint / _maxDistanceFromTouchPoint);
Vector3 fullDelta = planePosition - _initialTouchPoint;
Vector3 easedPosition = _initialTouchPoint + fullDelta * deltaAsPercent;
Vector3 positionDelta = rootPose.position - _pokeInteractor.Origin;
Vector3 targetPosePosition = easedPosition + positionDelta;
Pose wristPoseOverride = new Pose(targetPosePosition, rootPose.rotation);
_syntheticHand.LockWristPose(wristPoseOverride, 1.0f, SyntheticHandModifier.WristLockMode.Full, true, true);
_syntheticHand.MarkInputDataRequiresUpdate();
}
#region Inject
public void InjectAllHandPokeLimiterVisual(IHand hand, PokeInteractor pokeInteractor,
SyntheticHandModifier syntheticHand)
{
InjectHand(hand);
InjectPokeInteractor(pokeInteractor);
InjectSyntheticHand(syntheticHand);
}
public void InjectHand(IHand hand)
{
_hand = hand as MonoBehaviour;
Hand = hand;
}
public void InjectPokeInteractor(PokeInteractor pokeInteractor)
{
_pokeInteractor = pokeInteractor;
}
public void InjectSyntheticHand(SyntheticHandModifier syntheticHand)
{
_syntheticHand = syntheticHand;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cb028ce99f9700e4197a49bfdcd525dd
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