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