diff --git a/Assets/LeapMotionModules/InteractionEngine/Scripts/InteractionManager.cs b/Assets/LeapMotionModules/InteractionEngine/Scripts/InteractionManager.cs
index b1d2844..e8954c5 100644
--- a/Assets/LeapMotionModules/InteractionEngine/Scripts/InteractionManager.cs
+++ b/Assets/LeapMotionModules/InteractionEngine/Scripts/InteractionManager.cs
@@ -9,1182 +9,1184 @@
namespace Leap.Unity.Interaction {
- ///
- /// InteractionManager is the core behaviour that manages the IInteractionBehaviours in the scene.
- /// This class allows IInteractionBehaviours to register with it and provides all of the callbacks
- /// needed for operation. This class also takes care of all bookkeeping to keep track of the objects,
- /// hands, and the internal state of the interaction plugin.
- ///
- ///
- ///
- /// InteractionManager has the following features:
- /// - Allows instances of IInteractionBehaviour to register or unregister with it.
- /// - Registered instances stay registered even if this behaviour is disabled.
- /// - Dispatches events to the interaction plugin and uses the returned data to drive the registered
- /// behaviours. Takes care of all bookkeeping needed to maintain the internal state.
- /// - Supports the concept of 'suspension', where an untracked hand is still allowed to be considered
- /// grasping an object. This is to allow an object to not fall when a hand becomes untracked for
- /// a small amount of time. This helps with actions such as throwing.
- /// - Multiple instances of InteractionManager are ALLOWED! This allows you to have different simulation
- /// settings and control for different groups of objects.
- ///
- /// InteractionManager has the following requirements:
- /// - The DataSubfolder property must point to a valid subfolder in the StreamingAssets data folder.
- /// The subfolder must contain a valid ldat file names IE.
- ///
- public partial class InteractionManager : MonoBehaviour {
- #region SERIALIZED FIELDS
- [AutoFind]
- [SerializeField]
- protected LeapProvider _leapProvider;
-
- [AutoFind]
- [SerializeField]
- protected HandPool _handPool;
-
- [Tooltip("The streaming asset subpath of the ldat engine.")]
- [SerializeField]
- protected string _ldatPath = "InteractionEngine/IE.ldat";
-
- [Header("Interaction Settings")]
- [Tooltip("The default Interaction Material to use for Interaction Behaviours if none is specified, or for Interaction Behaviours created via scripting.")]
- [SerializeField]
- protected InteractionMaterial _defaultInteractionMaterial;
-
- [Tooltip("The name of the model group of the Hand Pool containing the brush hands.")]
- [SerializeField]
- protected string _brushGroupName = "BrushHands";
-
- [Tooltip("Allow the Interaction Engine to modify object velocities when pushing.")]
- [SerializeField]
- protected bool _contactEnabled = true;
-
- [Tooltip("Allow the Interaction plugin to modify object positions by grasping.")]
- [SerializeField]
- protected bool _graspingEnabled = true;
-
- [Tooltip("Depth before collision response becomes as if holding a sphere.")]
- [SerializeField]
- protected float _depthUntilSphericalInside = 0.023f;
-
- [Tooltip("Objects within this radius of a hand will be considered for interaction.")]
- [SerializeField]
- protected float _activationRadius = 0.15f;
-
- [Tooltip("How many objects away from the hand are still considered for interaction.")]
- [SerializeField]
- protected int _maxActivationDepth = 3;
-
- [Header("Layer Settings")]
- [SerializeField]
- protected bool _autoGenerateLayers = false;
-
- [Tooltip("Layer to use for auto-generation. The generated interaction layers will have the same collision settings as this layer.")]
- [SerializeField]
- protected SingleLayer _templateLayer = 0;
-
- [Tooltip("Default layer that interaction objects")]
- [SerializeField]
- protected SingleLayer _interactionLayer = 0;
-
- [Tooltip("Default layer that interaction objects when they become grasped.")]
- [SerializeField]
- protected SingleLayer _interactionNoClipLayer = 0;
-
- [Tooltip("Layer that interaction brushes will be on normally.")]
- [SerializeField]
- protected SingleLayer _brushLayer = 0;
-
- [Header("Debug")]
- [Tooltip("Automatically validate integrity of simulation state each frame. Can cause slowdown, but is always compiled out for release builds.")]
- [SerializeField]
- protected bool _automaticValidation = false;
-
- [Tooltip("Shows the debug visualization coming from the internal Interaction plugin.")]
- [SerializeField]
- protected bool _showDebugLines = false;
-
- [Tooltip("Shows the debug messages coming from the internal Interaction plugin.")]
- [SerializeField]
- protected bool _showDebugOutput = false;
-
- [Tooltip("Will display the debug messages if assigned.")]
- [SerializeField]
- protected Text _debugTextView;
- #endregion
-
- #region INTERNAL FIELDS
- private const float UNSCALED_RECOMMENDED_CONTACT_OFFSET_MAXIMUM = 0.001f; //One millimeter
- public float RecommendedContactOffsetMaximum {
- get {
- return UNSCALED_RECOMMENDED_CONTACT_OFFSET_MAXIMUM * SimulationScale;
- }
- }
-
- protected INTERACTION_SCENE _scene;
- private bool _hasSceneBeenCreated = false;
- private bool _enableGraspingLast = false;
-
- protected ActivityManager _activityManager = new ActivityManager();
- protected ShapeDescriptionPool _shapeDescriptionPool;
-
- //Maps the Interaction instance handle to the behaviour
- //A mapping only exists if a shape instance has been created
- protected Dictionary _instanceHandleToBehaviour = new Dictionary();
-
- protected Dictionary _idToInteractionHand = new Dictionary();
- protected List _graspedBehaviours = new List();
-
- private float _cachedSimulationScale = -1;
- //A temp list that is recycled. Used to remove items from _handIdToIeHand.
- private List _handIdsToRemove = new List();
- //A temp list that is recycled. Used as the argument to OnHandsHold.
- private List _holdingHands = new List();
- //A temp list that is recycled. Used to recieve results from InteractionC.
- private List _resultList = new List();
- //A temp list that is recycled. Used to recieve debug lines from InteractionC.
- private List _debugLines = new List();
- //A temp list that is recycled. Used to recieve debug logs from InteractionC.
- private List _debugOutput = new List();
- #endregion
-
- #region PUBLIC METHODS
- public Action OnGraphicalUpdate;
- public Action OnPrePhysicalUpdate;
- public Action OnPostPhysicalUpdate;
-
- ///
- /// Gets the current set of debug flags for this manager.
- ///
- public virtual DebugFlags DebugFlags {
- get {
- DebugFlags flags = DebugFlags.None;
- if (_showDebugLines) {
- flags |= DebugFlags.Lines;
- }
- if (_showDebugOutput) {
- flags |= DebugFlags.Strings;
- flags |= DebugFlags.Logging;
- }
- return flags;
- }
- }
-
- public float SimulationScale {
- get {
+ ///
+ /// InteractionManager is the core behaviour that manages the IInteractionBehaviours in the scene.
+ /// This class allows IInteractionBehaviours to register with it and provides all of the callbacks
+ /// needed for operation. This class also takes care of all bookkeeping to keep track of the objects,
+ /// hands, and the internal state of the interaction plugin.
+ ///
+ ///
+ ///
+ /// InteractionManager has the following features:
+ /// - Allows instances of IInteractionBehaviour to register or unregister with it.
+ /// - Registered instances stay registered even if this behaviour is disabled.
+ /// - Dispatches events to the interaction plugin and uses the returned data to drive the registered
+ /// behaviours. Takes care of all bookkeeping needed to maintain the internal state.
+ /// - Supports the concept of 'suspension', where an untracked hand is still allowed to be considered
+ /// grasping an object. This is to allow an object to not fall when a hand becomes untracked for
+ /// a small amount of time. This helps with actions such as throwing.
+ /// - Multiple instances of InteractionManager are ALLOWED! This allows you to have different simulation
+ /// settings and control for different groups of objects.
+ ///
+ /// InteractionManager has the following requirements:
+ /// - The DataSubfolder property must point to a valid subfolder in the StreamingAssets data folder.
+ /// The subfolder must contain a valid ldat file names IE.
+ ///
+ public partial class InteractionManager : MonoBehaviour {
+ #region SERIALIZED FIELDS
+ [AutoFind]
+ [SerializeField]
+ protected LeapProvider _leapProvider;
+
+ [AutoFind]
+ [SerializeField]
+ protected HandPool _handPool;
+
+ [Tooltip("The streaming asset subpath of the ldat engine.")]
+ [SerializeField]
+ protected string _ldatPath = "InteractionEngine/IE.ldat";
+
+ [Header("Interaction Settings")]
+ [Tooltip("The default Interaction Material to use for Interaction Behaviours if none is specified, or for Interaction Behaviours created via scripting.")]
+ [SerializeField]
+ protected InteractionMaterial _defaultInteractionMaterial;
+
+ [Tooltip("The name of the model group of the Hand Pool containing the brush hands.")]
+ [SerializeField]
+ protected string _brushGroupName = "BrushHands";
+
+ [Tooltip("Allow the Interaction Engine to modify object velocities when pushing.")]
+ [SerializeField]
+ protected bool _contactEnabled = true;
+
+ [Tooltip("Allow the Interaction plugin to modify object positions by grasping.")]
+ [SerializeField]
+ protected bool _graspingEnabled = true;
+
+ [Tooltip("Depth before collision response becomes as if holding a sphere.")]
+ [SerializeField]
+ protected float _depthUntilSphericalInside = 0.023f;
+
+ [Tooltip("Objects within this radius of a hand will be considered for interaction.")]
+ [SerializeField]
+ protected float _activationRadius = 0.15f;
+
+ [Tooltip("How many objects away from the hand are still considered for interaction.")]
+ [SerializeField]
+ protected int _maxActivationDepth = 3;
+
+ [Header("Layer Settings")]
+ [SerializeField]
+ protected bool _autoGenerateLayers = false;
+
+ [Tooltip("Layer to use for auto-generation. The generated interaction layers will have the same collision settings as this layer.")]
+ [SerializeField]
+ protected SingleLayer _templateLayer = 0;
+
+ [Tooltip("Default layer that interaction objects")]
+ [SerializeField]
+ protected SingleLayer _interactionLayer = 0;
+
+ [Tooltip("Default layer that interaction objects when they become grasped.")]
+ [SerializeField]
+ protected SingleLayer _interactionNoClipLayer = 0;
+
+ [Tooltip("Layer that interaction brushes will be on normally.")]
+ [SerializeField]
+ protected SingleLayer _brushLayer = 0;
+
+ [Header("Debug")]
+ [Tooltip("Automatically validate integrity of simulation state each frame. Can cause slowdown, but is always compiled out for release builds.")]
+ [SerializeField]
+ protected bool _automaticValidation = false;
+
+ [Tooltip("Shows the debug visualization coming from the internal Interaction plugin.")]
+ [SerializeField]
+ protected bool _showDebugLines = false;
+
+ [Tooltip("Shows the debug messages coming from the internal Interaction plugin.")]
+ [SerializeField]
+ protected bool _showDebugOutput = false;
+
+ [Tooltip("Will display the debug messages if assigned.")]
+ [SerializeField]
+ protected Text _debugTextView;
+ #endregion
+
+ #region INTERNAL FIELDS
+ private const float UNSCALED_RECOMMENDED_CONTACT_OFFSET_MAXIMUM = 0.001f; //One millimeter
+ public float RecommendedContactOffsetMaximum {
+ get {
+ return UNSCALED_RECOMMENDED_CONTACT_OFFSET_MAXIMUM * SimulationScale;
+ }
+ }
+
+ protected INTERACTION_SCENE _scene;
+ private bool _hasSceneBeenCreated = false;
+ private bool _enableGraspingLast = false;
+
+ protected ActivityManager _activityManager = new ActivityManager();
+ protected ShapeDescriptionPool _shapeDescriptionPool;
+
+ //Maps the Interaction instance handle to the behaviour
+ //A mapping only exists if a shape instance has been created
+ protected Dictionary _instanceHandleToBehaviour = new Dictionary();
+
+ protected Dictionary _idToInteractionHand = new Dictionary();
+ protected List _graspedBehaviours = new List();
+
+ private float _cachedSimulationScale = -1;
+ //A temp list that is recycled. Used to remove items from _handIdToIeHand.
+ private List _handIdsToRemove = new List();
+ //A temp list that is recycled. Used as the argument to OnHandsHold.
+ private List _holdingHands = new List();
+ //A temp list that is recycled. Used to recieve results from InteractionC.
+ private List _resultList = new List();
+ //A temp list that is recycled. Used to recieve debug lines from InteractionC.
+ private List _debugLines = new List();
+ //A temp list that is recycled. Used to recieve debug logs from InteractionC.
+ private List _debugOutput = new List();
+ #endregion
+
+ #region PUBLIC METHODS
+ public Action OnGraphicalUpdate;
+ public Action OnPrePhysicalUpdate;
+ public Action OnPostPhysicalUpdate;
+
+ ///
+ /// Gets the current set of debug flags for this manager.
+ ///
+ public virtual DebugFlags DebugFlags {
+ get {
+ DebugFlags flags = DebugFlags.None;
+ if (_showDebugLines) {
+ flags |= DebugFlags.Lines;
+ }
+ if (_showDebugOutput) {
+ flags |= DebugFlags.Strings;
+ flags |= DebugFlags.Logging;
+ }
+ return flags;
+ }
+ }
+
+ public float SimulationScale {
+ get {
#if UNITY_EDITOR
- if (Application.isPlaying) {
- return _cachedSimulationScale;
- } else {
- if (_leapProvider != null) {
- return _leapProvider.transform.lossyScale.x;
- } else {
- return 1;
- }
- }
+ if (Application.isPlaying) {
+ return _cachedSimulationScale;
+ } else {
+ if (_leapProvider != null) {
+ return _leapProvider.transform.lossyScale.x;
+ } else {
+ return 1;
+ }
+ }
#else
return _cachedSimulationScale;
#endif
- }
- }
-
- public INTERACTION_SCENE Scene {
- get {
- return _scene;
- }
- }
-
- ///
- /// Returns a ShapeDescriptionPool that can be used to allocate shape descriptions
- /// for this manager. Using the pool can be more efficient since identical shapes
- /// can be automatically combined to save memory. Shape descriptions aquired from this
- /// pool will be destroyed when this manager is disabled.
- ///
- public ShapeDescriptionPool ShapePool {
- get {
- return _shapeDescriptionPool;
- }
- }
-
- ///
- /// Returns true if any InteractionObject is currently being grasped by at least one Hand.
- ///
- public bool IsAnyObjectGrasped {
- get {
- return _graspedBehaviours.Count != 0;
- }
- }
-
- ///
- /// Returns a collection of InteractionBehaviours that are currently registered with this manager.
- ///
- public IEnumerable RegisteredObjects {
- get {
- return _activityManager.RegisteredObjects;
- }
- }
-
- ///
- /// Returns a collection of InteractionBehaviours that are currently being grasped by
- /// at least one hand.
- ///
- public ReadonlyList GraspedObjects {
- get {
- return _graspedBehaviours;
- }
- }
-
- ///
- /// Gets or sets the default InteractionMaterial used when InteractionBehaviours are spawned without a material explicitly assigned.
- ///
- public InteractionMaterial DefaultInteractionMaterial {
- get {
- return _defaultInteractionMaterial;
- }
- set {
- _defaultInteractionMaterial = value;
- }
- }
-
- ///
- /// Gets or sets whether or not the Interaction Engine can modify object velocities when pushing.
- ///
- public bool ContactEnabled {
- get {
- return _contactEnabled;
- }
- set {
- if (_contactEnabled != value) {
- _contactEnabled = value;
-
- if (_handPool != null) {
- if (_contactEnabled) {
- _handPool.EnableGroup(_brushGroupName);
- } else {
- _handPool.DisableGroup(_brushGroupName);
- }
- }
-
- UpdateSceneInfo();
- }
- }
- }
-
- ///
- /// Gets or sets whether or not the Interaction Engine can modify object positions by grasping.
- ///
- public bool GraspingEnabled {
- get {
- return _graspingEnabled;
- }
- set {
- if (_graspingEnabled != value) {
- _graspingEnabled = value;
- UpdateSceneInfo();
- }
- }
- }
-
- ///
- /// Depth before collision response becomes as if holding a sphere..
- ///
- public float DepthUntilSphericalInside {
- get {
- return _depthUntilSphericalInside;
- }
- set {
- _depthUntilSphericalInside = value;
- UpdateSceneInfo();
- }
- }
-
- ///
- /// Gets the layer that interaction objects should be on by default.
- ///
- public int InteractionLayer {
- get {
- return _interactionLayer;
- }
- set {
- _interactionLayer = value;
- }
- }
-
- ///
- /// Gets the layer that interaction objects should be on when they become grasped.
- ///
- public int InteractionNoClipLayer {
- get {
- return _interactionNoClipLayer;
- }
- set {
- _interactionNoClipLayer = value;
- }
- }
-
- ///
- /// Gets the layer that interaction brushes should be on normally.
- ///
- public int InteractionBrushLayer {
- get {
- return _brushLayer;
- }
- set {
- _brushLayer = value;
- }
- }
-
- ///
- /// Gets or sets the max activation depth.
- ///
- public int MaxActivationDepth {
- get {
- return _maxActivationDepth;
- }
- set {
- _maxActivationDepth = value;
- _activityManager.MaxDepth = value;
- }
- }
-
- ///
- /// Enables the display of proximity information from the library.
- ///
- public bool ShowDebugLines {
- get {
- return _showDebugLines;
- }
- set {
- _showDebugLines = value;
- applyDebugSettings();
- }
- }
-
- ///
- /// Enables the display of debug text from the library.
- ///
- public bool ShowDebugOutput {
- get {
- return _showDebugOutput;
- }
- set {
- _showDebugOutput = value;
- applyDebugSettings();
- }
- }
-
- /// Force an update of the internal scene info. This should be called if gravity has changed.
- ///
- public void UpdateSceneInfo() {
- if (!_hasSceneBeenCreated) {
- return; // UpdateSceneInfo is a side effect of a lot of changes.
- }
-
- INTERACTION_SCENE_INFO info = getSceneInfo();
-
- if (_graspingEnabled && !_enableGraspingLast) {
- using (LdatLoader.LoadLdat(ref info, _ldatPath)) {
- InteractionC.UpdateSceneInfo(ref _scene, ref info);
- }
- } else {
- InteractionC.UpdateSceneInfo(ref _scene, ref info);
- }
- _enableGraspingLast = _graspingEnabled;
-
- _cachedSimulationScale = _leapProvider.transform.lossyScale.x;
- _activityManager.OverlapRadius = _activationRadius * _cachedSimulationScale;
- }
-
- ///
- /// Tries to find an InteractionObject that is currently being grasped by a Hand with
- /// the given ID.
- ///
- public bool TryGetGraspedObject(int handId, out IInteractionBehaviour graspedObject) {
- for (int i = 0; i < _graspedBehaviours.Count; i++) {
- var iObj = _graspedBehaviours[i];
- if (iObj.IsBeingGraspedByHand(handId)) {
- graspedObject = iObj;
- return true;
- }
- }
-
- graspedObject = null;
- return false;
- }
-
- ///
- /// Forces the given object to be released by any hands currently holding it. Will return true
- /// only if there was at least one hand holding the object.
- ///
- public bool ReleaseObject(IInteractionBehaviour graspedObject) {
- if (!_graspedBehaviours.Remove(graspedObject)) {
- return false;
- }
-
- foreach (var interactionHand in _idToInteractionHand.Values) {
- if (interactionHand.graspedObject == graspedObject) {
- if (interactionHand.isUntracked) {
- interactionHand.MarkTimeout();
- } else {
- if (_graspingEnabled) {
- INTERACTION_HAND_RESULT result = new INTERACTION_HAND_RESULT();
- result.classification = ManipulatorMode.Contact;
- result.handFlags = HandResultFlags.ManipulatorMode;
- result.instanceHandle = new INTERACTION_SHAPE_INSTANCE_HANDLE();
- InteractionC.OverrideHandResult(ref _scene, (uint)interactionHand.hand.Id, ref result);
- }
- interactionHand.ReleaseObject();
- }
- }
- }
-
- return true;
- }
-
- ///
- /// Forces a hand with the given id to release an object if it is holding it. Will return true
- /// only if a hand with the given id releases an object it was holding.
- ///
- public bool ReleaseHand(int handId) {
- InteractionHand interactionHand;
- if (!_idToInteractionHand.TryGetValue(handId, out interactionHand)) {
- return false;
- }
-
- if (interactionHand.graspedObject == null) {
- return false;
- }
-
- if (interactionHand.graspedObject.GraspingHandCount == 1) {
- _graspedBehaviours.Remove(interactionHand.graspedObject);
- }
-
- interactionHand.ReleaseObject();
- return true;
- }
-
- ///
- /// Forces a hand to grasp the given interaction behaviour. The grasp will only be terminated when
- /// the hand either times out or the user calls ReleaseHand.
- ///
- ///
- public void GraspWithHand(Hand hand, IInteractionBehaviour interactionBehaviour) {
- if (!_activityManager.IsRegistered(interactionBehaviour)) {
- throw new InvalidOperationException("Cannot grasp " + interactionBehaviour + " because it is not registered with this manager.");
- }
-
- InteractionHand interactionHand;
- if (!_idToInteractionHand.TryGetValue(hand.Id, out interactionHand)) {
- throw new InvalidOperationException("Hand with id " + hand.Id + " is not registered with this manager.");
- }
-
- if (interactionHand.graspedObject != null) {
- throw new InvalidOperationException("Cannot grasp with hand " + hand.Id + " because that hand is already grasping " + interactionHand.graspedObject);
- }
-
- //Ensure behaviour is active already
- _activityManager.Activate(interactionBehaviour);
-
- if (!interactionBehaviour.IsBeingGrasped) {
- _graspedBehaviours.Add(interactionBehaviour);
- }
-
- interactionHand.GraspObject(interactionBehaviour, isUserGrasp: true);
- }
-
- ///
- /// Registers an InteractionObject with this manager, which automatically adds the objects
- /// representation into the internal interaction scene. If the manager is disabled,
- /// the registration will still succeed and the object will be added to the internal scene
- /// when the manager is next enabled.
- ///
- /// Trying to register a behaviour that is already registered is safe and is a no-op.
- ///
- public void RegisterInteractionBehaviour(IInteractionBehaviour interactionBehaviour) {
- _activityManager.Register(interactionBehaviour);
- }
-
- ///
- /// Unregisters an InteractionObject from this manager. This removes it from the internal
- /// scene and prevents any further interaction.
- ///
- /// Trying to unregister a behaviour that is not registered is safe and is a no-op.
- ///
- public void UnregisterInteractionBehaviour(IInteractionBehaviour interactionBehaviour) {
- if (_graspedBehaviours.Remove(interactionBehaviour)) {
- foreach (var interactionHand in _idToInteractionHand.Values) {
- if (interactionHand.graspedObject == interactionBehaviour) {
- try {
- if (interactionHand.isUntracked) {
- interactionHand.MarkTimeout();
- } else {
- interactionHand.ReleaseObject();
- }
- } catch (Exception e) {
- //Only log to console
- //We want to continue so we can destroy the shape and dispatch OnUnregister
- Debug.LogException(e);
- }
- break;
- }
- }
- }
-
- _activityManager.Unregister(interactionBehaviour);
- }
-
- public void EnsureActive(IInteractionBehaviour interactionBehaviour) {
- if (!_activityManager.IsActive(interactionBehaviour)) {
- _activityManager.Activate(interactionBehaviour);
- }
- }
-
- #endregion
-
- #region UNITY CALLBACKS
- protected virtual void Reset() {
- if (_leapProvider == null) {
- _leapProvider = FindObjectOfType();
- }
- }
-
- protected virtual void OnValidate() {
- if (Application.isPlaying && _hasSceneBeenCreated) {
- //Allow the debug lines to be toggled while the scene is playing
- applyDebugSettings();
- //Allow scene info to be updated while the scene is playing
- UpdateSceneInfo();
- }
-
- if (!Application.isPlaying && _autoGenerateLayers) {
- autoGenerateLayers();
- }
-
- _activationRadius = Mathf.Max(0, _activationRadius);
- _maxActivationDepth = Mathf.Max(1, _maxActivationDepth);
-
- if (_activityManager != null) {
- _activityManager.OverlapRadius = _activationRadius;
- _activityManager.MaxDepth = _maxActivationDepth;
- }
- }
-
- protected virtual void Awake() {
- if (_autoGenerateLayers) {
- autoGenerateLayers();
- autoSetupCollisionLayers();
- }
- }
-
- protected virtual void OnEnable() {
- if (_leapProvider == null) {
- enabled = false;
- Debug.LogError("Could not enable Interaction Manager because no Leap Provider was specified.");
- return;
- }
-
- Assert.IsFalse(_hasSceneBeenCreated, "Scene should not have been created yet");
-
- createScene();
- applyDebugSettings();
-
- _shapeDescriptionPool = new ShapeDescriptionPool(_scene);
-
- Assert.AreEqual(_instanceHandleToBehaviour.Count, 0, "There should not be any instances before the creation step.");
-
- _cachedSimulationScale = _leapProvider.transform.lossyScale.x;
-
- _activityManager.BrushLayer = InteractionBrushLayer;
- _activityManager.OverlapRadius = _activationRadius * _cachedSimulationScale;
- _activityManager.MaxDepth = _maxActivationDepth;
- _activityManager.OnActivate += createInteractionShape;
- _activityManager.OnDeactivate += destroyInteractionShape;
-
- if (_handPool != null) {
- if (_contactEnabled) {
- _handPool.EnableGroup(_brushGroupName);
- } else {
- _handPool.DisableGroup(_brushGroupName);
- }
- }
- }
-
- protected virtual void OnDisable() {
- for (int i = _graspedBehaviours.Count; i-- != 0;) {
- ReleaseObject(_graspedBehaviours[i]);
- }
-
- _activityManager.UnregisterMisbehavingObjects();
-
- _idToInteractionHand.Clear();
- _graspedBehaviours.Clear();
-
- _activityManager.DeactivateAll();
- _activityManager.OnActivate -= createInteractionShape;
- _activityManager.OnDeactivate -= destroyInteractionShape;
-
- Assert.AreEqual(_instanceHandleToBehaviour.Count, 0, "All instances should have been destroyed.");
-
- if (_shapeDescriptionPool != null) {
- _shapeDescriptionPool.RemoveAllShapes();
- _shapeDescriptionPool = null;
- }
-
- if (_hasSceneBeenCreated) {
- destroyScene();
- }
- }
-
- protected virtual void FixedUpdate() {
- Frame frame = _leapProvider.CurrentFixedFrame;
-
- if (OnPrePhysicalUpdate != null) {
- OnPrePhysicalUpdate();
- }
-
- simulateFrame(frame);
-
- if (OnPostPhysicalUpdate != null) {
- OnPostPhysicalUpdate();
- }
-
- if (_showDebugLines) {
- RuntimeGizmoDrawer gizmoDrawer;
- if (RuntimeGizmoManager.TryGetGizmoDrawer(gameObject, out gizmoDrawer)) {
- InteractionC.GetDebugLines(ref _scene, _debugLines);
- for (int i = 0; i < _debugLines.Count; i++) {
- var line = _debugLines[i];
- gizmoDrawer.color = line.color.ToUnityColor();
- gizmoDrawer.DrawLine(line.start.ToVector3(), line.end.ToVector3());
- }
- }
- }
-
- if (_showDebugOutput) {
- InteractionC.GetDebugStrings(ref _scene, _debugOutput);
- }
- }
-
- protected virtual void LateUpdate() {
- Frame frame = _leapProvider.CurrentFrame;
-
- dispatchOnHandsHoldingAll(frame, isPhysics: false);
-
- _activityManager.UnregisterMisbehavingObjects();
-
- if (OnGraphicalUpdate != null) {
- OnGraphicalUpdate();
- }
-
- if (_showDebugOutput && _debugTextView != null) {
- string text = "";
- for (int i = 0; i < _debugOutput.Count; i++) {
- text += _debugOutput[i];
- if (i != _debugOutput.Count - 1) {
- text += "\n";
- }
- }
- _debugTextView.text = text;
- }
-
- if (_automaticValidation) {
- Validate();
- }
- }
-
- protected virtual void OnGUI() {
- if (_showDebugOutput) {
- for (int i = 0; i < _debugOutput.Count; i++) {
- GUILayout.Label(_debugOutput[i]);
- }
- }
- }
- #endregion
-
- #region INTERNAL METHODS
-
- protected void autoGenerateLayers() {
- _interactionLayer = -1;
- _interactionNoClipLayer = -1;
- _brushLayer = -1;
- for (int i = 8; i < 32; i++) {
- string layerName = LayerMask.LayerToName(i);
- if (string.IsNullOrEmpty(layerName)) {
- if (_interactionLayer == -1) {
- _interactionLayer = i;
- } else if (_interactionNoClipLayer == -1) {
- _interactionNoClipLayer = i;
- } else if (_brushLayer == -1) {
- _brushLayer = i;
- break;
- }
- }
- }
-
- if (_interactionLayer == -1 || _interactionNoClipLayer == -1 || _brushLayer == -1) {
- if (Application.isPlaying) {
- enabled = false;
- }
- Debug.LogError("InteractionManager Could not find enough free layers for auto-setup, manual setup required.");
- _autoGenerateLayers = false;
- return;
- }
- }
-
- private void autoSetupCollisionLayers() {
- for (int i = 0; i < 32; i++) {
- // Copy ignore settings from template layer
- bool shouldIgnore = Physics.GetIgnoreLayerCollision(_templateLayer, i);
- Physics.IgnoreLayerCollision(_interactionLayer, i, shouldIgnore);
- Physics.IgnoreLayerCollision(_interactionNoClipLayer, i, shouldIgnore);
-
- // Set brush layer to collide with nothing
- Physics.IgnoreLayerCollision(_brushLayer, i, true);
- }
-
- //After copy and set we enable the interaction between the brushes and interaction objects
- Physics.IgnoreLayerCollision(_brushLayer, _interactionLayer, false);
- }
-
- protected virtual void simulateFrame(Frame frame) {
- _activityManager.UpdateState(frame);
-
- var active = _activityManager.ActiveBehaviours;
-
- for (int i = 0; i < active.Count; i++) {
- active[i].NotifyPreSolve();
- }
-
- dispatchOnHandsHoldingAll(frame, isPhysics: true);
-
- updateInteractionRepresentations();
-
- updateTracking(frame);
-
- simulateInteraction();
-
- updateInteractionStateChanges(frame);
-
- dispatchSimulationResults();
-
- for (int i = 0; i < active.Count; i++) {
- active[i].NotifyPostSolve();
- }
- }
-
- protected virtual void applyDebugSettings() {
- InteractionC.EnableDebugFlags(ref _scene, (uint)DebugFlags);
- }
-
- protected virtual void updateInteractionRepresentations() {
- var active = _activityManager.ActiveBehaviours;
- for (int i = 0; i < active.Count; i++) {
- IInteractionBehaviour interactionBehaviour = active[i];
- try {
- INTERACTION_SHAPE_INSTANCE_HANDLE shapeInstanceHandle = interactionBehaviour.ShapeInstanceHandle;
-
- INTERACTION_UPDATE_SHAPE_INFO updateInfo;
- INTERACTION_TRANSFORM updateTransform;
- interactionBehaviour.GetInteractionShapeUpdateInfo(out updateInfo, out updateTransform);
-
- InteractionC.UpdateShapeInstance(ref _scene, ref updateTransform, ref updateInfo, ref shapeInstanceHandle);
- } catch (Exception e) {
- _activityManager.NotifyMisbehaving(interactionBehaviour);
- Debug.LogException(e);
- }
- }
- }
-
- protected virtual void dispatchOnHandsHoldingAll(Frame frame, bool isPhysics) {
- var hands = frame.Hands;
- //Loop through the currently grasped objects to dispatch their OnHandsHold callback
- for (int i = 0; i < _graspedBehaviours.Count; i++) {
- dispatchOnHandsHolding(hands, _graspedBehaviours[i], isPhysics);
- }
- }
-
- protected virtual void dispatchOnHandsHolding(List hands, IInteractionBehaviour interactionBehaviour, bool isPhysics) {
- for (int j = 0; j < hands.Count; j++) {
- var hand = hands[j];
- InteractionHand interactionHand;
- if (_idToInteractionHand.TryGetValue(hand.Id, out interactionHand)) {
- if (interactionHand.graspedObject == interactionBehaviour) {
- _holdingHands.Add(hand);
- }
- }
- }
-
- try {
- if (isPhysics) {
- interactionBehaviour.NotifyHandsHoldPhysics(_holdingHands);
- } else {
- interactionBehaviour.NotifyHandsHoldGraphics(_holdingHands);
- }
- } catch (Exception e) {
- _activityManager.NotifyMisbehaving(interactionBehaviour);
- Debug.LogException(e);
- }
-
- _holdingHands.Clear();
- }
-
- protected virtual void updateTracking(Frame frame) {
- int handCount = frame.Hands.Count;
- IntPtr ptr = HandArrayBuilder.CreateHandArray(frame);
- InteractionC.UpdateHands(ref _scene, (uint)handCount, ptr);
- StructAllocator.CleanupAllocations();
- }
-
- protected virtual void simulateInteraction() {
- var _controllerTransform = new INTERACTION_TRANSFORM();
- _controllerTransform.position = _leapProvider.transform.position.ToCVector();
- _controllerTransform.rotation = _leapProvider.transform.rotation.ToCQuaternion();
- _controllerTransform.wallTime = Time.fixedTime;
-
- InteractionC.UpdateController(ref _scene, ref _controllerTransform);
- }
-
- protected virtual void updateInteractionStateChanges(Frame frame) {
- var hands = frame.Hands;
-
- INTERACTION_HAND_RESULT handResult = new INTERACTION_HAND_RESULT();
-
- //First loop through all the hands and get their classifications from the engine
- for (int i = 0; i < hands.Count; i++) {
- Hand hand = hands[i];
-
- bool handResultForced = false;
-
- //Get the InteractionHand associated with this hand id
- InteractionHand interactionHand;
- if (!_idToInteractionHand.TryGetValue(hand.Id, out interactionHand)) {
-
- //First we see if there is an untracked interactionHand that can be re-connected using this one
- InteractionHand untrackedInteractionHand = null;
- foreach (var pair in _idToInteractionHand) {
- //If the old ieHand is untracked, and the handedness matches, we re-connect it
- if (pair.Value.isUntracked && pair.Value.hand.IsLeft == hand.IsLeft) {
- untrackedInteractionHand = pair.Value;
- break;
- }
- }
-
- if (untrackedInteractionHand != null) {
- //If we found an untrackedIeHand, use it!
- interactionHand = untrackedInteractionHand;
- //Remove the old id from the mapping
- _idToInteractionHand.Remove(untrackedInteractionHand.hand.Id);
- _idToInteractionHand[hand.Id] = interactionHand;
-
- try {
- //This also dispatched InteractionObject.OnHandRegainedTracking()
- interactionHand.RegainTracking(hand);
-
- if (interactionHand.graspedObject == null) {
- continue;
- }
-
- // NotifyHandRegainedTracking() did not throw, continue on to NotifyHandsHoldPhysics().
- dispatchOnHandsHolding(hands, interactionHand.graspedObject, isPhysics: true);
- } catch (Exception e) {
- _activityManager.NotifyMisbehaving(interactionHand.graspedObject);
- Debug.LogException(e);
- continue;
- }
-
- //Override the existing classification to force the hand to grab the old object
- handResultForced = true;
- handResult.classification = ManipulatorMode.Grasp;
- handResult.handFlags = HandResultFlags.ManipulatorMode;
- handResult.instanceHandle = interactionHand.graspedObject.ShapeInstanceHandle;
-
- if (_graspingEnabled) {
- InteractionC.OverrideHandResult(ref _scene, (uint)hand.Id, ref handResult);
- }
- } else {
- //Otherwise just create a new one
- interactionHand = new InteractionHand(hand);
- _idToInteractionHand[hand.Id] = interactionHand;
- }
- }
-
- if (!handResultForced) {
- handResult = getHandResults(interactionHand);
- }
-
- interactionHand.UpdateHand(hand);
-
- if (!interactionHand.isUserGrasp) {
- switch (handResult.classification) {
- case ManipulatorMode.Grasp:
- {
- IInteractionBehaviour interactionBehaviour;
- if (_instanceHandleToBehaviour.TryGetValue(handResult.instanceHandle, out interactionBehaviour)) {
- if (interactionHand.graspedObject == null) {
- if (!interactionBehaviour.IsBeingGrasped) {
- _graspedBehaviours.Add(interactionBehaviour);
- }
-
- try {
- interactionHand.GraspObject(interactionBehaviour, isUserGrasp: false);
-
- //the grasp callback might have caused the object to become ungrasped
- //the component might have also destroyed itself!
- if (interactionHand.graspedObject == interactionBehaviour && interactionBehaviour != null) {
- dispatchOnHandsHolding(hands, interactionBehaviour, isPhysics: true);
- }
- } catch (Exception e) {
- _activityManager.NotifyMisbehaving(interactionBehaviour);
- Debug.LogException(e);
- continue;
- }
- }
- } else {
- Debug.LogError("Recieved a hand result with an unkown handle " + handResult.instanceHandle.handle);
- }
- break;
- }
- case ManipulatorMode.Contact:
- {
- if (interactionHand.graspedObject != null) {
- if (interactionHand.graspedObject.GraspingHandCount == 1) {
- _graspedBehaviours.Remove(interactionHand.graspedObject);
- }
-
- try {
- interactionHand.ReleaseObject();
- } catch (Exception e) {
- _activityManager.NotifyMisbehaving(interactionHand.graspedObject);
- Debug.LogException(e);
- continue;
- }
- }
- break;
- }
- default:
- throw new InvalidOperationException("Unexpected classification " + handResult.classification);
- }
- }
- }
-
- //Loop through all ieHands to check for timeouts and loss of tracking
- foreach (var pair in _idToInteractionHand) {
- var id = pair.Key;
- var ieHand = pair.Value;
-
- float handAge = Time.unscaledTime - ieHand.lastTimeUpdated;
- //Check to see if the hand is at least 1 frame old
- //We assume it has become untracked if this is the case
- if (handAge > 0) {
- //If the hand isn't grasping anything, just remove it
- if (ieHand.graspedObject == null) {
- _handIdsToRemove.Add(id);
- continue;
- }
-
- //If is isn't already marked as untracked, mark it as untracked
- if (!ieHand.isUntracked) {
- try {
- //This also dispatches InteractionObject.OnHandLostTracking()
- ieHand.MarkUntracked();
- } catch (Exception e) {
- _activityManager.NotifyMisbehaving(ieHand.graspedObject);
- Debug.LogException(e);
- }
- }
-
- //If the age is longer than the timeout, we also remove it from the list
- if (handAge >= ieHand.maxSuspensionTime) {
- _handIdsToRemove.Add(id);
-
- try {
- if (ieHand.graspedObject.GraspingHandCount == 1) {
- _graspedBehaviours.Remove(ieHand.graspedObject);
- }
-
- //This also dispatched InteractionObject.OnHandTimeout()
- ieHand.MarkTimeout();
- } catch (Exception e) {
- _activityManager.NotifyMisbehaving(ieHand.graspedObject);
- Debug.LogException(e);
- }
- }
- }
- }
-
- //Loop through the stale ids and remove them from the map
- for (int i = 0; i < _handIdsToRemove.Count; i++) {
- _idToInteractionHand.Remove(_handIdsToRemove[i]);
- }
- _handIdsToRemove.Clear();
- }
-
- protected virtual INTERACTION_HAND_RESULT getHandResults(InteractionHand hand) {
- if (!_graspingEnabled) {
- INTERACTION_HAND_RESULT result = new INTERACTION_HAND_RESULT();
- result.classification = ManipulatorMode.Contact;
- result.handFlags = HandResultFlags.ManipulatorMode;
- result.instanceHandle = new INTERACTION_SHAPE_INSTANCE_HANDLE();
- return result;
- }
-
- INTERACTION_HAND_RESULT handResult;
- InteractionC.GetHandResult(ref _scene,
- (uint)hand.hand.Id,
- out handResult);
- return handResult;
- }
-
- protected virtual void dispatchSimulationResults() {
- InteractionC.GetShapeInstanceResults(ref _scene, _resultList);
-
- for (int i = 0; i < _resultList.Count; ++i) {
- INTERACTION_SHAPE_INSTANCE_RESULTS result = _resultList[i];
-
- //Behaviour might have already been unregistered during an earlier callback for this simulation step
- IInteractionBehaviour interactionBehaviour;
- if (_instanceHandleToBehaviour.TryGetValue(result.handle, out interactionBehaviour)) {
- try {
- // ShapeInstanceResultFlags.None may be returned if requested when hands are not touching.
- interactionBehaviour.NotifyRecievedSimulationResults(result);
- } catch (Exception e) {
- _activityManager.NotifyMisbehaving(interactionBehaviour);
- Debug.LogException(e);
- }
- }
- }
- }
-
- protected virtual void createInteractionShape(IInteractionBehaviour interactionBehaviour) {
- INTERACTION_SHAPE_DESCRIPTION_HANDLE descriptionHandle = interactionBehaviour.ShapeDescriptionHandle;
- INTERACTION_SHAPE_INSTANCE_HANDLE instanceHandle = new INTERACTION_SHAPE_INSTANCE_HANDLE();
-
- INTERACTION_CREATE_SHAPE_INFO createInfo;
- INTERACTION_TRANSFORM createTransform;
- interactionBehaviour.GetInteractionShapeCreationInfo(out createInfo, out createTransform);
-
- InteractionC.CreateShapeInstance(ref _scene, ref descriptionHandle, ref createTransform, ref createInfo, out instanceHandle);
-
- _instanceHandleToBehaviour[instanceHandle] = interactionBehaviour;
-
- interactionBehaviour.NotifyInteractionShapeCreated(instanceHandle);
- }
-
- protected virtual void destroyInteractionShape(IInteractionBehaviour interactionBehaviour) {
- INTERACTION_SHAPE_INSTANCE_HANDLE instanceHandle = interactionBehaviour.ShapeInstanceHandle;
-
- _instanceHandleToBehaviour.Remove(instanceHandle);
-
- InteractionC.DestroyShapeInstance(ref _scene, ref instanceHandle);
-
- interactionBehaviour.NotifyInteractionShapeDestroyed();
- }
-
- protected virtual void createScene() {
- _scene.pScene = (IntPtr)0;
-
- UInt32 libraryVersion = InteractionC.GetLibraryVersion();
- UInt32 expectedVersion = InteractionC.GetExpectedVersion();
- if (libraryVersion != expectedVersion) {
- Debug.LogError("Leap Interaction dll version expected: " + expectedVersion + " got version: " + libraryVersion);
- throw new Exception("Leap Interaction library version wrong");
- }
-
- InteractionC.CreateScene(ref _scene);
- _hasSceneBeenCreated = true;
-
- UpdateSceneInfo();
- }
-
- protected virtual void destroyScene() {
- InteractionC.DestroyScene(ref _scene);
- _hasSceneBeenCreated = false;
- }
-
- protected virtual INTERACTION_SCENE_INFO getSceneInfo() {
- INTERACTION_SCENE_INFO info = new INTERACTION_SCENE_INFO();
- info.sceneFlags = SceneInfoFlags.None;
-
- if (Physics.gravity.sqrMagnitude != 0.0f) {
- info.sceneFlags |= SceneInfoFlags.HasGravity;
- info.gravity = Physics.gravity.ToCVector();
- }
-
- if (_depthUntilSphericalInside > 0.0f) {
- info.sceneFlags |= SceneInfoFlags.SphericalInside;
- info.depthUntilSphericalInside = _depthUntilSphericalInside;
- }
-
- if (_contactEnabled) {
- info.sceneFlags |= SceneInfoFlags.ContactEnabled;
- }
-
- // _enableGraspingLast gaurds against expensive file IO. Only load the ldat
- // data when grasping is being enabled.
- if (_graspingEnabled) {
- info.sceneFlags |= SceneInfoFlags.GraspEnabled;
- }
-
- return info;
- }
-
- //A persistant structure for storing useful data about a hand as it interacts with objects
- //TODO: Investigate pooling?
- protected partial class InteractionHand {
- public Hand hand { get; protected set; }
- public float lastTimeUpdated { get; protected set; }
- public float maxSuspensionTime { get; protected set; }
- public IInteractionBehaviour graspedObject { get; protected set; }
- public bool isUntracked { get; protected set; }
- public bool isUserGrasp { get; protected set; }
-
- public InteractionHand(Hand hand) {
- this.hand = new Hand().CopyFrom(hand);
- lastTimeUpdated = Time.unscaledTime;
- graspedObject = null;
- }
-
- public void UpdateHand(Hand hand) {
- this.hand.CopyFrom(hand);
- lastTimeUpdated = Time.unscaledTime;
- }
-
- public void GraspObject(IInteractionBehaviour obj, bool isUserGrasp) {
- this.isUserGrasp = isUserGrasp;
- graspedObject = obj;
- graspedObject.NotifyHandGrasped(hand);
- }
-
- public void ReleaseObject() {
- graspedObject.NotifyHandReleased(hand);
- graspedObject = null;
- isUntracked = false;
- isUserGrasp = false;
- }
-
- public void MarkUntracked() {
- isUntracked = true;
- float outTime;
- graspedObject.NotifyHandLostTracking(hand, out outTime);
- maxSuspensionTime = outTime;
- }
-
- public void MarkTimeout() {
- graspedObject.NotifyHandTimeout(hand);
- graspedObject = null;
- isUntracked = true;
- isUserGrasp = false;
- hand = null;
- }
-
- public void RegainTracking(Hand newHand) {
- int oldId = hand.Id;
- UpdateHand(newHand);
-
- isUntracked = false;
- graspedObject.NotifyHandRegainedTracking(newHand, oldId);
- }
- }
- #endregion
- }
+ }
+ }
+
+ public INTERACTION_SCENE Scene {
+ get {
+ return _scene;
+ }
+ }
+
+ ///
+ /// Returns a ShapeDescriptionPool that can be used to allocate shape descriptions
+ /// for this manager. Using the pool can be more efficient since identical shapes
+ /// can be automatically combined to save memory. Shape descriptions aquired from this
+ /// pool will be destroyed when this manager is disabled.
+ ///
+ public ShapeDescriptionPool ShapePool {
+ get {
+ return _shapeDescriptionPool;
+ }
+ }
+
+ ///
+ /// Returns true if any InteractionObject is currently being grasped by at least one Hand.
+ ///
+ public bool IsAnyObjectGrasped {
+ get {
+ return _graspedBehaviours.Count != 0;
+ }
+ }
+
+ ///
+ /// Returns a collection of InteractionBehaviours that are currently registered with this manager.
+ ///
+ public IEnumerable RegisteredObjects {
+ get {
+ return _activityManager.RegisteredObjects;
+ }
+ }
+
+ ///
+ /// Returns a collection of InteractionBehaviours that are currently being grasped by
+ /// at least one hand.
+ ///
+ public ReadonlyList GraspedObjects {
+ get {
+ return _graspedBehaviours;
+ }
+ }
+
+ ///
+ /// Gets or sets the default InteractionMaterial used when InteractionBehaviours are spawned without a material explicitly assigned.
+ ///
+ public InteractionMaterial DefaultInteractionMaterial {
+ get {
+ return _defaultInteractionMaterial;
+ }
+ set {
+ _defaultInteractionMaterial = value;
+ }
+ }
+
+ ///
+ /// Gets or sets whether or not the Interaction Engine can modify object velocities when pushing.
+ ///
+ public bool ContactEnabled {
+ get {
+ return _contactEnabled;
+ }
+ set {
+ if (_contactEnabled != value) {
+ _contactEnabled = value;
+
+ if (_handPool != null) {
+ if (_contactEnabled) {
+ _handPool.EnableGroup(_brushGroupName);
+ } else {
+ _handPool.DisableGroup(_brushGroupName);
+ }
+ }
+
+ UpdateSceneInfo();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets whether or not the Interaction Engine can modify object positions by grasping.
+ ///
+ public bool GraspingEnabled {
+ get {
+ return _graspingEnabled;
+ }
+ set {
+ if (_graspingEnabled != value) {
+ _graspingEnabled = value;
+ UpdateSceneInfo();
+ }
+ }
+ }
+
+ ///
+ /// Depth before collision response becomes as if holding a sphere..
+ ///
+ public float DepthUntilSphericalInside {
+ get {
+ return _depthUntilSphericalInside;
+ }
+ set {
+ _depthUntilSphericalInside = value;
+ UpdateSceneInfo();
+ }
+ }
+
+ ///
+ /// Gets the layer that interaction objects should be on by default.
+ ///
+ public int InteractionLayer {
+ get {
+ return _interactionLayer;
+ }
+ set {
+ _interactionLayer = value;
+ }
+ }
+
+ ///
+ /// Gets the layer that interaction objects should be on when they become grasped.
+ ///
+ public int InteractionNoClipLayer {
+ get {
+ return _interactionNoClipLayer;
+ }
+ set {
+ _interactionNoClipLayer = value;
+ }
+ }
+
+ ///
+ /// Gets the layer that interaction brushes should be on normally.
+ ///
+ public int InteractionBrushLayer {
+ get {
+ return _brushLayer;
+ }
+ set {
+ _brushLayer = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the max activation depth.
+ ///
+ public int MaxActivationDepth {
+ get {
+ return _maxActivationDepth;
+ }
+ set {
+ _maxActivationDepth = value;
+ _activityManager.MaxDepth = value;
+ }
+ }
+
+ ///
+ /// Enables the display of proximity information from the library.
+ ///
+ public bool ShowDebugLines {
+ get {
+ return _showDebugLines;
+ }
+ set {
+ _showDebugLines = value;
+ applyDebugSettings();
+ }
+ }
+
+ ///
+ /// Enables the display of debug text from the library.
+ ///
+ public bool ShowDebugOutput {
+ get {
+ return _showDebugOutput;
+ }
+ set {
+ _showDebugOutput = value;
+ applyDebugSettings();
+ }
+ }
+
+ /// Force an update of the internal scene info. This should be called if gravity has changed.
+ ///
+ public void UpdateSceneInfo() {
+ if (!_hasSceneBeenCreated) {
+ return; // UpdateSceneInfo is a side effect of a lot of changes.
+ }
+
+ INTERACTION_SCENE_INFO info = getSceneInfo();
+
+ if (_graspingEnabled && !_enableGraspingLast) {
+ using (LdatLoader.LoadLdat(ref info, _ldatPath)) {
+ InteractionC.UpdateSceneInfo(ref _scene, ref info);
+ }
+ } else {
+ InteractionC.UpdateSceneInfo(ref _scene, ref info);
+ }
+ _enableGraspingLast = _graspingEnabled;
+
+ _cachedSimulationScale = _leapProvider.transform.lossyScale.x;
+ _activityManager.OverlapRadius = _activationRadius * _cachedSimulationScale;
+ }
+
+ ///
+ /// Tries to find an InteractionObject that is currently being grasped by a Hand with
+ /// the given ID.
+ ///
+ public bool TryGetGraspedObject(int handId, out IInteractionBehaviour graspedObject) {
+ for (int i = 0; i < _graspedBehaviours.Count; i++) {
+ var iObj = _graspedBehaviours[i];
+ if (iObj.IsBeingGraspedByHand(handId)) {
+ graspedObject = iObj;
+ return true;
+ }
+ }
+
+ graspedObject = null;
+ return false;
+ }
+
+ ///
+ /// Forces the given object to be released by any hands currently holding it. Will return true
+ /// only if there was at least one hand holding the object.
+ ///
+ public bool ReleaseObject(IInteractionBehaviour graspedObject) {
+ if (!_graspedBehaviours.Remove(graspedObject)) {
+ return false;
+ }
+
+ foreach (var interactionHand in _idToInteractionHand.Values) {
+ if (interactionHand.graspedObject == graspedObject) {
+ if (interactionHand.isUntracked) {
+ interactionHand.MarkTimeout();
+ } else {
+ if (_graspingEnabled) {
+ INTERACTION_HAND_RESULT result = new INTERACTION_HAND_RESULT();
+ result.classification = ManipulatorMode.Contact;
+ result.handFlags = HandResultFlags.ManipulatorMode;
+ result.instanceHandle = new INTERACTION_SHAPE_INSTANCE_HANDLE();
+ InteractionC.OverrideHandResult(ref _scene, (uint)interactionHand.hand.Id, ref result);
+ }
+ interactionHand.ReleaseObject();
+ }
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Forces a hand with the given id to release an object if it is holding it. Will return true
+ /// only if a hand with the given id releases an object it was holding.
+ ///
+ public bool ReleaseHand(int handId) {
+ InteractionHand interactionHand;
+ if (!_idToInteractionHand.TryGetValue(handId, out interactionHand)) {
+ return false;
+ }
+
+ if (interactionHand.graspedObject == null) {
+ return false;
+ }
+
+ if (interactionHand.graspedObject.GraspingHandCount == 1) {
+ _graspedBehaviours.Remove(interactionHand.graspedObject);
+ }
+
+ interactionHand.ReleaseObject();
+ return true;
+ }
+
+ ///
+ /// Forces a hand to grasp the given interaction behaviour. The grasp will only be terminated when
+ /// the hand either times out or the user calls ReleaseHand.
+ ///
+ ///
+ public void GraspWithHand(Hand hand, IInteractionBehaviour interactionBehaviour) {
+ if (!_activityManager.IsRegistered(interactionBehaviour)) {
+ throw new InvalidOperationException("Cannot grasp " + interactionBehaviour + " because it is not registered with this manager.");
+ }
+
+ InteractionHand interactionHand;
+ if (!_idToInteractionHand.TryGetValue(hand.Id, out interactionHand)) {
+ throw new InvalidOperationException("Hand with id " + hand.Id + " is not registered with this manager.");
+ }
+
+ if (interactionHand.graspedObject != null) {
+ throw new InvalidOperationException("Cannot grasp with hand " + hand.Id + " because that hand is already grasping " + interactionHand.graspedObject);
+ }
+
+ //Ensure behaviour is active already
+ _activityManager.Activate(interactionBehaviour);
+
+ if (!interactionBehaviour.IsBeingGrasped) {
+ _graspedBehaviours.Add(interactionBehaviour);
+ }
+
+ interactionHand.GraspObject(interactionBehaviour, isUserGrasp: true);
+ }
+
+ ///
+ /// Registers an InteractionObject with this manager, which automatically adds the objects
+ /// representation into the internal interaction scene. If the manager is disabled,
+ /// the registration will still succeed and the object will be added to the internal scene
+ /// when the manager is next enabled.
+ ///
+ /// Trying to register a behaviour that is already registered is safe and is a no-op.
+ ///
+ public void RegisterInteractionBehaviour(IInteractionBehaviour interactionBehaviour) {
+ _activityManager.Register(interactionBehaviour);
+ }
+
+ ///
+ /// Unregisters an InteractionObject from this manager. This removes it from the internal
+ /// scene and prevents any further interaction.
+ ///
+ /// Trying to unregister a behaviour that is not registered is safe and is a no-op.
+ ///
+ public void UnregisterInteractionBehaviour(IInteractionBehaviour interactionBehaviour) {
+ if (_graspedBehaviours.Remove(interactionBehaviour)) {
+ foreach (var interactionHand in _idToInteractionHand.Values) {
+ if (interactionHand.graspedObject == interactionBehaviour) {
+ try {
+ if (interactionHand.isUntracked) {
+ interactionHand.MarkTimeout();
+ } else {
+ interactionHand.ReleaseObject();
+ }
+ } catch (Exception e) {
+ //Only log to console
+ //We want to continue so we can destroy the shape and dispatch OnUnregister
+ Debug.LogException(e);
+ }
+ break;
+ }
+ }
+ }
+
+ _activityManager.Unregister(interactionBehaviour);
+ }
+
+ public void EnsureActive(IInteractionBehaviour interactionBehaviour) {
+ if (!_activityManager.IsActive(interactionBehaviour)) {
+ _activityManager.Activate(interactionBehaviour);
+ }
+ }
+
+ #endregion
+
+ #region UNITY CALLBACKS
+ protected virtual void Reset() {
+ if (_leapProvider == null) {
+ _leapProvider = FindObjectOfType();
+ }
+ }
+
+ protected virtual void OnValidate() {
+ if (Application.isPlaying && _hasSceneBeenCreated) {
+ //Allow the debug lines to be toggled while the scene is playing
+ applyDebugSettings();
+ //Allow scene info to be updated while the scene is playing
+ UpdateSceneInfo();
+ }
+
+ if (!Application.isPlaying && _autoGenerateLayers) {
+ autoGenerateLayers();
+ }
+
+ _activationRadius = Mathf.Max(0, _activationRadius);
+ _maxActivationDepth = Mathf.Max(1, _maxActivationDepth);
+
+ if (_activityManager != null) {
+ _activityManager.OverlapRadius = _activationRadius;
+ _activityManager.MaxDepth = _maxActivationDepth;
+ }
+ }
+
+ protected virtual void Awake() {
+ if (_autoGenerateLayers) {
+ autoGenerateLayers();
+ autoSetupCollisionLayers();
+ }
+ }
+
+ protected virtual void OnEnable() {
+ if (_leapProvider == null) {
+ enabled = false;
+ Debug.LogError("Could not enable Interaction Manager because no Leap Provider was specified.");
+ return;
+ }
+
+ Assert.IsFalse(_hasSceneBeenCreated, "Scene should not have been created yet");
+
+ createScene();
+ applyDebugSettings();
+
+ _shapeDescriptionPool = new ShapeDescriptionPool(_scene);
+
+ Assert.AreEqual(_instanceHandleToBehaviour.Count, 0, "There should not be any instances before the creation step.");
+
+ _cachedSimulationScale = _leapProvider.transform.lossyScale.x;
+
+ _activityManager.BrushLayer = InteractionBrushLayer;
+ _activityManager.OverlapRadius = _activationRadius * _cachedSimulationScale;
+ _activityManager.MaxDepth = _maxActivationDepth;
+ _activityManager.OnActivate += createInteractionShape;
+ _activityManager.OnDeactivate += destroyInteractionShape;
+
+ if (_handPool != null) {
+ if (_contactEnabled) {
+ _handPool.EnableGroup(_brushGroupName);
+ } else {
+ _handPool.DisableGroup(_brushGroupName);
+ }
+ }
+ }
+
+ protected virtual void OnDisable() {
+ for (int i = _graspedBehaviours.Count; i-- != 0;) {
+ ReleaseObject(_graspedBehaviours[i]);
+ }
+
+ _activityManager.UnregisterMisbehavingObjects();
+
+ _idToInteractionHand.Clear();
+ _graspedBehaviours.Clear();
+
+ _activityManager.DeactivateAll();
+ _activityManager.OnActivate -= createInteractionShape;
+ _activityManager.OnDeactivate -= destroyInteractionShape;
+
+ Assert.AreEqual(_instanceHandleToBehaviour.Count, 0, "All instances should have been destroyed.");
+
+ if (_shapeDescriptionPool != null) {
+ _shapeDescriptionPool.RemoveAllShapes();
+ _shapeDescriptionPool = null;
+ }
+
+ if (_hasSceneBeenCreated) {
+ destroyScene();
+ }
+ }
+
+ protected virtual void FixedUpdate() {
+ Frame frame = _leapProvider.CurrentFixedFrame;
+
+ if (OnPrePhysicalUpdate != null) {
+ OnPrePhysicalUpdate();
+ }
+
+ simulateFrame(frame);
+
+ if (OnPostPhysicalUpdate != null) {
+ OnPostPhysicalUpdate();
+ }
+
+ if (_showDebugLines) {
+ RuntimeGizmoDrawer gizmoDrawer;
+ if (RuntimeGizmoManager.TryGetGizmoDrawer(gameObject, out gizmoDrawer)) {
+ InteractionC.GetDebugLines(ref _scene, _debugLines);
+ for (int i = 0; i < _debugLines.Count; i++) {
+ var line = _debugLines[i];
+ gizmoDrawer.color = line.color.ToUnityColor();
+ gizmoDrawer.DrawLine(line.start.ToVector3(), line.end.ToVector3());
+ }
+ }
+ }
+
+ if (_showDebugOutput) {
+ InteractionC.GetDebugStrings(ref _scene, _debugOutput);
+ }
+ }
+
+ protected virtual void LateUpdate() {
+ Frame frame = _leapProvider.CurrentFrame;
+
+ dispatchOnHandsHoldingAll(frame, isPhysics: false);
+
+ _activityManager.UnregisterMisbehavingObjects();
+
+ if (OnGraphicalUpdate != null) {
+ OnGraphicalUpdate();
+ }
+
+ if (_showDebugOutput && _debugTextView != null) {
+ string text = "";
+ for (int i = 0; i < _debugOutput.Count; i++) {
+ text += _debugOutput[i];
+ if (i != _debugOutput.Count - 1) {
+ text += "\n";
+ }
+ }
+ _debugTextView.text = text;
+ }
+
+ if (_automaticValidation) {
+ Validate();
+ }
+ }
+
+ protected virtual void OnGUI() {
+ if (_showDebugOutput) {
+ for (int i = 0; i < _debugOutput.Count; i++) {
+ GUILayout.Label(_debugOutput[i]);
+ }
+ }
+ }
+ #endregion
+
+ #region INTERNAL METHODS
+
+ protected void autoGenerateLayers() {
+ _interactionLayer = -1;
+ _interactionNoClipLayer = -1;
+ _brushLayer = -1;
+ for (int i = 8; i < 32; i++) {
+ string layerName = LayerMask.LayerToName(i);
+ if (string.IsNullOrEmpty(layerName)) {
+ if (_interactionLayer == -1) {
+ _interactionLayer = i;
+ } else if (_interactionNoClipLayer == -1) {
+ _interactionNoClipLayer = i;
+ } else if (_brushLayer == -1) {
+ _brushLayer = i;
+ break;
+ }
+ }
+ }
+
+ if (_interactionLayer == -1 || _interactionNoClipLayer == -1 || _brushLayer == -1) {
+ if (Application.isPlaying) {
+ enabled = false;
+ }
+ Debug.LogError("InteractionManager Could not find enough free layers for auto-setup, manual setup required.");
+ _autoGenerateLayers = false;
+ return;
+ }
+ }
+
+ private void autoSetupCollisionLayers() {
+ for (int i = 0; i < 32; i++) {
+ // Copy ignore settings from template layer
+ bool shouldIgnore = Physics.GetIgnoreLayerCollision(_templateLayer, i);
+ Physics.IgnoreLayerCollision(_interactionLayer, i, shouldIgnore);
+ Physics.IgnoreLayerCollision(_interactionNoClipLayer, i, shouldIgnore);
+
+ // Set brush layer to collide with nothing
+ Physics.IgnoreLayerCollision(_brushLayer, i, true);
+ }
+
+ //After copy and set we enable the interaction between the brushes and interaction objects
+ Physics.IgnoreLayerCollision(_brushLayer, _interactionLayer, false);
+ }
+
+ protected virtual void simulateFrame(Frame frame) {
+ _activityManager.UpdateState(frame);
+
+ var active = _activityManager.ActiveBehaviours;
+
+ for (int i = 0; i < active.Count; i++) {
+ active[i].NotifyPreSolve();
+ }
+
+ dispatchOnHandsHoldingAll(frame, isPhysics: true);
+
+ updateInteractionRepresentations();
+
+ updateTracking(frame);
+
+ simulateInteraction();
+
+ updateInteractionStateChanges(frame);
+
+ dispatchSimulationResults();
+
+ for (int i = 0; i < active.Count; i++) {
+ active[i].NotifyPostSolve();
+ }
+ }
+
+ protected virtual void applyDebugSettings() {
+ InteractionC.EnableDebugFlags(ref _scene, (uint)DebugFlags);
+ }
+
+ protected virtual void updateInteractionRepresentations() {
+ var active = _activityManager.ActiveBehaviours;
+ for (int i = 0; i < active.Count; i++) {
+ IInteractionBehaviour interactionBehaviour = active[i];
+ try {
+ INTERACTION_SHAPE_INSTANCE_HANDLE shapeInstanceHandle = interactionBehaviour.ShapeInstanceHandle;
+
+ INTERACTION_UPDATE_SHAPE_INFO updateInfo;
+ INTERACTION_TRANSFORM updateTransform;
+ interactionBehaviour.GetInteractionShapeUpdateInfo(out updateInfo, out updateTransform);
+
+ InteractionC.UpdateShapeInstance(ref _scene, ref updateTransform, ref updateInfo, ref shapeInstanceHandle);
+ } catch (Exception e) {
+ _activityManager.NotifyMisbehaving(interactionBehaviour);
+ Debug.LogException(e);
+ }
+ }
+ }
+
+ protected virtual void dispatchOnHandsHoldingAll(Frame frame, bool isPhysics) {
+ if (frame == null) return; // @Hyro
+
+ var hands = frame.Hands;
+ //Loop through the currently grasped objects to dispatch their OnHandsHold callback
+ for (int i = 0; i < _graspedBehaviours.Count; i++) {
+ dispatchOnHandsHolding(hands, _graspedBehaviours[i], isPhysics);
+ }
+ }
+
+ protected virtual void dispatchOnHandsHolding(List hands, IInteractionBehaviour interactionBehaviour, bool isPhysics) {
+ for (int j = 0; j < hands.Count; j++) {
+ var hand = hands[j];
+ InteractionHand interactionHand;
+ if (_idToInteractionHand.TryGetValue(hand.Id, out interactionHand)) {
+ if (interactionHand.graspedObject == interactionBehaviour) {
+ _holdingHands.Add(hand);
+ }
+ }
+ }
+
+ try {
+ if (isPhysics) {
+ interactionBehaviour.NotifyHandsHoldPhysics(_holdingHands);
+ } else {
+ interactionBehaviour.NotifyHandsHoldGraphics(_holdingHands);
+ }
+ } catch (Exception e) {
+ _activityManager.NotifyMisbehaving(interactionBehaviour);
+ Debug.LogException(e);
+ }
+
+ _holdingHands.Clear();
+ }
+
+ protected virtual void updateTracking(Frame frame) {
+ int handCount = frame.Hands.Count;
+ IntPtr ptr = HandArrayBuilder.CreateHandArray(frame);
+ InteractionC.UpdateHands(ref _scene, (uint)handCount, ptr);
+ StructAllocator.CleanupAllocations();
+ }
+
+ protected virtual void simulateInteraction() {
+ var _controllerTransform = new INTERACTION_TRANSFORM();
+ _controllerTransform.position = _leapProvider.transform.position.ToCVector();
+ _controllerTransform.rotation = _leapProvider.transform.rotation.ToCQuaternion();
+ _controllerTransform.wallTime = Time.fixedTime;
+
+ InteractionC.UpdateController(ref _scene, ref _controllerTransform);
+ }
+
+ protected virtual void updateInteractionStateChanges(Frame frame) {
+ var hands = frame.Hands;
+
+ INTERACTION_HAND_RESULT handResult = new INTERACTION_HAND_RESULT();
+
+ //First loop through all the hands and get their classifications from the engine
+ for (int i = 0; i < hands.Count; i++) {
+ Hand hand = hands[i];
+
+ bool handResultForced = false;
+
+ //Get the InteractionHand associated with this hand id
+ InteractionHand interactionHand;
+ if (!_idToInteractionHand.TryGetValue(hand.Id, out interactionHand)) {
+
+ //First we see if there is an untracked interactionHand that can be re-connected using this one
+ InteractionHand untrackedInteractionHand = null;
+ foreach (var pair in _idToInteractionHand) {
+ //If the old ieHand is untracked, and the handedness matches, we re-connect it
+ if (pair.Value.isUntracked && pair.Value.hand.IsLeft == hand.IsLeft) {
+ untrackedInteractionHand = pair.Value;
+ break;
+ }
+ }
+
+ if (untrackedInteractionHand != null) {
+ //If we found an untrackedIeHand, use it!
+ interactionHand = untrackedInteractionHand;
+ //Remove the old id from the mapping
+ _idToInteractionHand.Remove(untrackedInteractionHand.hand.Id);
+ _idToInteractionHand[hand.Id] = interactionHand;
+
+ try {
+ //This also dispatched InteractionObject.OnHandRegainedTracking()
+ interactionHand.RegainTracking(hand);
+
+ if (interactionHand.graspedObject == null) {
+ continue;
+ }
+
+ // NotifyHandRegainedTracking() did not throw, continue on to NotifyHandsHoldPhysics().
+ dispatchOnHandsHolding(hands, interactionHand.graspedObject, isPhysics: true);
+ } catch (Exception e) {
+ _activityManager.NotifyMisbehaving(interactionHand.graspedObject);
+ Debug.LogException(e);
+ continue;
+ }
+
+ //Override the existing classification to force the hand to grab the old object
+ handResultForced = true;
+ handResult.classification = ManipulatorMode.Grasp;
+ handResult.handFlags = HandResultFlags.ManipulatorMode;
+ handResult.instanceHandle = interactionHand.graspedObject.ShapeInstanceHandle;
+
+ if (_graspingEnabled) {
+ InteractionC.OverrideHandResult(ref _scene, (uint)hand.Id, ref handResult);
+ }
+ } else {
+ //Otherwise just create a new one
+ interactionHand = new InteractionHand(hand);
+ _idToInteractionHand[hand.Id] = interactionHand;
+ }
+ }
+
+ if (!handResultForced) {
+ handResult = getHandResults(interactionHand);
+ }
+
+ interactionHand.UpdateHand(hand);
+
+ if (!interactionHand.isUserGrasp) {
+ switch (handResult.classification) {
+ case ManipulatorMode.Grasp:
+ {
+ IInteractionBehaviour interactionBehaviour;
+ if (_instanceHandleToBehaviour.TryGetValue(handResult.instanceHandle, out interactionBehaviour)) {
+ if (interactionHand.graspedObject == null) {
+ if (!interactionBehaviour.IsBeingGrasped) {
+ _graspedBehaviours.Add(interactionBehaviour);
+ }
+
+ try {
+ interactionHand.GraspObject(interactionBehaviour, isUserGrasp: false);
+
+ //the grasp callback might have caused the object to become ungrasped
+ //the component might have also destroyed itself!
+ if (interactionHand.graspedObject == interactionBehaviour && interactionBehaviour != null) {
+ dispatchOnHandsHolding(hands, interactionBehaviour, isPhysics: true);
+ }
+ } catch (Exception e) {
+ _activityManager.NotifyMisbehaving(interactionBehaviour);
+ Debug.LogException(e);
+ continue;
+ }
+ }
+ } else {
+ Debug.LogError("Recieved a hand result with an unkown handle " + handResult.instanceHandle.handle);
+ }
+ break;
+ }
+ case ManipulatorMode.Contact:
+ {
+ if (interactionHand.graspedObject != null) {
+ if (interactionHand.graspedObject.GraspingHandCount == 1) {
+ _graspedBehaviours.Remove(interactionHand.graspedObject);
+ }
+
+ try {
+ interactionHand.ReleaseObject();
+ } catch (Exception e) {
+ _activityManager.NotifyMisbehaving(interactionHand.graspedObject);
+ Debug.LogException(e);
+ continue;
+ }
+ }
+ break;
+ }
+ default:
+ throw new InvalidOperationException("Unexpected classification " + handResult.classification);
+ }
+ }
+ }
+
+ //Loop through all ieHands to check for timeouts and loss of tracking
+ foreach (var pair in _idToInteractionHand) {
+ var id = pair.Key;
+ var ieHand = pair.Value;
+
+ float handAge = Time.unscaledTime - ieHand.lastTimeUpdated;
+ //Check to see if the hand is at least 1 frame old
+ //We assume it has become untracked if this is the case
+ if (handAge > 0) {
+ //If the hand isn't grasping anything, just remove it
+ if (ieHand.graspedObject == null) {
+ _handIdsToRemove.Add(id);
+ continue;
+ }
+
+ //If is isn't already marked as untracked, mark it as untracked
+ if (!ieHand.isUntracked) {
+ try {
+ //This also dispatches InteractionObject.OnHandLostTracking()
+ ieHand.MarkUntracked();
+ } catch (Exception e) {
+ _activityManager.NotifyMisbehaving(ieHand.graspedObject);
+ Debug.LogException(e);
+ }
+ }
+
+ //If the age is longer than the timeout, we also remove it from the list
+ if (handAge >= ieHand.maxSuspensionTime) {
+ _handIdsToRemove.Add(id);
+
+ try {
+ if (ieHand.graspedObject.GraspingHandCount == 1) {
+ _graspedBehaviours.Remove(ieHand.graspedObject);
+ }
+
+ //This also dispatched InteractionObject.OnHandTimeout()
+ ieHand.MarkTimeout();
+ } catch (Exception e) {
+ _activityManager.NotifyMisbehaving(ieHand.graspedObject);
+ Debug.LogException(e);
+ }
+ }
+ }
+ }
+
+ //Loop through the stale ids and remove them from the map
+ for (int i = 0; i < _handIdsToRemove.Count; i++) {
+ _idToInteractionHand.Remove(_handIdsToRemove[i]);
+ }
+ _handIdsToRemove.Clear();
+ }
+
+ protected virtual INTERACTION_HAND_RESULT getHandResults(InteractionHand hand) {
+ if (!_graspingEnabled) {
+ INTERACTION_HAND_RESULT result = new INTERACTION_HAND_RESULT();
+ result.classification = ManipulatorMode.Contact;
+ result.handFlags = HandResultFlags.ManipulatorMode;
+ result.instanceHandle = new INTERACTION_SHAPE_INSTANCE_HANDLE();
+ return result;
+ }
+
+ INTERACTION_HAND_RESULT handResult;
+ InteractionC.GetHandResult(ref _scene,
+ (uint)hand.hand.Id,
+ out handResult);
+ return handResult;
+ }
+
+ protected virtual void dispatchSimulationResults() {
+ InteractionC.GetShapeInstanceResults(ref _scene, _resultList);
+
+ for (int i = 0; i < _resultList.Count; ++i) {
+ INTERACTION_SHAPE_INSTANCE_RESULTS result = _resultList[i];
+
+ //Behaviour might have already been unregistered during an earlier callback for this simulation step
+ IInteractionBehaviour interactionBehaviour;
+ if (_instanceHandleToBehaviour.TryGetValue(result.handle, out interactionBehaviour)) {
+ try {
+ // ShapeInstanceResultFlags.None may be returned if requested when hands are not touching.
+ interactionBehaviour.NotifyRecievedSimulationResults(result);
+ } catch (Exception e) {
+ _activityManager.NotifyMisbehaving(interactionBehaviour);
+ Debug.LogException(e);
+ }
+ }
+ }
+ }
+
+ protected virtual void createInteractionShape(IInteractionBehaviour interactionBehaviour) {
+ INTERACTION_SHAPE_DESCRIPTION_HANDLE descriptionHandle = interactionBehaviour.ShapeDescriptionHandle;
+ INTERACTION_SHAPE_INSTANCE_HANDLE instanceHandle = new INTERACTION_SHAPE_INSTANCE_HANDLE();
+
+ INTERACTION_CREATE_SHAPE_INFO createInfo;
+ INTERACTION_TRANSFORM createTransform;
+ interactionBehaviour.GetInteractionShapeCreationInfo(out createInfo, out createTransform);
+
+ InteractionC.CreateShapeInstance(ref _scene, ref descriptionHandle, ref createTransform, ref createInfo, out instanceHandle);
+
+ _instanceHandleToBehaviour[instanceHandle] = interactionBehaviour;
+
+ interactionBehaviour.NotifyInteractionShapeCreated(instanceHandle);
+ }
+
+ protected virtual void destroyInteractionShape(IInteractionBehaviour interactionBehaviour) {
+ INTERACTION_SHAPE_INSTANCE_HANDLE instanceHandle = interactionBehaviour.ShapeInstanceHandle;
+
+ _instanceHandleToBehaviour.Remove(instanceHandle);
+
+ InteractionC.DestroyShapeInstance(ref _scene, ref instanceHandle);
+
+ interactionBehaviour.NotifyInteractionShapeDestroyed();
+ }
+
+ protected virtual void createScene() {
+ _scene.pScene = (IntPtr)0;
+
+ UInt32 libraryVersion = InteractionC.GetLibraryVersion();
+ UInt32 expectedVersion = InteractionC.GetExpectedVersion();
+ if (libraryVersion != expectedVersion) {
+ Debug.LogError("Leap Interaction dll version expected: " + expectedVersion + " got version: " + libraryVersion);
+ throw new Exception("Leap Interaction library version wrong");
+ }
+
+ InteractionC.CreateScene(ref _scene);
+ _hasSceneBeenCreated = true;
+
+ UpdateSceneInfo();
+ }
+
+ protected virtual void destroyScene() {
+ InteractionC.DestroyScene(ref _scene);
+ _hasSceneBeenCreated = false;
+ }
+
+ protected virtual INTERACTION_SCENE_INFO getSceneInfo() {
+ INTERACTION_SCENE_INFO info = new INTERACTION_SCENE_INFO();
+ info.sceneFlags = SceneInfoFlags.None;
+
+ if (Physics.gravity.sqrMagnitude != 0.0f) {
+ info.sceneFlags |= SceneInfoFlags.HasGravity;
+ info.gravity = Physics.gravity.ToCVector();
+ }
+
+ if (_depthUntilSphericalInside > 0.0f) {
+ info.sceneFlags |= SceneInfoFlags.SphericalInside;
+ info.depthUntilSphericalInside = _depthUntilSphericalInside;
+ }
+
+ if (_contactEnabled) {
+ info.sceneFlags |= SceneInfoFlags.ContactEnabled;
+ }
+
+ // _enableGraspingLast gaurds against expensive file IO. Only load the ldat
+ // data when grasping is being enabled.
+ if (_graspingEnabled) {
+ info.sceneFlags |= SceneInfoFlags.GraspEnabled;
+ }
+
+ return info;
+ }
+
+ //A persistant structure for storing useful data about a hand as it interacts with objects
+ //TODO: Investigate pooling?
+ protected partial class InteractionHand {
+ public Hand hand { get; protected set; }
+ public float lastTimeUpdated { get; protected set; }
+ public float maxSuspensionTime { get; protected set; }
+ public IInteractionBehaviour graspedObject { get; protected set; }
+ public bool isUntracked { get; protected set; }
+ public bool isUserGrasp { get; protected set; }
+
+ public InteractionHand(Hand hand) {
+ this.hand = new Hand().CopyFrom(hand);
+ lastTimeUpdated = Time.unscaledTime;
+ graspedObject = null;
+ }
+
+ public void UpdateHand(Hand hand) {
+ this.hand.CopyFrom(hand);
+ lastTimeUpdated = Time.unscaledTime;
+ }
+
+ public void GraspObject(IInteractionBehaviour obj, bool isUserGrasp) {
+ this.isUserGrasp = isUserGrasp;
+ graspedObject = obj;
+ graspedObject.NotifyHandGrasped(hand);
+ }
+
+ public void ReleaseObject() {
+ graspedObject.NotifyHandReleased(hand);
+ graspedObject = null;
+ isUntracked = false;
+ isUserGrasp = false;
+ }
+
+ public void MarkUntracked() {
+ isUntracked = true;
+ float outTime;
+ graspedObject.NotifyHandLostTracking(hand, out outTime);
+ maxSuspensionTime = outTime;
+ }
+
+ public void MarkTimeout() {
+ graspedObject.NotifyHandTimeout(hand);
+ graspedObject = null;
+ isUntracked = true;
+ isUserGrasp = false;
+ hand = null;
+ }
+
+ public void RegainTracking(Hand newHand) {
+ int oldId = hand.Id;
+ UpdateHand(newHand);
+
+ isUntracked = false;
+ graspedObject.NotifyHandRegainedTracking(newHand, oldId);
+ }
+ }
+ #endregion
+ }
}
diff --git a/Assets/Materials/Blue.mat b/Assets/Materials/Blue.mat
new file mode 100644
index 0000000..a8841fb
Binary files /dev/null and b/Assets/Materials/Blue.mat differ
diff --git a/Assets/Prefabs/Camera 2.prefab.meta b/Assets/Materials/Blue.mat.meta
similarity index 65%
rename from Assets/Prefabs/Camera 2.prefab.meta
rename to Assets/Materials/Blue.mat.meta
index 6aa9f22..106b99a 100644
--- a/Assets/Prefabs/Camera 2.prefab.meta
+++ b/Assets/Materials/Blue.mat.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: 61cc92642ab6a4c418ce2ea33674c6d1
-timeCreated: 1475592569
+guid: 849f200d547021a4599b4e3b6bfadb13
+timeCreated: 1486909065
licenseType: Free
NativeFormatImporter:
userData:
diff --git a/Assets/Materials/Green.mat b/Assets/Materials/Green.mat
new file mode 100644
index 0000000..83b9d54
Binary files /dev/null and b/Assets/Materials/Green.mat differ
diff --git a/Assets/Prefabs/Camera 1.prefab.meta b/Assets/Materials/Green.mat.meta
similarity index 65%
rename from Assets/Prefabs/Camera 1.prefab.meta
rename to Assets/Materials/Green.mat.meta
index 76f17cd..47a6554 100644
--- a/Assets/Prefabs/Camera 1.prefab.meta
+++ b/Assets/Materials/Green.mat.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: d7ee9a117f456b14ca6891534caaf66d
-timeCreated: 1474922739
+guid: 94dd74d329d6b2546b91b3de5842f823
+timeCreated: 1486909047
licenseType: Free
NativeFormatImporter:
userData:
diff --git a/Assets/Materials/PlayerColor.mat b/Assets/Materials/PlayerColor.mat
new file mode 100644
index 0000000..2227c76
Binary files /dev/null and b/Assets/Materials/PlayerColor.mat differ
diff --git a/Assets/Prefabs/Camera.prefab.meta b/Assets/Materials/PlayerColor.mat.meta
similarity index 65%
rename from Assets/Prefabs/Camera.prefab.meta
rename to Assets/Materials/PlayerColor.mat.meta
index 7d841ba..ebe5720 100644
--- a/Assets/Prefabs/Camera.prefab.meta
+++ b/Assets/Materials/PlayerColor.mat.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: 156da23fb1ce3154e91aade46eb31259
-timeCreated: 1474921314
+guid: e7c8197ccda909b4585bcb4ac8633cd6
+timeCreated: 1487516646
licenseType: Free
NativeFormatImporter:
userData:
diff --git a/Assets/Materials/Red.mat b/Assets/Materials/Red.mat
index 478dc24..5f677a6 100644
Binary files a/Assets/Materials/Red.mat and b/Assets/Materials/Red.mat differ
diff --git a/Assets/Prefabs/Camera 1.prefab b/Assets/Prefabs/Camera 1.prefab
deleted file mode 100644
index a688e7d..0000000
Binary files a/Assets/Prefabs/Camera 1.prefab and /dev/null differ
diff --git a/Assets/Prefabs/Camera 2 1.prefab b/Assets/Prefabs/Camera 2 1.prefab
deleted file mode 100644
index 2db6e9f..0000000
Binary files a/Assets/Prefabs/Camera 2 1.prefab and /dev/null differ
diff --git a/Assets/Prefabs/Camera 2.prefab b/Assets/Prefabs/Camera 2.prefab
deleted file mode 100644
index 61dbe96..0000000
Binary files a/Assets/Prefabs/Camera 2.prefab and /dev/null differ
diff --git a/Assets/Prefabs/Camera.prefab b/Assets/Prefabs/Camera.prefab
deleted file mode 100644
index c80c959..0000000
Binary files a/Assets/Prefabs/Camera.prefab and /dev/null differ
diff --git a/Assets/Prefabs/CapsuleHand_L.prefab b/Assets/Prefabs/CapsuleHand_L.prefab
deleted file mode 100644
index 8e8462e..0000000
Binary files a/Assets/Prefabs/CapsuleHand_L.prefab and /dev/null differ
diff --git a/Assets/Prefabs/CapsuleHand_L.prefab.meta b/Assets/Prefabs/CapsuleHand_L.prefab.meta
deleted file mode 100644
index 0c8522f..0000000
--- a/Assets/Prefabs/CapsuleHand_L.prefab.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 2b037574bfbbe4744975691ea214acbd
-timeCreated: 1486828017
-licenseType: Free
-NativeFormatImporter:
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Prefabs/CapsuleHand_R.prefab b/Assets/Prefabs/CapsuleHand_R.prefab
deleted file mode 100644
index d30009b..0000000
Binary files a/Assets/Prefabs/CapsuleHand_R.prefab and /dev/null differ
diff --git a/Assets/Prefabs/CapsuleHand_R.prefab.meta b/Assets/Prefabs/CapsuleHand_R.prefab.meta
deleted file mode 100644
index 78e008b..0000000
--- a/Assets/Prefabs/CapsuleHand_R.prefab.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: bef07a2347620974fb9dad0e4f5d8c52
-timeCreated: 1486828177
-licenseType: Free
-NativeFormatImporter:
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Prefabs/PlayerCam.prefab b/Assets/Prefabs/Cube.prefab
similarity index 56%
rename from Assets/Prefabs/PlayerCam.prefab
rename to Assets/Prefabs/Cube.prefab
index 61d4801..eb62e91 100644
Binary files a/Assets/Prefabs/PlayerCam.prefab and b/Assets/Prefabs/Cube.prefab differ
diff --git a/Assets/Prefabs/Camera 2 1.prefab.meta b/Assets/Prefabs/Cube.prefab.meta
similarity index 65%
rename from Assets/Prefabs/Camera 2 1.prefab.meta
rename to Assets/Prefabs/Cube.prefab.meta
index fd02714..a1e1be2 100644
--- a/Assets/Prefabs/Camera 2 1.prefab.meta
+++ b/Assets/Prefabs/Cube.prefab.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: c876153b102d1b047913eb5c56abd7e6
-timeCreated: 1475682244
+guid: 50a1d10f81d128545aa37939f519242f
+timeCreated: 1486896769
licenseType: Free
NativeFormatImporter:
userData:
diff --git a/Assets/Prefabs/LeapPrefab.prefab b/Assets/Prefabs/LeapPrefab.prefab
deleted file mode 100644
index 90616f3..0000000
Binary files a/Assets/Prefabs/LeapPrefab.prefab and /dev/null differ
diff --git a/Assets/Prefabs/LeapPrefab.prefab.meta b/Assets/Prefabs/LeapPrefab.prefab.meta
deleted file mode 100644
index fe6e0c9..0000000
--- a/Assets/Prefabs/LeapPrefab.prefab.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 840a966d91e98c742822f41db61ce8d8
-timeCreated: 1486824316
-licenseType: Free
-NativeFormatImporter:
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Prefabs/Player 1.prefab b/Assets/Prefabs/Player 1.prefab
index 4fe5947..d34efe1 100644
Binary files a/Assets/Prefabs/Player 1.prefab and b/Assets/Prefabs/Player 1.prefab differ
diff --git a/Assets/Prefabs/Player.prefab b/Assets/Prefabs/Player.prefab
deleted file mode 100644
index ee0ca72..0000000
Binary files a/Assets/Prefabs/Player.prefab and /dev/null differ
diff --git a/Assets/Prefabs/Player.prefab.meta b/Assets/Prefabs/Player.prefab.meta
deleted file mode 100644
index 0f0926a..0000000
--- a/Assets/Prefabs/Player.prefab.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: fc7f9c336cdbec84a815978510b8fbfe
-timeCreated: 1476451825
-licenseType: Free
-NativeFormatImporter:
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Prefabs/PlayerCam.prefab.meta b/Assets/Prefabs/PlayerCam.prefab.meta
deleted file mode 100644
index f31a27b..0000000
--- a/Assets/Prefabs/PlayerCam.prefab.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 4e819790e0fd93a4882c334ccabefdf8
-timeCreated: 1486824328
-licenseType: Free
-NativeFormatImporter:
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scenes/Game.unity b/Assets/Scenes/Game.unity
deleted file mode 100644
index fca0342..0000000
Binary files a/Assets/Scenes/Game.unity and /dev/null differ
diff --git a/Assets/Scenes/Game.unity.meta b/Assets/Scenes/Game.unity.meta
deleted file mode 100644
index 44f6ff4..0000000
--- a/Assets/Scenes/Game.unity.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 814b14891c712634d9e80dface97f8dc
-timeCreated: 1476451470
-licenseType: Free
-DefaultImporter:
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity
index e6f3cd5..49e7698 100644
Binary files a/Assets/Scenes/Main.unity and b/Assets/Scenes/Main.unity differ
diff --git a/Assets/Scenes/Menu.unity b/Assets/Scenes/Menu.unity
deleted file mode 100644
index d9f4d8c..0000000
Binary files a/Assets/Scenes/Menu.unity and /dev/null differ
diff --git a/Assets/Scenes/Menu.unity.meta b/Assets/Scenes/Menu.unity.meta
deleted file mode 100644
index 9a7b533..0000000
--- a/Assets/Scenes/Menu.unity.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 7988906c94fe302498f0435489901905
-timeCreated: 1476451330
-licenseType: Free
-DefaultImporter:
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/CubeSpawner.cs b/Assets/Scripts/CubeSpawner.cs
new file mode 100644
index 0000000..dc84f3b
--- /dev/null
+++ b/Assets/Scripts/CubeSpawner.cs
@@ -0,0 +1,51 @@
+using UnityEngine;
+using UnityEngine.Networking;
+
+public class CubeSpawner : NetworkBehaviour {
+
+ [SerializeField]
+ Vector3 spawnPosition;
+
+ [SerializeField]
+ GameObject prefab;
+
+ //public override void OnStartServer() {
+ //NetworkServer.Spawn(Instantiate(prefab, t.position, t.rotation));
+ //NetworkIdentity.AssignClientAuthority();
+ //NetworkServer.SpawnWithClientAuthority(Instantiate(prefab, t.position, t.rotation),
+ // NetworkServer.connections[0].
+ //}
+
+ void Update() {
+ if (isLocalPlayer && Input.GetKeyDown(KeyCode.Space)) {
+ CmdSpawn();
+ }
+ }
+
+ [Command]
+ public void CmdSpawn() {
+ GameObject go = (GameObject)Instantiate(prefab, transform.position + transform.forward * .25f, Quaternion.identity);
+ // NetworkServer.SpawnWithClientAuthority(go, connectionToClient);
+ NetworkServer.Spawn(go);
+ }
+
+ public void SetAuthority(NetworkIdentity ni) {
+ CmdSetAuthority(ni);
+ }
+
+ [Command]
+ void CmdSetAuthority(NetworkIdentity grabID) {
+ if (grabID.clientAuthorityOwner != null)
+ grabID.RemoveClientAuthority(grabID.clientAuthorityOwner);
+ grabID.AssignClientAuthority(connectionToClient);
+ }
+
+ public void RemoveAuthority(NetworkIdentity ni) {
+ CmdRemoveAuthority(ni);
+ }
+
+ [Command]
+ void CmdRemoveAuthority(NetworkIdentity grabID) {
+ grabID.RemoveClientAuthority(grabID.clientAuthorityOwner);
+ }
+}
diff --git a/Assets/Scripts/CubeSpawner.cs.meta b/Assets/Scripts/CubeSpawner.cs.meta
new file mode 100644
index 0000000..b443f41
--- /dev/null
+++ b/Assets/Scripts/CubeSpawner.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 947727ead3fe2c341bbc7186beaa886c
+timeCreated: 1486896793
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/CustomHand.cs b/Assets/Scripts/CustomHand.cs
index 467f589..991cb87 100644
--- a/Assets/Scripts/CustomHand.cs
+++ b/Assets/Scripts/CustomHand.cs
@@ -70,6 +70,10 @@ public override Chirality Handedness {
set { }
}
+ public Color PlayerColor {
+ get; set;
+ }
+
public override bool SupportsEditorPersistence() {
return true;
}
@@ -121,7 +125,6 @@ public override void InitHand() {
BuildSpheres();
BuildCylinders();
updateArmVisibility();
-
_hasGeneratedMeshes = false;
}
@@ -249,7 +252,7 @@ private void BuildSpheres() {
Transform[] joints = GetComponentsInChildren().Where(c => c.name == "Joint").ToArray();
int k = 0;
-
+ //PlayerColor = new Color(UnityEngine.Random.Range(0f, 1), UnityEngine.Random.Range(0f, 1), UnityEngine.Random.Range(0f, 1));
//Create spheres for finger joints
List fingers = hand_.Fingers;
for (int i = 0; i < fingers.Count; i++) {
@@ -257,13 +260,20 @@ private void BuildSpheres() {
for (int j = 0; j < 4; j++) {
int key = getFingerJointIndex((int)finger.Type, j);
_jointSpheres[key] = joints[k++]; /*createSphere("Joint", SPHERE_RADIUS);*/
+ _jointSpheres[key].GetComponent().material = _material;
+ _jointSpheres[key].GetComponent().material.color = PlayerColor;
}
}
mockThumbJointSphere = transform.FindChild("MockJoint");//createSphere("MockJoint", SPHERE_RADIUS);
palmPositionSphere = transform.FindChild("PalmPosition");//createSphere("PalmPosition", PALM_RADIUS);
wristPositionSphere = transform.FindChild("WristPosition");//createSphere("WristPosition", SPHERE_RADIUS);
-
+ mockThumbJointSphere.GetComponent().material = _material;
+ palmPositionSphere.GetComponent().material = _material;
+ wristPositionSphere.GetComponent().material = _material;
+ mockThumbJointSphere.GetComponent().material.color = PlayerColor;
+ palmPositionSphere.GetComponent().material.color = PlayerColor;
+ wristPositionSphere.GetComponent().material.color = PlayerColor;
//armFrontLeft = createSphere("ArmFrontLeft", SPHERE_RADIUS, true);
//armFrontRight = createSphere("ArmFrontRight", SPHERE_RADIUS, true);
//armBackLeft = createSphere("ArmBackLeft", SPHERE_RADIUS, true);
@@ -274,7 +284,7 @@ private void BuildCylinders() {
Transform[] fingerJoints = GetComponentsInChildren().Where(c => c.name == "Finger Joint").ToArray();
int k = 0;
-
+
//Create cylinders between finger joints
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
diff --git a/Assets/Scripts/EnableOnLocalPlayer.cs b/Assets/Scripts/EnableOnLocalPlayer.cs
index 1ac7c3a..c7903ad 100644
--- a/Assets/Scripts/EnableOnLocalPlayer.cs
+++ b/Assets/Scripts/EnableOnLocalPlayer.cs
@@ -1,6 +1,4 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
+using UnityEngine;
using UnityEngine.Networking;
public class EnableOnLocalPlayer : NetworkBehaviour {
@@ -9,9 +7,19 @@ public class EnableOnLocalPlayer : NetworkBehaviour {
GameObject leapController;
[SerializeField]
Camera playerCam;
+ [SerializeField]
+ GameObject[] rigidHands;
+ [SerializeField]
+ LinkHandSpheres[] linkHandSpheres;
public override void OnStartLocalPlayer() {
leapController.SetActive(true);
playerCam.enabled = true;
+ foreach(var rigidHand in rigidHands) {
+ rigidHand.SetActive(true);
+ }
+ foreach (var link in linkHandSpheres) {
+ link.enabled = false;
+ }
}
}
diff --git a/Assets/Scripts/LinkHandSpheres.cs b/Assets/Scripts/LinkHandSpheres.cs
new file mode 100644
index 0000000..3e1a6cb
--- /dev/null
+++ b/Assets/Scripts/LinkHandSpheres.cs
@@ -0,0 +1,182 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using UnityEngine;
+using UnityEngine.Networking;
+
+public class LinkHandSpheres : NetworkBehaviour {
+
+ private const float CYLINDER_RADIUS = 0.006f;
+ [SerializeField]
+ private int _cylinderResolution = 12;
+
+ private Transform[] _jointSpheres;
+ private List _sphereATransforms;
+ private List _sphereBTransforms;
+ private List _cylinderTransforms;
+ private bool _hasGeneratedMeshes;
+ private Transform mockThumbJointSphere;
+ private Transform palmPositionSphere;
+
+ private Transform wristPositionSphere;
+
+ // Use this for initialization
+ void Awake () {
+ _jointSpheres = new Transform[4 * 5];
+ mockThumbJointSphere = transform.FindChild("MockJoint");
+ palmPositionSphere = transform.FindChild("PalmPosition");
+ wristPositionSphere = transform.FindChild("WristPosition");
+
+ for (int i = 0; i < _jointSpheres.Length; ++i) {
+ _jointSpheres[i] = transform.GetChild(i);
+ }
+ _cylinderTransforms = new List();
+ _sphereATransforms = new List();
+ _sphereBTransforms = new List();
+ _hasGeneratedMeshes = false;
+
+ BuildCylinders();
+ }
+
+ void Update() {
+ updateCylinders();
+ }
+
+ private void updateCylinders() {
+ for (int i = 0; i < _cylinderTransforms.Count; i++) {
+ Transform cylinder = _cylinderTransforms[i];
+ Transform sphereA = _sphereATransforms[i];
+ Transform sphereB = _sphereBTransforms[i];
+ Vector3 delta = sphereA.position - sphereB.position;
+
+ if (!_hasGeneratedMeshes) {
+ MeshFilter filter = cylinder.GetComponent();
+ filter.sharedMesh = generateCylinderMesh(delta.magnitude / transform.lossyScale.x);
+ }
+
+ cylinder.position = sphereA.position;
+
+ if (delta.sqrMagnitude <= Mathf.Epsilon) {
+ //Two spheres are at the same location, no rotation will be found
+ continue;
+ }
+
+ cylinder.LookAt(sphereB);
+ }
+
+ _hasGeneratedMeshes = true;
+ }
+
+ private int getFingerJointIndex(int fingerIndex, int jointIndex) {
+ return fingerIndex * 4 + jointIndex;
+ }
+
+ private void BuildCylinders() {
+
+ Transform[] fingerJoints = GetComponentsInChildren().Where(c => c.name == "Finger Joint").ToArray();
+ int k = 0;
+
+ //Create cylinders between finger joints
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < 3; j++) {
+ int keyA = getFingerJointIndex(i, j);
+ int keyB = getFingerJointIndex(i, j + 1);
+
+ Transform sphereA = _jointSpheres[keyA];
+ Transform sphereB = _jointSpheres[keyB];
+ buildCylinder(sphereA, sphereB, fingerJoints[k++]);
+ //createCylinder("Finger Joint", sphereA, sphereB);
+ }
+ }
+
+ Transform[] handJoints = GetComponentsInChildren().Where(c => c.name == "Hand Joints").ToArray();
+ k = 0;
+ //Create cylinders between finger knuckles
+ for (int i = 0; i < 4; i++) {
+ int keyA = getFingerJointIndex(i, 0);
+ int keyB = getFingerJointIndex(i + 1, 0);
+
+ Transform sphereA = _jointSpheres[keyA];
+ Transform sphereB = _jointSpheres[keyB];
+ buildCylinder(sphereA, sphereB, handJoints[k++]);
+
+ //createCylinder("Hand Joints", sphereA, sphereB);
+ }
+
+ //Create the rest of the hand
+ Transform thumbBase = _jointSpheres[0]; // 0 * 4 = THUMB_BASE_INDEX
+ Transform pinkyBase = _jointSpheres[16]; // 4 * 4 = PINKY_BASE_INDEX
+ buildCylinder(thumbBase, mockThumbJointSphere, transform.FindChild("Hand Bottom"));
+ buildCylinder(pinkyBase, mockThumbJointSphere, transform.FindChild("Hand Side"));
+ //createCylinder("Hand Bottom", thumbBase, mockThumbJointSphere);
+ //createCylinder("Hand Side", pinkyBase, mockThumbJointSphere);
+
+ //createCylinder("ArmFront", armFrontLeft, armFrontRight, true);
+ //createCylinder("ArmBack", armBackLeft, armBackRight, true);
+ //createCylinder("ArmLeft", armFrontLeft, armBackLeft, true);
+ //createCylinder("ArmRight", armFrontRight, armBackRight, true);
+ }
+
+ private void buildCylinder(Transform sphereA, Transform sphereB, Transform joint) {
+ _sphereATransforms.Add(sphereA);
+ _sphereBTransforms.Add(sphereB);
+ _cylinderTransforms.Add(joint);
+
+ }
+
+ private Mesh generateCylinderMesh(float length) {
+ Mesh mesh = new Mesh();
+ mesh.name = "GeneratedCylinder";
+ mesh.hideFlags = HideFlags.DontSave;
+
+ List verts = new List();
+ List colors = new List();
+ List tris = new List();
+
+ Vector3 p0 = Vector3.zero;
+ Vector3 p1 = Vector3.forward * length;
+ for (int i = 0; i < _cylinderResolution; i++) {
+ float angle = (Mathf.PI * 2.0f * i) / _cylinderResolution;
+ float dx = CYLINDER_RADIUS * Mathf.Cos(angle);
+ float dy = CYLINDER_RADIUS * Mathf.Sin(angle);
+
+ Vector3 spoke = new Vector3(dx, dy, 0);
+
+ verts.Add((p0 + spoke) * transform.lossyScale.x);
+ verts.Add((p1 + spoke) * transform.lossyScale.x);
+
+ colors.Add(Color.white);
+ colors.Add(Color.white);
+
+ int triStart = verts.Count;
+ int triCap = _cylinderResolution * 2;
+
+ tris.Add((triStart + 0) % triCap);
+ tris.Add((triStart + 2) % triCap);
+ tris.Add((triStart + 1) % triCap);
+
+ tris.Add((triStart + 2) % triCap);
+ tris.Add((triStart + 3) % triCap);
+ tris.Add((triStart + 1) % triCap);
+ }
+
+ mesh.SetVertices(verts);
+ mesh.SetIndices(tris.ToArray(), MeshTopology.Triangles, 0);
+ mesh.RecalculateBounds();
+ mesh.RecalculateNormals();
+ ;
+ mesh.UploadMeshData(true);
+
+ return mesh;
+ }
+
+ public void SetHandColor(Color color) {
+ for (int i = 0; i < _jointSpheres.Length; ++i) {
+ _jointSpheres[i].GetComponent().material.color = color;
+ }
+ mockThumbJointSphere.GetComponent().material.color = color;
+ palmPositionSphere.GetComponent().material.color = color;
+ wristPositionSphere.GetComponent().material.color = color;
+ }
+}
diff --git a/Assets/Scripts/LinkHandSpheres.cs.meta b/Assets/Scripts/LinkHandSpheres.cs.meta
new file mode 100644
index 0000000..9227486
--- /dev/null
+++ b/Assets/Scripts/LinkHandSpheres.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5c05dec50c3fede4285966a3321adc21
+timeCreated: 1487522303
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NetworkCube.cs b/Assets/Scripts/NetworkCube.cs
new file mode 100644
index 0000000..6d9c53e
--- /dev/null
+++ b/Assets/Scripts/NetworkCube.cs
@@ -0,0 +1,105 @@
+using Leap;
+using Leap.Unity.Interaction;
+using UnityEngine;
+using UnityEngine.Networking;
+
+public class NetworkCube : NetworkBehaviour {
+
+ [SerializeField] Material red, blue, green;
+ Color playerColor;
+ Renderer _renderer;
+ //Collider _collider;
+ NetworkIdentity _networkIdentity;
+ InteractionBehaviour _interactionBehaviour;
+
+ void Awake() {
+ _renderer = GetComponent();
+ //_collider = GetComponentInChildren();
+ _networkIdentity = GetComponent();
+ _interactionBehaviour = GetComponent();
+ }
+
+ void SetRigidbodyEnabled(bool enabled) {
+ _interactionBehaviour.isKinematic = !enabled;
+ _interactionBehaviour.useGravity = enabled;
+ }
+
+ public override void OnStartClient() {
+ InteractionManager interactionManager = FindObjectOfType();
+ GetComponent().Manager = interactionManager;
+
+ //SetRigidbodyEnabled(false);
+ }
+
+ // If we have the local authority, disable trigering and enable physics interactions
+ public override void OnStartAuthority() {
+ //_collider.isTrigger = false;
+ SetRigidbodyEnabled(true);
+ }
+
+ // If we haven't the local authority, disable physics interactions and enable trigering
+ public override void OnStopAuthority() {
+ //_collider.isTrigger = true;
+ SetRigidbodyEnabled(false);
+ }
+
+ public void OnGraspBegin() {
+ if (hasAuthority) return;
+ CubeSpawner cubeSpawner = FindObjectOfType() // TODO cache
+ .GetComponentInParent();
+ if (cubeSpawner) cubeSpawner.SetAuthority(_networkIdentity);
+ }
+
+ public void OnGraspEnd(Hand lastHand) {
+ //if (!hasAuthority || isServer) return;
+
+ //if (GetComponent().IsBeingGrasped) // TODO cache
+ // return;
+
+ //// Transfert rigidbody informations (velocity, angularVelocity) to server
+ //Rigidbody rigidbody = GetComponent(); // TODO cache
+ // CmdTransfertRigidbodyState(rigidbody.velocity, rigidbody.angularVelocity);
+
+ //CubeSpawner cubeSpawner = FindObjectOfType() // TODO cache
+ //.GetComponentInParent();
+ //if (cubeSpawner) cubeSpawner.RemoveAuthority(_networkIdentity);
+ }
+
+ [Command]
+ void CmdTransfertRigidbodyState(Vector3 velocity, Vector3 angularVelocity) {
+ GetComponent().velocity = velocity;
+ GetComponent().angularVelocity= angularVelocity;
+ }
+
+
+ void Update() {
+ if (hasAuthority) {
+ if (_renderer.material != green) _renderer.material = green;
+ } else {
+ if (_renderer.material != red) _renderer.material = red;
+ }
+ }
+
+ //[Command]
+ //void CmdRemoveAuthority(NetworkIdentity grabID) {
+ // grabID.RemoveClientAuthority(grabID.clientAuthorityOwner);
+ //}
+
+ //[Command]
+ //void CmdAssignAuthority(NetworkIdentity grabID) {
+ // grabID.AssignClientAuthority(connectionToClient);
+ //}
+
+ void OnTriggerEnter(Collider other) {
+ if (other.name == "DestroyTrigger") {
+ CmdDestroy();
+ }
+ }
+
+ [Command]
+ void CmdDestroy() {
+ _networkIdentity.RemoveClientAuthority(_networkIdentity.clientAuthorityOwner);
+ GameObject go = NetworkServer.FindLocalObject(_networkIdentity.netId);
+ NetworkServer.Destroy(go);
+ }
+}
diff --git a/Assets/Scripts/NetworkCube.cs.meta b/Assets/Scripts/NetworkCube.cs.meta
new file mode 100644
index 0000000..87031c7
--- /dev/null
+++ b/Assets/Scripts/NetworkCube.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 496f0ce23cc885344abcae22466ec784
+timeCreated: 1486937740
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 100
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NetworkCubeInteractionBehaviour.cs b/Assets/Scripts/NetworkCubeInteractionBehaviour.cs
new file mode 100644
index 0000000..13d1c52
--- /dev/null
+++ b/Assets/Scripts/NetworkCubeInteractionBehaviour.cs
@@ -0,0 +1,24 @@
+using Leap;
+using Leap.Unity.Interaction;
+using UnityEngine;
+
+[RequireComponent(typeof(NetworkCube))]
+public class NetworkCubeInteractionBehaviour : InteractionBehaviour {
+
+ NetworkCube _networkCube;
+
+ protected override void Awake() {
+ base.Awake();
+ _networkCube = GetComponent();
+ }
+
+ protected override void OnGraspBegin() {
+ base.OnGraspBegin();
+ _networkCube.OnGraspBegin();
+ }
+
+ protected override void OnGraspEnd(Hand lastHand) {
+ base.OnGraspEnd(lastHand);
+ _networkCube.OnGraspEnd(lastHand);
+ }
+}
diff --git a/Assets/Scripts/NetworkCubeInteractionBehaviour.cs.meta b/Assets/Scripts/NetworkCubeInteractionBehaviour.cs.meta
new file mode 100644
index 0000000..26b92d5
--- /dev/null
+++ b/Assets/Scripts/NetworkCubeInteractionBehaviour.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 1c9b1a8985b7e9d46bf1bc1af887529a
+timeCreated: 1487359943
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NetworkPlayer.cs b/Assets/Scripts/NetworkPlayer.cs
index 9305e82..deacfe7 100644
--- a/Assets/Scripts/NetworkPlayer.cs
+++ b/Assets/Scripts/NetworkPlayer.cs
@@ -6,36 +6,35 @@
using Leap.Unity;
public class NetworkPlayer : NetworkBehaviour {
+
[SyncVar]
- public string playerID;
- Camera playerCam;
+ public Color playerColor;
- public InteractionManager interactionManager;
- [SerializeField]
- GameObject _leapPrefab;
+ void Start() {
+ //playerColor.Callback += OnColorSet;
+ SetColor();
+ }
- void Awake() {
- playerCam = GetComponent();
- //if (isLocalPlayer) {
- // GameObject.Find("LeapHandController").transform.SetParent(transform);
- //}
- //else {
- playerCam.enabled = false;
- //}
+ public override void OnStartClient() {
+ if (isServer) {
+ CmdPlayerColor();
+ }
}
- //[Command]
- //void CmdSetPlayerID(string newID) {
- // playerID = newID;
- //}
+ [Command]
+ void CmdPlayerColor() {
+ playerColor = new Color(Random.Range(0f, 1), Random.Range(0f, 1), Random.Range(0f, 1), 1);
+ }
- public override void OnStartLocalPlayer() {
- //GameObject.Find("LeapHandController").transform.SetParent(transform);
- transform.GetChild(0).GetComponent().material.color = Color.blue;
- GameObject leap = Instantiate(_leapPrefab);
- leap.transform.parent = transform;
- leap.transform.localPosition = Vector3.zero;
- leap.transform.localRotation = Quaternion.identity;
- playerCam.enabled = true;
+ void SetColor() {
+ if (isLocalPlayer) {
+ foreach (CustomHand hand in GetComponentsInChildren()) {
+ hand.PlayerColor = playerColor;
+ }
+ } else {
+ foreach (LinkHandSpheres hand in GetComponentsInChildren()) {
+ hand.SetHandColor(playerColor);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/NetworkSync.cs b/Assets/Scripts/NetworkSync.cs
index 4d3e867..5eacf6b 100644
--- a/Assets/Scripts/NetworkSync.cs
+++ b/Assets/Scripts/NetworkSync.cs
@@ -23,7 +23,8 @@ void OnValidate() {
NetworkTransformChild ntc = gameObject.AddComponent();
ntc.target = child;
ntc.syncRotationAxis = NetworkTransform.AxisSyncMode.None;
- ntc.sendInterval = 1/29f;
+ ntc.sendInterval = 1/14f;
+ ntc.movementThreshold = 0;
}
}
}
diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset
index 89ad6b2..caa0891 100644
Binary files a/ProjectSettings/ProjectSettings.asset and b/ProjectSettings/ProjectSettings.asset differ
diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt
index 86d1b1e..66e05aa 100644
--- a/ProjectSettings/ProjectVersion.txt
+++ b/ProjectSettings/ProjectVersion.txt
@@ -1 +1 @@
-m_EditorVersion: 5.5.1f1
+m_EditorVersion: 5.5.0f3
diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset
index bd9e618..d6435dc 100644
Binary files a/ProjectSettings/UnityConnectSettings.asset and b/ProjectSettings/UnityConnectSettings.asset differ