Skip to content

Commit

Permalink
Merge pull request #1053 from leapmotion/develop
Browse files Browse the repository at this point in the history
Merge develop with master so master can be the new develop
  • Loading branch information
Nicholas Benson committed Aug 7, 2018
2 parents 4a73b50 + 619bac8 commit b521085
Show file tree
Hide file tree
Showing 23 changed files with 186 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ public static void OnGUI(List<CombinablePropertyAttribute> attributes,

IFullPropertyDrawer fullPropertyDrawer = null;
foreach (var a in attributes) {
a.Init(fieldInfo, property.serializedObject.targetObjects);
if (fieldInfo != null) {
a.Init(fieldInfo, property.serializedObject.targetObjects);
}

if (a is IBeforeLabelAdditiveDrawer) {
EditorGUIUtility.labelWidth -= (a as IBeforeLabelAdditiveDrawer).GetWidth();
Expand Down
10 changes: 6 additions & 4 deletions Assets/LeapMotion/Core/Scripts/DataStructures/AssetFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System;
using UnityEngine;
using UnityObject = UnityEngine.Object;
using UnityEngine.Serialization;
#if UNITY_EDITOR
using UnityEditor;
#endif
Expand All @@ -27,7 +28,8 @@ namespace Leap.Unity {
public class AssetFolder {

[SerializeField]
protected UnityObject _assetFolder;
[FormerlySerializedAs("_assetFolder")]
protected UnityObject _assetReference;

public AssetFolder() { }

Expand All @@ -44,8 +46,8 @@ public AssetFolder(string path) {
public virtual string Path {
get {
#if UNITY_EDITOR
if (_assetFolder != null) {
return AssetDatabase.GetAssetPath(_assetFolder);
if (_assetReference != null) {
return AssetDatabase.GetAssetPath(_assetReference);
} else {
return null;
}
Expand All @@ -55,7 +57,7 @@ public virtual string Path {
}
set {
#if UNITY_EDITOR
_assetFolder = AssetDatabase.LoadAssetAtPath<DefaultAsset>(value);
_assetReference = AssetDatabase.LoadAssetAtPath<DefaultAsset>(value);
#else
throw new InvalidOperationException("Cannot set the Path of an Asset Folder in a build.");
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
Object folderAsset = null;
string folderPath = "";

SerializedProperty folderProp = property.FindPropertyRelative("_assetFolder");
if (folderProp.hasMultipleDifferentValues) {
SerializedProperty assetProp = property.FindPropertyRelative("_assetReference");
if (assetProp.hasMultipleDifferentValues) {
EditorGUI.showMixedValue = true;
} else {
folderAsset = folderProp.objectReferenceValue;
folderAsset = assetProp.objectReferenceValue;
if (folderAsset != null) {
folderPath = AssetDatabase.GetAssetPath(folderAsset);
}
Expand All @@ -50,7 +50,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
if (!ValidatePath(resultPath, relativePath, out errorMessage)) {
EditorUtility.DisplayDialog("Invalid selection.", errorMessage, "OK");
} else {
folderProp.objectReferenceValue = asset;
assetProp.objectReferenceValue = asset;
}
}
}
Expand All @@ -72,7 +72,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
case EventType.DragPerform:
if (ValidateObject(draggedObject, out errorMessage)) {
DragAndDrop.AcceptDrag();
folderProp.objectReferenceValue = draggedObject;
assetProp.objectReferenceValue = draggedObject;
}
break;
}
Expand All @@ -85,6 +85,9 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent
}

protected virtual string PromptUserForPath(string currentPath) {
if (string.IsNullOrEmpty(currentPath)) {
currentPath = "Assets";
}
return EditorUtility.OpenFolderPanel("Select Folder", currentPath, "");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ namespace Leap.Unity {
public class StreamingAssetPropertyDrawer : StreamingFolderPropertyDrawer {

protected override string PromptUserForPath(string currentPath) {
if (string.IsNullOrEmpty(currentPath)) {
currentPath = "Assets";
}
return EditorUtility.OpenFilePanel("Select File", currentPath, "");
}

Expand Down
15 changes: 13 additions & 2 deletions Assets/LeapMotion/Core/Scripts/DataStructures/StreamingFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace Leap.Unity {
public class StreamingFolder : AssetFolder, ISerializationCallbackReceiver {

[SerializeField]
private string _relativePath;
protected string _relativePath;

/// <summary>
/// Gets the full path to the streaming folder. This operation is safe to be
Expand All @@ -41,7 +41,18 @@ public void OnAfterDeserialize() { }

public void OnBeforeSerialize() {
#if UNITY_EDITOR
string assetPath = AssetDatabase.GetAssetPath(_assetFolder);
if (_assetReference == null && !string.IsNullOrEmpty(_relativePath)) {
//If the asset folder is null, we first see if the current relative path points to a valid
//folder. This can happen during deserialization of a unitypackage.

//We hardcode Assets/StreamingAssets since this can only occur within the editor
//and unity doesn't let us call Application.streamingAssetsPath from within the
//OnBeforeSerialize callback
string path = System.IO.Path.Combine("Assets/StreamingAssets", _relativePath);
_assetReference = AssetDatabase.LoadAssetAtPath<DefaultAsset>(path);
}

string assetPath = AssetDatabase.GetAssetPath(_assetReference);
if (string.IsNullOrEmpty(assetPath)) {
_relativePath = null;
} else {
Expand Down
14 changes: 14 additions & 0 deletions Assets/LeapMotion/Core/Scripts/Hands/RiggedFinger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ static RiggedFinger() {
}
}

private RiggedHand _parentRiggedHand = null;
/// <summary>
/// Updates model bone positions and rotations based on tracked hand data.
/// </summary>
Expand All @@ -72,7 +73,20 @@ public override void UpdateFinger() {
// the standard "test" edit-time hand model from the TestHandFactory.
var boneTipPos = GetJointPosition(i + 1);
var boneVec = boneTipPos - boneRootPos;

// If the rigged hand is scaled (due to a scaled rig), we'll need to divide
// out that scale from the bone length to get its normal length.
if (_parentRiggedHand == null) {
_parentRiggedHand = GetComponentInParent<RiggedHand>();
}
if (_parentRiggedHand != null) {
var parentRiggedHandScale = _parentRiggedHand.transform.lossyScale.x;
if (parentRiggedHandScale != 0f && parentRiggedHandScale != 1f) {
boneVec /= parentRiggedHandScale;
}
}
var boneLen = boneVec.magnitude;

var standardLen = s_standardFingertipLengths[(int)this.fingerType];
var newScale = bones[i].transform.localScale;
var lengthComponentIdx = getLargestComponentIndex(modelFingerPointing);
Expand Down
26 changes: 24 additions & 2 deletions Assets/LeapMotion/Core/Scripts/LeapImageRetriever.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ public class LeapImageRetriever : MonoBehaviour {
private EyeTextureData _eyeTextureData = new EyeTextureData();

//Image that we have requested from the service. Are requested in Update and retrieved in OnPreRender
protected ProduceConsumeBuffer<Image> _imageQueue = new ProduceConsumeBuffer<Image>(32);
protected ProduceConsumeBuffer<Image> _imageQueue = new ProduceConsumeBuffer<Image>(128);
protected Image _currentImage = null;

private long _prevSequenceId;
private bool _needQueueReset;

public EyeTextureData TextureData {
get {
return _eyeTextureData;
Expand Down Expand Up @@ -275,6 +278,11 @@ private void LateUpdate() {

_currentImage = null;

if (_needQueueReset) {
while (_imageQueue.TryDequeue()) { }
_needQueueReset = false;
}

/* Use the most recent image that is not newer than the current frame
* This means that the shown image might be slightly older than the current
* frame if for some reason a frame arrived before an image did.
Expand Down Expand Up @@ -340,7 +348,21 @@ private IEnumerator serviceCoroutine() {

private void onImageReady(object sender, ImageEventArgs args) {
Image image = args.image;
_imageQueue.TryEnqueue(image);

if (!_imageQueue.TryEnqueue(image)) {
Debug.LogWarning("Image buffer filled up. This is unexpected and means images are being provided faster than " +
"LeapImageRetriever can consume them. This might happen if the application has stalled " +
"or we recieved a very high volume of images suddenly.");
_needQueueReset = true;
}

if (image.SequenceId < _prevSequenceId) {
//We moved back in time, so we should reset the queue so it doesn't get stuck
//on the previous image, which will be very old.
//this typically happens when the service is restarted while the application is running.
_needQueueReset = true;
}
_prevSequenceId = image.SequenceId;
}

public void ApplyGammaCorrectionValues() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,16 @@ private static void init() {
}
};

#if UNITY_2017_1_OR_NEWER
EditorApplication.playModeStateChanged -= onPlayModeStateChanged;
EditorApplication.playModeStateChanged += onPlayModeStateChanged;
#else
EditorApplication.playmodeStateChanged -= onPlayModeStateChanged;
EditorApplication.playmodeStateChanged += onPlayModeStateChanged;
#endif
}

#if UNITY_2017_1_OR_NEWER
private static void onPlayModeStateChanged(PlayModeStateChange stateChange) {
if (stateChange == PlayModeStateChange.EnteredPlayMode) {
bool allChecksPassed = runAllChecks_NoGUI();
Expand All @@ -53,6 +59,20 @@ private static void onPlayModeStateChanged(PlayModeStateChange stateChange) {
}
}
}
#else
private static void onPlayModeStateChanged() {
var didEnterPlayMode = EditorApplication.isPlayingOrWillChangePlaymode
&& EditorApplication.isPlaying;

if (didEnterPlayMode) {
bool allChecksPassed = runAllChecks_NoGUI();

if (!allChecksPassed) {
EditorApplication.delayCall += LeapUnityWindow.Init;
}
}
}
#endif

[LeapProjectCheck("Interaction Engine", 10)]
public static bool CheckInteractionSettings() {
Expand All @@ -70,7 +90,7 @@ public static bool CheckInteractionSettings() {
return true;
}

#region Run All Checks
#region Run All Checks

private static bool runAllChecks_NoGUI() {
bool allChecksPassed = true;
Expand Down Expand Up @@ -104,9 +124,9 @@ private static bool runAllChecks_WithGUI() {
return allChecksPassed;
}

#endregion
#endregion

#region Check Gravity
#region Check Gravity

private static bool checkGravity() {
if (LeapProjectChecks.CheckIgnoredKey(IGNORE_GRAVITY_CHECK_KEY)) {
Expand Down Expand Up @@ -147,9 +167,9 @@ private static bool checkGravityAndDrawGUI() {
return gravityOK;
}

#endregion
#endregion

#region Check Timestep
#region Check Timestep

private static bool checkTimeStep() {
if (LeapProjectChecks.CheckIgnoredKey(IGNORE_TIMESTEP_CHECK_KEY)) {
Expand Down Expand Up @@ -192,9 +212,9 @@ private static bool checkTimeStepAndDrawGUI() {
return timeStepOK;
}

#endregion
#endregion

#region Check Rigid Hands
#region Check Rigid Hands

private static bool checkRigidHands() {
var unusedRigidHandObjects = (GameObject[])null;
Expand Down Expand Up @@ -248,7 +268,7 @@ private static bool checkRigidHandsAndDrawGUI() {
return rigidHandsOK;
}

#endregion
#endregion

}
}
2 changes: 1 addition & 1 deletion docs/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ PROJECT_NUMBER = 4.4.0
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.

PROJECT_BRIEF = "Leap Motion's Unity SDK."
PROJECT_BRIEF = "Leap Motion's Unity SDK"

# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
Expand Down
2 changes: 1 addition & 1 deletion docs/content/200-Interaction-Engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ You can override both or only one of the layers for interaction objects as long
Be sure to take a look at examples 2 through 6 to see how interaction objects can have their behavior fine-tuned to meet the specific needs of your application. The standard workflow for writing custom scripts for interaction objects goes something like this:

- Be sure your object has an [InteractionBehaviour][ref_InteractionBehaviour] component (or an [InteractionButton][ref_InteractionButton] or [InteractionSlider][ref_InteractionSlider] component, each of which inherit from @ref InteractionBehaviour).
- Add your custom script to the interaction object and initialize a reference to the InteracrionBehaviour component.
- Add your custom script to the interaction object and initialize a reference to the InteractionBehaviour component.
```{.cs}
using Leap.Unity.Interaction;
using UnityEngine;
Expand Down
14 changes: 7 additions & 7 deletions docs/content/400-Hands-Module.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ In this post, we’ll provide a detailed overview on how to use our new autorig

The new LeapHandAutorig Monobehavior script acts as a quarterback for an array of scripts and methods which comprise the rigged hands setup process. Sitting at the top of a hands hierarchy, the script runs in the editor. The Autorig button in the Inspector sets off a chain of actions that works through the steps outlined in the chart below.

![](http://blog.leapmotion.com/wp-content/uploads/2016/07/hands-module-4.png)
![](@ref images/hands-module-4.png)

After autorigging, the LeapHands Autorig Inspector console acts as a central control panel to push values to the other Leap Motion rigging scripts. This allows you to test the model quickly and set certain values centrally, instead of digging through the hierarchy to set values in all the scripts manually.

Autorigging can act on a variety of FBX assets, and works in two different ways, depending on whether the asset has a Unity Mecanim Humanoid definition. If so, then LeapHandsAutorig finds and assign joints based on this mapping. If not, then LeapHandsAutorig searches the hierarchy below its Transform by a list of names typical of common character rigging practices.

![](http://blog.leapmotion.com/wp-content/uploads/2016/07/hands-module-6.png)
![](@ref images/hands-module-6.png)

# Step 1: Setting the Scene {#hands-auto-rig-step-1}

Expand Down Expand Up @@ -48,19 +48,19 @@ In this alternative example, the LoPoly_Rigged_Hands_Skeleton transform is an FB

Drag the LeapHandsAutorig script to this transform and click the Autorig button. In this case, if you check the SetEditorLeapPose checkbox, you’ll see that the hands’ palms are flipped. So for this model, you can check the FlipPalms checkbox. This reverses the direction of the ModelPalmFacing vectors for each RiggedHand script and all of the RiggedFinger scripts as well.

# Step 3: RiggedHand and RiggedFingers Are Set Up Automatically #{hands-auto-rig-step-3}
# Step 3: RiggedHand and RiggedFingers Are Set Up Automatically {#hands-auto-rig-step-3}

One of the main tasks of the LeapHandAutorig component is to find hand transforms and assign RiggedHand components, then to find the base transform for each finger and assign RiggedFinger components. After autorigging, you can find them quickly by clicking on their references in the LeapHandAutorig’s Inspector. This expands the hierarchy and highlights their individual transforms for easy selection. These are the script components that receive and translate tracking data from Leap Motion’s Core Assets scripts and actually drive the rigged hand models at runtime.

The RiggedHand script contains references to the palm and forearm (if they exist) as well as reference to the five RiggedFinger components in its hierarchy. The ModelPalmFacing and ModelFingerPointing vectors represent the cardinal direction the the palm and fingers face. These, and the several remaining fields, are identical to those exposed in the LeapHandAutorig script. When those values are changed, those values are pushed here.

![](http://blog.leapmotion.com/wp-content/uploads/2016/07/hands-module-2.png)
![](@ref images/hands-module-2.png)

Each RiggedFinger script contains references for its three child bone transforms and one of five finger types. They also have fields for the cardinal-direction-facing vectors for the palm and the direction of its children bones. Again, like those in the RiggedHand script, these vectors are calculated by methods within the RiggedFingers script, but can be changed via the central interface of the LeapHandsAutorig.

Each RiggedFinger component also has a significant checkbox called Deform Position. This causes the joint transforms to not only be rotated by Leap Motion tracking but to be positioned as well. To take advantage of this feature, the FBX model needs to have been built with joints close to human proportions and weighted well enough to allow joints to move without polygon tearing. This field then allows for scaling and proportioning the rigged model to the user’s tracked hand.

![](http://blog.leapmotion.com/wp-content/uploads/2016/07/hands-module-7.png)
![](@ref images/hands-module-7.png)

# Making New Hand Models or Choosing Hand Assets #{hands-new-hand-models}

Expand All @@ -70,7 +70,7 @@ That being said, the steps outlined below are equally relevant if you’re choos

Sculpting and topology: Sculpting something that can bend and deform well is more that simply creating a visually appealing shape. You’ll want to think about and plan how your model will look when it’s stretched to its limits, curled into a fist or other extreme poses. We strongly recommend topology that features edgeloops flowing along the creases of the hand, rather than a uniform distribution of polygons. This is critical for good deformations.

![](http://blog.leapmotion.com/wp-content/uploads/2016/07/hands-module-3.png)
![](@ref images/hands-module-3.png)

Performance: Since you’re probably creating these hands for a VR application, it’s good to remember that these hands get rendered twice. To keep your framerates high, polygon budgets and draw calls should be managed. (Underscore that several times if you’re creating a mobile application.)

Expand All @@ -89,7 +89,7 @@ Joint Orientation: Having proper joint orientations is critical for a couple of

Keep in mind that the end user’s hands will be curling anatomically. Understanding the finer details – like how fingers curl toward the center of the palm, rather just folding straight in – will streamline your development and help you get more convincing poses out of your rigged hands.

![](http://blog.leapmotion.com/wp-content/uploads/2016/07/hands-module-1.png)
![](@ref images/hands-module-1.png)

Vertex Weighting for Range of Motion and Good Deformation: Since your rigged hands may be driven by many different end users, hand models for Leap Motion tracking need to deform well through a rich range of motions. Joint placement and careful weighting for good deformations is important for quality posing.

Expand Down
Binary file added docs/images/hands-module-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/hands-module-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/hands-module-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/hands-module-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/hands-module-6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/hands-module-7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit b521085

Please sign in to comment.