Unity3D Leap Motion - 将手放在静态姿势(姿势完全无法旋转)

时间:2018-01-25 09:36:31

标签: unity3d vector rotation quaternions leap-motion

我试图将手设置为一系列姿势中的一个。我创建了一个捕获框架的脚本,将左手序列化,然后将其存储为xml以进行加载。我目前让系统通过计算实时数据中的手掌位置与存储的姿势的手掌位置之间的偏移来将手设置为正确的姿势。我遇到的问题是让手旋转。

'hand'参数是来自实时数据的当前手,'pose'参数是从xml加载的手。

这是我创建的方法:

public static Hand SetHandInPose(Hand hand, Hand pose)
{
    if(hand == null)
    {
        Debug.Log("Hand is null, so returning that");
        return hand;
    }
    if(pose == null)
    {
        Debug.Log("The loaded pose is null, so let's just return the original hand");
        return hand;
    }
    Hand h = pose;

    Quaternion handRotOffset = pose.Rotation.ToQuaternion() * Quaternion.Inverse(hand.Rotation.ToQuaternion());
    //Debug.Log("The rotational offset is: " + handRotOffset.eulerAngles);
    Vector offset = hand.PalmPosition - pose.PalmPosition;

    h.Rotation = hand.Rotation;//(h.Rotation.ToQuaternion() * handRotOffset).ToLeapQuaternion();
    h.PalmPosition += (offset.ToVector3()).ToVector();
    h.WristPosition += (offset.ToVector3()).ToVector();

    for(int f = 0; f< h.Fingers.Count; f++)
    {
        for(int i = 0; i < h.Fingers[f].bones.Length; i++)
        {
            //offset = hand.Fingers[f].bones[i].Center - pose.Fingers[f].bones[i].Center;     

            //if (h.Fingers[f].bones[i].Type == Bone.BoneType.TYPE_METACARPAL) continue;
            h.Fingers[f].bones[i].Center += (offset.ToVector3()).ToVector();
            h.Fingers[f].bones[i].NextJoint += (offset.ToVector3()).ToVector();
            h.Fingers[f].bones[i].PrevJoint += (offset.ToVector3()).ToVector();
            h.Fingers[f].bones[i].Rotation = hand.Fingers[f].bones[i].Rotation;
            //h.Fingers[f].bones[i].Rotation = (h.Fingers[f].bones[i].Rotation.ToQuaternion() * handRotOffset).ToLeapQuaternion();
            h.Fingers[f].bones[i].Direction = (h.Fingers[f].bones[i].NextJoint - h.Fingers[f].bones[i].PrevJoint);
            h.Fingers[f].bones[i].Center = (h.Fingers[f].bones[i].PrevJoint + h.Fingers[f].bones[i].NextJoint) / 2f;
        }
        h.Fingers[f].Direction = h.Fingers[f].GetBone(Bone.BoneType.TYPE_INTERMEDIATE).Direction;
    }
    return h;
}

非常感谢任何建议/帮助。

更新:

以下是基于收到建议的新代码。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Leap;
using Leap.Unity;

 public class MimicHandModelDriver : MonoBehaviour
 {

    public Chirality whichHand = Chirality.Left;

    public enum RotationMode { Inherit, Overwrite }
    public RotationMode rotationMode = RotationMode.Inherit;

    public MultiLeap_CapsuleHand handModelToDrive;
    private bool _handModelInitialized = false;
    private bool _handModelBegun = false;

    private Hand mimicHand = null;

    Hand sourceHand; //The current hand from the live data
    Hand poseHand; //Reference to the hand we load from XML that is in the pose we want

    public Hand SourceHand
    {
        set
        {
            sourceHand = value;
        }
    }

    public Hand PoseHand
    {
        set
        {
            poseHand = value;
        }
    }

    private void Update()
    {

        if (sourceHand != null)
        {
            // Copy data from the tracked hand into the mimic hand.
            if (mimicHand == null) { mimicHand = new Hand(); }

            mimicHand.CopyFrom(poseHand); //copy the stored pose in the mimic hand
            mimicHand.Arm.CopyFrom(poseHand.Arm); // copy the stored pose's arm into the mimic hand

            // Use the rotation from the live data
            var handRotation = sourceHand.Rotation.ToQuaternion();

            // Transform the copied hand so that it's centered on the current hands position and matches it's rotation.
            mimicHand.SetTransform(sourceHand.PalmPosition.ToVector3(), handRotation);
        }

        // Drive the attached HandModel.
        if (mimicHand != null && handModelToDrive != null)
        {
            // Initialize the handModel if it hasn't already been initialized.
            if (!_handModelInitialized)
            {
                handModelToDrive.SetLeapHand(mimicHand); //Prevents an error with null reference exception when creating the spheres from
                //the init hand call
                handModelToDrive.InitHand();
                _handModelInitialized = true;
            }

            // Set the HandModel's hand data.
            handModelToDrive.SetLeapHand(mimicHand);

            // "Begin" the HandModel to represent a 'newly tracked' hand.
            if (!_handModelBegun)
            {
                handModelToDrive.BeginHand();
                _handModelBegun = true;
            }

            Debug.Log("Updating the mimic hand");
            handModelToDrive.UpdateTheHand(); //This method contains the update code, with update code commented out
            //so i control when a hand is updated. I have used this throughout the rest of my project so i know this works. 
        }
    }

}

