Unity3D 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 ответ

Решение

У нас есть несколько удобных / расширенных методов, встроенных в наши UnityModules, которые облегчают копирование данных рук:

hand.Transform(LeapTransform transform) - это применит преобразование к руке.hand.SetTransform(Vector3 position, Quaternion rotation) - это преобразует руку для центрирования положения ладони и трансформирует всю руку, чтобы выровнять rotation,hand.CopyFrom(Hand other) - этот будет обрабатывать все ужасные детали копирования данных (читай: поза) из одной руки в другую.

В этом случае CopyFrom и SetTransform предоставят вам данные, которые вы ищете.

РЕДАКТИРОВАТЬ: Итак, новый аспект вашего вопроса включает в себя использование этих данных для управления HandModel - в данном случае, CapsuleHand. Для текущей версии наших активов Unity конвейер ручной модели довольно закрыт. Вы либо используете стандартную оснастку Leap для управления реальными гусеничными руками, либо у вас немного трудное время.

К счастью, вы можете просто управлять HandModel вручную, но вы должны быть осторожны, чтобы вызывать правильные методы в правильном порядке. Посмотрите этот пример скрипта, который может управлять HandModel, на который есть ссылка. Важно: поскольку вы управляете HandModel независимо от обычного конвейера Provider / HandPool / HandGroup, вам необходимо создать новую 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);
  }

}
Другие вопросы по тегам