Add transforms for rigging (might not be a good implementation)

This commit is contained in:
rodolpheh 2023-12-04 19:52:23 +00:00
parent 72c602fe67
commit 555200995b
12 changed files with 244 additions and 74 deletions

View File

@ -37,6 +37,9 @@ void UltraleapBone::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rotation"), &UltraleapBone::get_rotation);
ClassDB::bind_method(D_METHOD("set_rotation", "rotation"), &UltraleapBone::set_rotation);
ClassDB::bind_method(D_METHOD("get_global_transform"), &UltraleapBone::get_global_transform);
ClassDB::bind_method(D_METHOD("get_transform"), &UltraleapBone::get_transform);
ClassDB::add_property(
"UltraleapBone",
PropertyInfo(
@ -137,56 +140,95 @@ void UltraleapBone::_bind_methods() {
BIND_ENUM_CONSTANT(None);
}
void UltraleapBone::fill_bone_data(Ref<UltraleapBone> bone, Ref<UltraleapBone> previous_bone, LEAP_BONE* leap_bone, BoneType type, LEAP_PALM* palm) {
void UltraleapBone::fill_bone_data(Ref<UltraleapBone> bone, LEAP_BONE* leap_bone, BoneType type) {
bone->type = type;
bone->prev_joint = UltraleapTypes::ultraleap_vector3_to_godot_vector3(&leap_bone->prev_joint);
bone->next_joint = UltraleapTypes::ultraleap_vector3_to_godot_vector3(&leap_bone->next_joint);
bone->width = leap_bone->width / 1000;
bone->orientation = UltraleapTypes::ultraleap_quaternion_to_godot_quaternion(&leap_bone->rotation);
}
if (previous_bone != NULL) {
previous_bone->next_bone = bone;
bone->previous_bone = previous_bone;
}
void UltraleapBone::fill_bone_data(Ref<UltraleapBone> bone, Transform3D root, LEAP_BONE* leap_bone, BoneType type, Transform3D rigging_transform) {
// This function is for metacarpals or arms, this way root can be changed to be anything
// If palm is defined, this bone is either a metacarpal or an arm. We still compute relative transform though.
if (palm != NULL) {
Transform3D previous_bone_transform = Transform3D(
Basis(UltraleapTypes::ultraleap_quaternion_to_godot_quaternion(&palm->orientation)),
UltraleapTypes::ultraleap_vector3_to_godot_vector3(&palm->position)
);
fill_bone_data(bone, leap_bone, type);
Transform3D current_bone_transform = Transform3D(
Basis(bone->orientation),
type == UltraleapBone::BoneType::Arm ? bone->next_joint : bone->prev_joint
);
// Set rotation and translation
Transform3D current_bone_transform = Transform3D(
Basis(bone->orientation),
type == UltraleapBone::BoneType::Arm ? bone->next_joint : bone->prev_joint
);
Transform3D relative_bone_transform = previous_bone_transform.inverse() * current_bone_transform;
bone->rotation = relative_bone_transform.basis.get_quaternion();
bone->translation = relative_bone_transform.origin;
}
Transform3D relative_bone_transform = root.inverse() * current_bone_transform;
bone->rotation = relative_bone_transform.basis.get_quaternion();
bone->translation = relative_bone_transform.origin;
compute_bone_data(bone);
// Set transform for rigging
bone->set_global_transform(current_bone_transform * rigging_transform, root * rigging_transform);
}
void UltraleapBone::fill_bone_data(Ref<UltraleapBone> bone, Ref<UltraleapBone> previous_bone, LEAP_BONE* leap_bone, BoneType type, Transform3D rigging_transform) {
fill_bone_data(bone, leap_bone, type);
// Set previous bone (and set its next bone to current bone)
previous_bone->next_bone = bone;
bone->previous_bone = previous_bone;
// Set rotation and translation
Transform3D previous_bone_transform = Transform3D(
Basis(bone->previous_bone->orientation),
bone->previous_bone->prev_joint
);
Transform3D current_bone_transform = Transform3D(
Basis(bone->orientation),
bone->prev_joint
);
Transform3D relative_bone_transform = previous_bone_transform.inverse() * current_bone_transform;
bone->rotation = relative_bone_transform.basis.get_quaternion();
bone->translation = relative_bone_transform.origin;
compute_bone_data(bone);
// Set transform for rigging
bone->set_global_transform(current_bone_transform * rigging_transform);
}
void UltraleapBone::compute_bone_data(Ref<UltraleapBone> bone) {
bone->center = (bone->prev_joint + bone->next_joint) / 2.0f;
bone->length = (bone->prev_joint - bone->next_joint).length();
}
// If previous_bone is defined, we compute the transform from the previous bone to the current bone
if (bone->previous_bone != NULL) {
Transform3D previous_bone_transform = Transform3D(
Basis(bone->previous_bone->orientation),
bone->previous_bone->prev_joint
);
Transform3D current_bone_transform = Transform3D(
Basis(bone->orientation),
bone->prev_joint
);
Transform3D relative_bone_transform = previous_bone_transform.inverse() * current_bone_transform;
bone->rotation = relative_bone_transform.basis.get_quaternion();
bone->translation = relative_bone_transform.origin;
void UltraleapBone::set_transform(Transform3D new_transform) {
if (previous_bone != NULL) {
global_transform = previous_bone->global_transform * new_transform;
}
// Ideally, we want to add a flag to allow propagating the transform
// to next bones
}
void UltraleapBone::set_global_transform(Transform3D new_transform) {
global_transform = new_transform;
// Set the local transform based on the global transform with the previous bone
if (previous_bone != NULL) {
transform = previous_bone->global_transform.inverse() * new_transform;
}
// Ideally, we want to add a flag to allow propagating the transform
// to next bones
}
void UltraleapBone::set_global_transform(Transform3D new_transform, Transform3D parent) {
global_transform = new_transform;
// Set the local transform based on the global transform with the previous transform
transform = parent.inverse() * new_transform;
// Ideally, we want to add a flag to allow propagating the transform
// to next bones
}

View File

@ -10,6 +10,7 @@
#include <godot_cpp/variant/vector3.hpp>
#include <godot_cpp/variant/quaternion.hpp>
#include <godot_cpp/variant/transform3d.hpp>
#include <LeapC.h>
@ -44,6 +45,16 @@ public:
Ref<UltraleapBone> previous_bone;
Ref<UltraleapBone> next_bone;
Transform3D transform;
Transform3D global_transform;
Transform3D get_global_transform() { return global_transform; }
void set_global_transform(Transform3D new_transform);
void set_global_transform(Transform3D new_transform, Transform3D parent);
Transform3D get_transform() { return transform; }
void set_transform(Transform3D new_transform);
// Getters / Setters
BoneType get_type() { return type; }
@ -73,7 +84,9 @@ public:
Quaternion get_rotation() { return rotation; }
void set_rotation(Quaternion value) { rotation = value; }
static void fill_bone_data(Ref<UltraleapBone> bone, Ref<UltraleapBone> previous_bone, LEAP_BONE* leap_bone, BoneType type, LEAP_PALM* palm = NULL);
static void fill_bone_data(Ref<UltraleapBone> bone, LEAP_BONE* leap_bone, BoneType type);
static void fill_bone_data(Ref<UltraleapBone> bone, Transform3D root, LEAP_BONE* leap_bone, BoneType type, Transform3D rigging_transform);
static void fill_bone_data(Ref<UltraleapBone> bone, Ref<UltraleapBone> previous_bone, LEAP_BONE* leap_bone, BoneType type, Transform3D rigging_transform);
static void compute_bone_data(Ref<UltraleapBone> bone);
protected:
static void _bind_methods();

View File

@ -14,6 +14,7 @@
using namespace godot;
UltraleapDevice::~UltraleapDevice() {
UtilityFunctions::print("Destroying device");
unsubscribe();
close();
}
@ -37,6 +38,9 @@ void UltraleapDevice::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_left_image"), &UltraleapDevice::get_left_image);
ClassDB::bind_method(D_METHOD("get_right_image"), &UltraleapDevice::get_right_image);
ClassDB::bind_method(D_METHOD("get_rigging_transform"), &UltraleapDevice::get_rigging_transform);
ClassDB::bind_method(D_METHOD("set_rigging_transform"), &UltraleapDevice::set_rigging_transform);
ClassDB::bind_method(D_METHOD("open"), &UltraleapDevice::open);
ClassDB::bind_method(D_METHOD("close"), &UltraleapDevice::close);
ClassDB::bind_method(D_METHOD("subscribe"), &UltraleapDevice::subscribe);
@ -72,6 +76,16 @@ void UltraleapDevice::_bind_methods() {
"get_id"
);
ClassDB::add_property(
"UltraleapDevice",
PropertyInfo(
Variant::TRANSFORM3D,
"rigging_transform"
),
"set_rigging_transform",
"get_rigging_transform"
);
// Signals
ClassDB::add_signal(
@ -201,7 +215,7 @@ void UltraleapDevice::close() {
void UltraleapDevice::on_frame_received(const LEAP_TRACKING_EVENT* frame) {
Ref<UltraleapFrame> new_frame = Ref<UltraleapFrame>(memnew(UltraleapFrame));
UltraleapFrame::fill_frame_data(new_frame, frame);
UltraleapFrame::fill_frame_data(new_frame, frame, rigging_transform);
frame_mutex.lock();
last_frame_ref = new_frame;
frame_mutex.unlock();
@ -252,7 +266,7 @@ Ref<UltraleapFrame> UltraleapDevice::get_interpolated_frame(int64_t time) {
result = LeapInterpolateFrameEx(connection, device, time, interpolatedFrame, targetFrameSize);
if (result == eLeapRS_Success) {
Ref<UltraleapFrame> new_frame = memnew(UltraleapFrame);
UltraleapFrame::fill_frame_data(new_frame, interpolatedFrame);
UltraleapFrame::fill_frame_data(new_frame, interpolatedFrame, rigging_transform);
return new_frame;
}
}

View File

@ -53,6 +53,9 @@ public:
uint32_t get_id() { return id; }
void set_id(uint32_t value) { id = value; }
Transform3D get_rigging_transform() { return rigging_transform; }
void set_rigging_transform(Transform3D t) { rigging_transform = t; }
void subscribe();
void unsubscribe();
@ -83,6 +86,8 @@ private:
bool opened = false;
bool subscribed = false;
bool interpolation_available = false;
Transform3D rigging_transform;
};

View File

@ -23,23 +23,14 @@ void UltraleapDeviceNode::device_added(Ref<UltraleapDevice> device) {
// If user has chosen "Any", the strategy here is to use the newly connected device
if (chosen_serial == 0) {
if (current_device != NULL && should_run()) {
unsubscribe_and_close();
}
current_device = device;
if (should_run()) {
open_and_subscribe();
}
setup_device(device);
}
// Otherwise we check for the serial and subscribe to the device
else {
if (get_tracking() != NULL) {
if ((chosen_serial - 1) < get_tracking()->devices->size()) {
if (devices[chosen_serial - 1] == device->serial) {
current_device = device;
if (should_run()) {
open_and_subscribe();
}
setup_device(device);
}
}
}
@ -56,7 +47,7 @@ void UltraleapDeviceNode::device_removed(Ref<UltraleapDevice> device) {
if (current_device != NULL) {
if (device->serial == current_device->serial) {
unsubscribe_and_close();
current_device = Ref<UltraleapDevice>();
current_device = Ref<UltraleapDevice>(NULL);
}
}
@ -67,15 +58,34 @@ void UltraleapDeviceNode::device_removed(Ref<UltraleapDevice> device) {
void UltraleapDeviceNode::open_and_subscribe() {
if (current_device != NULL) {
current_device->open();
current_device->subscribe();
if (!current_device->is_opened()) {
current_device->open();
}
if (!current_device->is_subscribed()) {
current_device->subscribe();
}
}
}
void UltraleapDeviceNode::unsubscribe_and_close() {
if (current_device != NULL) {
current_device->unsubscribe();
current_device->close();
if (current_device->is_subscribed()) {
current_device->unsubscribe();
}
if (current_device->is_opened()) {
current_device->close();
}
}
}
void UltraleapDeviceNode::setup_device(Ref<UltraleapDevice> device) {
if (current_device != NULL && should_run()) {
unsubscribe_and_close();
}
current_device = device;
current_device->set_rigging_transform(rigging_transform);
if (should_run()) {
open_and_subscribe();
}
}
@ -104,6 +114,9 @@ void UltraleapDeviceNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_run_in_editor", "run_in_editor"), &UltraleapDeviceNode::set_run_in_editor, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_run_in_editor"), &UltraleapDeviceNode::get_run_in_editor);
ClassDB::bind_method(D_METHOD("get_rigging_transform"), &UltraleapDeviceNode::get_rigging_transform);
ClassDB::bind_method(D_METHOD("set_rigging_transform"), &UltraleapDeviceNode::set_rigging_transform);
ClassDB::add_property(
"UltraleapDeviceNode",
PropertyInfo(
@ -133,6 +146,25 @@ void UltraleapDeviceNode::_bind_methods() {
"get_run_in_editor"
);
ClassDB::add_property_group(
"UltraleapDeviceNode",
"Rigging",
"rigging"
);
ClassDB::add_property(
"UltraleapDeviceNode",
PropertyInfo(
Variant::TRANSFORM3D,
"rigging_transform",
PROPERTY_HINT_NONE,
"",
PROPERTY_USAGE_NO_EDITOR
),
"set_rigging_transform",
"get_rigging_transform"
);
// Signals
ClassDB::add_signal(
@ -244,6 +276,24 @@ void UltraleapDeviceNode::_get_property_list(List<PropertyInfo> *p_list) const {
String(",").join(devices).insert(0, "Any,")
)
);
p_list->push_back(
PropertyInfo(
Variant::INT,
"rigging/presets",
PROPERTY_HINT_ENUM,
"Blender,Custom"
)
);
if (rigging_transform_type == 1) {
p_list->push_back(
PropertyInfo(
Variant::TRANSFORM3D,
"rigging/transform"
)
);
}
}
bool UltraleapDeviceNode::_get(const StringName &p_name, Variant &r_ret) const {
@ -252,6 +302,14 @@ bool UltraleapDeviceNode::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = chosen_serial;
return true;
}
else if (name == "rigging/presets") {
r_ret = rigging_transform_type;
return true;
}
else if (name == "rigging/transform") {
r_ret = rigging_transform;
return true;
}
return false;
}
@ -262,6 +320,15 @@ bool UltraleapDeviceNode::_set(const StringName &p_name, const Variant &p_value)
notify_property_list_changed();
return true;
}
else if (name == "rigging/presets") {
rigging_transform_type = p_value;
notify_property_list_changed();
return true;
}
else if (name == "rigging/transform") {
rigging_transform = p_value;
return true;
}
return false;
}
@ -324,4 +391,20 @@ void UltraleapDeviceNode::set_run_in_editor(bool value) {
if (should_run() && current_device != NULL) {
open_and_subscribe();
}
}
Transform3D UltraleapDeviceNode::get_rigging_transform() {
if (current_device != NULL) {
return current_device->get_rigging_transform();
}
return rigging_transform;
}
void UltraleapDeviceNode::set_rigging_transform(Transform3D t) {
if (current_device != NULL) {
current_device->set_rigging_transform(t);
}
rigging_transform = t;
}

View File

@ -49,6 +49,9 @@ public:
void set_run_in_editor(bool value);
bool get_run_in_editor() { return run_in_editor; }
Transform3D get_rigging_transform();
void set_rigging_transform(Transform3D t);
protected:
static void _bind_methods();
@ -64,8 +67,14 @@ private:
void open_and_subscribe();
void unsubscribe_and_close();
void setup_device(Ref<UltraleapDevice> device);
bool should_run();
int rigging_transform_type = 0;
// Used as a cache for when/if no device is connected yet (in editor for example)
Transform3D rigging_transform;
};
#endif

View File

@ -126,7 +126,7 @@ void UltraleapDigit::_bind_methods() {
BIND_ENUM_CONSTANT(None);
}
void UltraleapDigit::fill_digit_data(Ref<UltraleapDigit> ul_digit, LEAP_DIGIT* digit, FingerType type, LEAP_PALM* palm) {
void UltraleapDigit::fill_digit_data(Ref<UltraleapDigit> ul_digit, LEAP_DIGIT* digit, FingerType type, Transform3D root, Transform3D rigging_transform) {
if (ul_digit->metacarpal_ref == NULL) {
ul_digit->metacarpal_ref = Ref<UltraleapBone>(memnew(UltraleapBone));
ul_digit->proximal_ref = Ref<UltraleapBone>(memnew(UltraleapBone));
@ -139,16 +139,16 @@ void UltraleapDigit::fill_digit_data(Ref<UltraleapDigit> ul_digit, LEAP_DIGIT* d
// We need some extra logic here because the metacarpal doesn't exist in Ultraleap's hand data
if (type == UltraleapDigit::FingerType::Thumb) {
UltraleapBone::fill_bone_data(ul_digit->proximal_ref, NULL, &digit->proximal, UltraleapBone::BoneType::Proximal, palm);
UltraleapBone::fill_bone_data(ul_digit->proximal_ref, root, &digit->proximal, UltraleapBone::BoneType::Proximal, rigging_transform);
// Still filling it out but shouldn't be used
UltraleapBone::fill_bone_data(ul_digit->metacarpal_ref, NULL, &digit->metacarpal, UltraleapBone::BoneType::Metacarpal, palm);
UltraleapBone::fill_bone_data(ul_digit->metacarpal_ref, root, &digit->metacarpal, UltraleapBone::BoneType::Metacarpal, rigging_transform);
}
else {
UltraleapBone::fill_bone_data(ul_digit->metacarpal_ref, NULL, &digit->metacarpal, UltraleapBone::BoneType::Metacarpal, palm);
UltraleapBone::fill_bone_data(ul_digit->proximal_ref, ul_digit->metacarpal_ref, &digit->proximal, UltraleapBone::BoneType::Proximal);
UltraleapBone::fill_bone_data(ul_digit->metacarpal_ref, root, &digit->metacarpal, UltraleapBone::BoneType::Metacarpal, rigging_transform);
UltraleapBone::fill_bone_data(ul_digit->proximal_ref, ul_digit->metacarpal_ref, &digit->proximal, UltraleapBone::BoneType::Proximal, rigging_transform);
}
UltraleapBone::fill_bone_data(ul_digit->intermediate_ref, ul_digit->proximal_ref, &digit->intermediate, UltraleapBone::BoneType::Intermediate);
UltraleapBone::fill_bone_data(ul_digit->distal_ref, ul_digit->intermediate_ref, &digit->distal, UltraleapBone::BoneType::Distal);
UltraleapBone::fill_bone_data(ul_digit->intermediate_ref, ul_digit->proximal_ref, &digit->intermediate, UltraleapBone::BoneType::Intermediate, rigging_transform);
UltraleapBone::fill_bone_data(ul_digit->distal_ref, ul_digit->intermediate_ref, &digit->distal, UltraleapBone::BoneType::Distal, rigging_transform);
}

View File

@ -8,6 +8,7 @@
#include <godot_cpp/variant/vector3.hpp>
#include <godot_cpp/variant/quaternion.hpp>
#include <godot_cpp/variant/transform3d.hpp>
#include <LeapC.h>
@ -66,7 +67,7 @@ public:
return bones;
}
static void fill_digit_data(Ref<UltraleapDigit> ul_digit, LEAP_DIGIT* digit, FingerType type, LEAP_PALM* palm);
static void fill_digit_data(Ref<UltraleapDigit> ul_digit, LEAP_DIGIT* digit, FingerType type, Transform3D root, Transform3D rigging_transform);
protected:
static void _bind_methods();

View File

@ -80,7 +80,7 @@ void UltraleapFrame::_bind_methods() {
);
}
void UltraleapFrame::fill_frame_data(Ref<UltraleapFrame> ul_frame, const LEAP_TRACKING_EVENT* frame) {
void UltraleapFrame::fill_frame_data(Ref<UltraleapFrame> ul_frame, const LEAP_TRACKING_EVENT* frame, Transform3D rigging_transform) {
ul_frame->id = frame->tracking_frame_id;
ul_frame->framerate = frame->framerate;
ul_frame->is_left_hand_visible = false;
@ -92,14 +92,14 @@ void UltraleapFrame::fill_frame_data(Ref<UltraleapFrame> ul_frame, const LEAP_TR
if (ul_frame->get_left_hand() == NULL) {
ul_frame->set_left_hand(Ref<UltraleapHand>(memnew(UltraleapHand)));
}
UltraleapHand::fill_hand_data(ul_frame->get_left_hand(), &frame->pHands[i]);
UltraleapHand::fill_hand_data(ul_frame->get_left_hand(), &frame->pHands[i], rigging_transform);
ul_frame->is_left_hand_visible = true;
}
else {
if (ul_frame->get_right_hand() == NULL) {
ul_frame->set_right_hand(Ref<UltraleapHand>(memnew(UltraleapHand)));
}
UltraleapHand::fill_hand_data(ul_frame->get_right_hand(), &frame->pHands[i]);
UltraleapHand::fill_hand_data(ul_frame->get_right_hand(), &frame->pHands[i], rigging_transform);
ul_frame->is_right_hand_visible = true;
}
}

View File

@ -41,7 +41,7 @@ public:
float get_framerate() { return framerate; }
void set_framerate(float value) { return; }
static void fill_frame_data(Ref<UltraleapFrame> ul_frame, const LEAP_TRACKING_EVENT* frame);
static void fill_frame_data(Ref<UltraleapFrame> ul_frame, const LEAP_TRACKING_EVENT* frame, Transform3D rigging_transform);
protected:
static void _bind_methods();

View File

@ -238,7 +238,7 @@ void UltraleapHand::_bind_methods() {
);
};
void UltraleapHand::fill_hand_data(Ref<UltraleapHand> ul_hand, LEAP_HAND* hand) {
void UltraleapHand::fill_hand_data(Ref<UltraleapHand> ul_hand, LEAP_HAND* hand, Transform3D rigging_transform) {
// For now we just check if the arm ref is not set
if (ul_hand->arm_ref == NULL) {
ul_hand->arm_ref = Ref<UltraleapBone>(memnew(UltraleapBone));
@ -260,13 +260,16 @@ void UltraleapHand::fill_hand_data(Ref<UltraleapHand> ul_hand, LEAP_HAND* hand)
UltraleapPalm::fill_palm_data(ul_hand->palm_ref, &hand->palm);
UltraleapDigit::fill_digit_data(ul_hand->get_thumb(), &hand->thumb, UltraleapDigit::FingerType::Thumb, &hand->palm);
UltraleapDigit::fill_digit_data(ul_hand->get_index(), &hand->index, UltraleapDigit::FingerType::Index, &hand->palm);
UltraleapDigit::fill_digit_data(ul_hand->get_middle(), &hand->middle, UltraleapDigit::FingerType::Middle, &hand->palm);
UltraleapDigit::fill_digit_data(ul_hand->get_ring(), &hand->ring, UltraleapDigit::FingerType::Ring, &hand->palm);
UltraleapDigit::fill_digit_data(ul_hand->get_pinky(), &hand->pinky, UltraleapDigit::FingerType::Pinky, &hand->palm);
ul_hand->wrist = Transform3D(
Basis(UltraleapTypes::ultraleap_quaternion_to_godot_quaternion(&hand->palm.orientation)),
UltraleapTypes::ultraleap_vector3_to_godot_vector3(&hand->arm.next_joint)
);
UltraleapBone::fill_bone_data(ul_hand->arm_ref, NULL, &hand->arm, UltraleapBone::BoneType::Arm, &hand->palm);
UltraleapDigit::fill_digit_data(ul_hand->get_thumb(), &hand->thumb, UltraleapDigit::FingerType::Thumb, ul_hand->wrist, rigging_transform);
UltraleapDigit::fill_digit_data(ul_hand->get_index(), &hand->index, UltraleapDigit::FingerType::Index, ul_hand->wrist, rigging_transform);
UltraleapDigit::fill_digit_data(ul_hand->get_middle(), &hand->middle, UltraleapDigit::FingerType::Middle, ul_hand->wrist, rigging_transform);
UltraleapDigit::fill_digit_data(ul_hand->get_ring(), &hand->ring, UltraleapDigit::FingerType::Ring, ul_hand->wrist, rigging_transform);
UltraleapDigit::fill_digit_data(ul_hand->get_pinky(), &hand->pinky, UltraleapDigit::FingerType::Pinky, ul_hand->wrist, rigging_transform);
ul_hand->wrist = Transform3D(Basis(ul_hand->palm_ref->orientation), ul_hand->arm_ref->next_joint);
UltraleapBone::fill_bone_data(ul_hand->arm_ref, ul_hand->wrist, &hand->arm, UltraleapBone::BoneType::Arm, rigging_transform);
}

View File

@ -92,7 +92,7 @@ public:
Transform3D get_wrist() { return wrist; }
void set_wrist(Transform3D value) { wrist = value; }
static void fill_hand_data(Ref<UltraleapHand> ul_hand, LEAP_HAND* hand);
static void fill_hand_data(Ref<UltraleapHand> ul_hand, LEAP_HAND* hand, Transform3D rigging_transform);
protected:
static void _bind_methods();