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);
}
}