1 个答案:

答案 0 :(得分:1)

我们的UnityModules内置了一些便利/扩展方法,可以更轻松地复制手数据:

hand.Transform(LeapTransform transform) - 这将应用转换为Hand。 hand.SetTransform(Vector3 position, Quaternion rotation) - 这会将Hand变换为将PalmPosition置于中心位置并将整个手变换为与rotation对齐。 hand.CopyFrom(Hand other) - 这个将处理从一只手到另一只手复制数据(读取:姿势)的所有gorey细节。

在这种情况下,CopyFrom和SetTransform会为您提供您正在寻找的数据。

编辑:因此,您问题的新方面涉及使用此数据来驱动HandModel - 在本例中为CapsuleHand。对于当前版本的Unity资产,手动模型管道非常关闭。你要么使用标准的Leap装备来驾驶真正的被追踪的手,要么你有一段艰难的时间。

幸运的是,你可以手动驱动HandModel,但你必须小心按正确的顺序调用正确的方法。查看此示例脚本,该脚本可以驱动它具有引用的HandModel。 重要提示,因为您独立于普通的Provider / HandPool / HandGroup管道驱动HandModel,您需要创建一个新的HandModel - 例如一个新的,重复的CapsuleHand对象 - 在您的Leap Rig中不是并且不会被该装备的HandPool驱动。然后该脚本可以驱动它:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Leap;
using Leap.Unity;

public class MimicHandModelDriver : MonoBehaviour {

  public Chirality whichHand = Chirality.Left;

  public enum RotationMode { Inherit, Overwrite }
  public RotationMode rotationMode = RotationMode.Inherit;

  public HandModelBase handModelToDrive;
  private bool _handModelInitialized = false;
  private bool _handModelBegun = false;

  [Header("Debug")]
  public bool drawEditorGizmos = false;

  private Hand mimicHand = null;

  private void Update() {
    // Get a hand from the standard tracking pipeline.
    var sourceHand = Hands.Get(whichHand);

    if (sourceHand != null) {
      // Copy data from the tracked hand into the mimic hand.
      if (mimicHand == null) { mimicHand = new Hand(); }
      mimicHand.CopyFrom(sourceHand);
      mimicHand.Arm.CopyFrom(sourceHand.Arm); // Capsule Hands like to have Arm data too.

      // Figure out what rotation to use for the mimic hand.
      var handRotation = this.transform.rotation;
      if (rotationMode == RotationMode.Inherit) {
        handRotation = mimicHand.Rotation.ToQuaternion();
      }

      // Transform the copied hand so that it's centered on this object's transform.
      mimicHand.SetTransform(this.transform.position, handRotation);
    }

    // Drive the attached HandModel.
    if (mimicHand != null && handModelToDrive != null) {
      // Initialize the handModel if it hasn't already been initialized.
      if (!_handModelInitialized) {
        handModelToDrive.InitHand();
        _handModelInitialized = true;
      }

      // Set the HandModel's hand data.
      handModelToDrive.SetLeapHand(mimicHand);

      // "Begin" the HandModel to represent a 'newly tracked' hand.
      if (!_handModelBegun) {
        handModelToDrive.BeginHand();
        _handModelBegun = true;
      }

      // Every Update, we call UpdateHand. It's necessary to call this every update
      // specifically for CapsuleHands, which uses manual GL calls to render rather than
      // a standard MeshRenderer.
      handModelToDrive.UpdateHand();
    }
  }

  // Draw some gizmos in case there's no HandModel attached.
  private void OnDrawGizmos() {
    if (!drawEditorGizmos) return;

    Gizmos.color = Color.red;

    if (mimicHand != null) {
      draw(mimicHand.PalmPosition.ToVector3());

      for (int f = 0; f < 5; f++) {
        for (int b = 0; b < 4; b++) {
          draw(mimicHand.Fingers[f].bones[b].NextJoint.ToVector3());
        }
      }
    }
  }

  private void draw(Vector3 pos) {
    Gizmos.DrawWireSphere(pos, 0.01f);
  }

}