Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
3142d0420This file is pinned to the selected release. Download the original to open it in your design tool.
/******************************************************************************
* Copyright (C) Leap Motion, Inc. 2011-2018. *
* Leap Motion proprietary and confidential. *
* *
* Use subject to the terms of the Leap Motion SDK Agreement available at *
* https://developer.leapmotion.com/sdk_agreement, or another agreement *
* between Leap Motion and you, your company or other organization. *
******************************************************************************/
using Leap.Unity.Interaction.Internal;
using Leap.Unity.Space;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Leap.Unity.Interaction {
/// <summary>
/// ActivityManager is a wrapper around PhysX sphere queries for arbitrary Unity objects.
/// "Active" objects are objects found in the latest query. It's also possible to get the
/// sets of objects that just began or stopped being active since the last query; this
/// requires enabling the trackStateChanges setting.
/// </summary>
public class ActivityManager<T> {
/// <summary> The radius of the query in world-space. </summary>
public float activationRadius;
/// <summary>
/// The layer mask against which to query for active objects. The ActivityManager will only
/// find objects in these layers. By default, the ActivityManager will query all layers, but
/// this is highly inefficient.
///
/// See SingleLayer and use bitwise operations on their layerMasks for a convenient way to
/// express layer masks.
/// <see cref="SingleLayer"/>
/// </summary>
public int activationLayerMask = ~0;
/// <summary>
/// This function, if set to a non-null value, overrides the activationLayerMask
/// setting with the result of calling the function. Use this if your application
/// state will modify the layer mask to use for the activity manager.
/// </summary>
public Func<int> activationLayerFunction = null;
/// <summary>
/// This is the function by which the ActivityManager converts the Colliders it finds
/// through PhysX queries into Ts to be placed in the ActiveObjects set.
///
/// Only objects with at least one Collider for which this function returns a non-null T
/// will be added to the ActiveObjects set.
/// </summary>
public Func<Collider, T> filter = null;
private HashSet<T> _activeObjects = new HashSet<T>();
/// <summary>
/// Returns the currently "active" objects -- objects that were within the latest sphere query.
/// </summary>
public HashSet<T> ActiveObjects { get { return _activeObjects; } }
/// <summary>
/// If set to true, BeganActive and EndedActive will be calculated and populated every time a
/// new query occurs.
/// </summary>
public bool trackStateChanges = false;
private HashSet<T> _activeObjectsLastFrame = new HashSet<T>();
private HashSet<T> _beganActiveObjects = new HashSet<T>();
/// <summary>
/// If trackStateChanges is enabled (on by default), contains the objects that just started being active since
/// the last query.
/// </summary>
public HashSet<T> BeganActive {
get { return _beganActiveObjects; }
}
private HashSet<T> _endedActiveObjects = new HashSet<T>();
/// <summary>
/// If trackStateChanges is enabled (on by default), contains the objects that just stopped being active since
/// the last query.
/// </summary>
public HashSet<T> EndedActive {
get { return _endedActiveObjects; }
}
public ActivityManager(float activationRadius, Func<Collider, T> filter) {
this.activationRadius = activationRadius;
this.filter = filter;
}
[ThreadStatic]
private static Collider[] s_colliderResultsBuffer = new Collider[32];
public void UpdateActivityQuery(Vector3? queryPosition, List<LeapSpace> spaces = null) {
using (new ProfilerSample("Update Activity Manager")) {
using (new ProfilerSample("Initialize Buffers")) {
_activeObjects.Clear();
// Make sure collider results buffer exists (for other threads; see ThreadStatic)
if (s_colliderResultsBuffer == null || s_colliderResultsBuffer.Length < 32) {
s_colliderResultsBuffer = new Collider[32];
}
}
// Update object activity by activation radius around the query position (PhysX colliders necessary).
// queryPosition can be null, in which case no objects will be found, and they will all become inactive.
if (queryPosition.HasValue) {
Vector3 actualQueryPosition = queryPosition.GetValueOrDefault();
int count = GetSphereColliderResults(actualQueryPosition, ref s_colliderResultsBuffer);
UpdateActiveList(count, s_colliderResultsBuffer);
using (new ProfilerSample("Check GUI Spaces")) {
if (spaces != null) {
//Check once in each of the GUI's subspaces
foreach (LeapSpace space in spaces) {
count = GetSphereColliderResults(transformPoint(actualQueryPosition, space), ref s_colliderResultsBuffer);
UpdateActiveList(count, s_colliderResultsBuffer);
}
}
}
}
using (new ProfilerSample("Track State Changes")) {
if (trackStateChanges) {
_endedActiveObjects.Clear();
_beganActiveObjects.Clear();
foreach (var behaviour in _activeObjects) {
if (!_activeObjectsLastFrame.Contains(behaviour)) {
_beganActiveObjects.Add(behaviour);
}
}
foreach (var behaviour in _activeObjectsLastFrame) {
if (!_activeObjects.Contains(behaviour)) {
_endedActiveObjects.Add(behaviour);
}
}
_activeObjectsLastFrame.Clear();
foreach (var behaviour in _activeObjects) {
_activeObjectsLastFrame.Add(behaviour);
}
}
}
}
}
private int GetSphereColliderResults(Vector3 position, ref Collider[] resultsBuffer) {
using (new ProfilerSample("GetSphereColliderResults()")) {
int layerMask = activationLayerFunction == null ? activationLayerMask : activationLayerFunction();
int overlapCount = 0;
while (true) {
overlapCount = Physics.OverlapSphereNonAlloc(position,
activationRadius,
resultsBuffer,
layerMask,
QueryTriggerInteraction.Collide);
if (overlapCount < resultsBuffer.Length) {
break;
} else {
// Non-allocating sphere-overlap fills the existing _resultsBuffer array.
// If the output overlapCount is equal to the array's length, there might be more collision results
// that couldn't be returned because the array wasn't large enough, so try again with increased length.
// The _in, _out argument setup allows allocating a new array from within this function.
resultsBuffer = new Collider[resultsBuffer.Length * 2];
}
}
return overlapCount;
}
}
private void UpdateActiveList(int numResults, Collider[] results) {
if (filter == null) {
Debug.LogWarning("[ActivityManager] No filter provided for this ActivityManager; no objects will ever be returned."
+ "Please make sure you specify a non-null filter property of any ActivityManagers you wish to use.");
return;
}
for (int i = 0; i < numResults; i++) {
T obj = filter(results[i]);
if (obj != null) {
_activeObjects.Add(obj);
}
}
}
private Vector3 transformPoint(Vector3 worldPoint, LeapSpace space) {
Vector3 localPalmPos = space.transform.InverseTransformPoint(worldPoint);
return space.transform.TransformPoint(space.transformer.InverseTransformPoint(localPalmPos));
}
}
}