Unity3D Leap Motion - 将手置于静态姿势(姿势完成后无法旋转)
Unity3D Leap Motion - Put hand in static pose (Pose done just can't rotate)
我正在尝试将手设置为一系列姿势中的一个。我创建了一个捕获帧的脚本,序列化左手,然后将其存储为 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;
}
任何 suggestions/help 将不胜感激。
更新:
这是根据收到的建议生成的新代码。
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.
}
}
}
我们的 UnityModules 中内置了一些 convenience/extension 方法,可以更轻松地复制手部数据:
hand.Transform(LeapTransform transform)
-- 这将应用 对手的变换。
hand.SetTransform(Vector3 position, Quaternion rotation)
-- 这将变换一只手以将 PalmPosition 居中放置并变换整个手以与 rotation
对齐。
hand.CopyFrom(Hand other)
-- 这个将处理将数据(读取:姿势)从一只手复制到另一只手的所有细节。
在这种情况下,CopyFrom 和 SetTransform 将为您提供所需的数据。
编辑:因此,您问题的新方面涉及使用此数据来驱动 HandModel——在本例中为 CapsuleHand。对于我们当前版本的 Unity 资产,手部模型管道已完全关闭。您要么正在使用标准的 Leap 钻机来驱动真实的跟踪手,要么您有一点困难。
幸运的是,您可以 手动驱动 HandModel,但您必须小心以正确的顺序调用正确的方法。查看此示例脚本,它可以驱动它引用的 HandModel。 重要提示: 因为您要独立于正常的 Provider/HandPool/HandGroup 管道驱动 HandModel,所以您需要创建一个新的 HandModel——例如一个新的、重复的 CapsuleHand 对象——不在您的 Leap Rig 中,并且不由该 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);
}
}
我正在尝试将手设置为一系列姿势中的一个。我创建了一个捕获帧的脚本,序列化左手,然后将其存储为 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;
}
任何 suggestions/help 将不胜感激。
更新:
这是根据收到的建议生成的新代码。
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.
}
}
}
我们的 UnityModules 中内置了一些 convenience/extension 方法,可以更轻松地复制手部数据:
hand.Transform(LeapTransform transform)
-- 这将应用 对手的变换。
hand.SetTransform(Vector3 position, Quaternion rotation)
-- 这将变换一只手以将 PalmPosition 居中放置并变换整个手以与 rotation
对齐。
hand.CopyFrom(Hand other)
-- 这个将处理将数据(读取:姿势)从一只手复制到另一只手的所有细节。
在这种情况下,CopyFrom 和 SetTransform 将为您提供所需的数据。
编辑:因此,您问题的新方面涉及使用此数据来驱动 HandModel——在本例中为 CapsuleHand。对于我们当前版本的 Unity 资产,手部模型管道已完全关闭。您要么正在使用标准的 Leap 钻机来驱动真实的跟踪手,要么您有一点困难。
幸运的是,您可以 手动驱动 HandModel,但您必须小心以正确的顺序调用正确的方法。查看此示例脚本,它可以驱动它引用的 HandModel。 重要提示: 因为您要独立于正常的 Provider/HandPool/HandGroup 管道驱动 HandModel,所以您需要创建一个新的 HandModel——例如一个新的、重复的 CapsuleHand 对象——不在您的 Leap Rig 中,并且不由该 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);
}
}