mirror of
https://github.com/godotengine/godot.git
synced 2024-11-22 12:12:28 +00:00
add rest fixer to importer retarget
This commit is contained in:
parent
9904a9db5a
commit
f3af3aedfe
@ -9,6 +9,13 @@
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="find_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<argument index="0" name="bone_name" type="StringName" />
|
||||
<description>
|
||||
Returns the bone index that matches [code]bone_name[/code] as its name.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_bone_name" qualifiers="const">
|
||||
<return type="StringName" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
@ -17,6 +24,20 @@
|
||||
In the retargeting process, the returned bone name is the bone name of the target skeleton.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_bone_parent" qualifiers="const">
|
||||
<return type="StringName" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<description>
|
||||
Returns the name of the bone which is the parent to the bone at [code]bone_idx[/code]. The result is empty if the bone has no parent.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_bone_tail" qualifiers="const">
|
||||
<return type="StringName" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<description>
|
||||
Returns the name of the bone which is the tail of the bone at [code]bone_idx[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_group" qualifiers="const">
|
||||
<return type="StringName" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
@ -39,6 +60,20 @@
|
||||
This is the offset with origin at the top left corner of the square.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_reference_pose" qualifiers="const">
|
||||
<return type="Transform3D" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<description>
|
||||
Returns the reference pose transform for bone [code]bone_idx[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_tail_direction" qualifiers="const">
|
||||
<return type="int" enum="SkeletonProfile.TailDirection" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<description>
|
||||
Returns the tail direction of the bone at [code]bone_idx[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_texture" qualifiers="const">
|
||||
<return type="Texture2D" />
|
||||
<argument index="0" name="group_idx" type="int" />
|
||||
@ -55,6 +90,22 @@
|
||||
In the retargeting process, the setting bone name is the bone name of the target skeleton.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_bone_parent">
|
||||
<return type="void" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<argument index="1" name="bone_parent" type="StringName" />
|
||||
<description>
|
||||
Sets the bone with name [code]bone_parent[/code] as the parent of the bone at [code]bone_idx[/code]. If an empty string is passed, then the bone has no parent.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_bone_tail">
|
||||
<return type="void" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<argument index="1" name="bone_tail" type="StringName" />
|
||||
<description>
|
||||
Sets the bone with name [code]bone_tail[/code] as the tail of the bone at [code]bone_idx[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_group">
|
||||
<return type="void" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
@ -80,6 +131,23 @@
|
||||
This is the offset with origin at the top left corner of the square.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_reference_pose">
|
||||
<return type="void" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<argument index="1" name="bone_name" type="Transform3D" />
|
||||
<description>
|
||||
Sets the reference pose transform for bone [code]bone_idx[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_tail_direction">
|
||||
<return type="void" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<argument index="1" name="tail_direction" type="int" enum="SkeletonProfile.TailDirection" />
|
||||
<description>
|
||||
Sets the tail direction of the bone at [code]bone_idx[/code].
|
||||
[b]Note:[/b] This only specifies the method of calculation. The actual coordinates required should be stored in an external skeleton, so the calculation itself needs to be done externally.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_texture">
|
||||
<return type="void" />
|
||||
<argument index="0" name="group_idx" type="int" />
|
||||
@ -103,4 +171,15 @@
|
||||
</description>
|
||||
</signal>
|
||||
</signals>
|
||||
<constants>
|
||||
<constant name="TAIL_DIRECTION_AVERAGE_CHILDREN" value="0" enum="TailDirection">
|
||||
Direction to the average coordinates of bone children.
|
||||
</constant>
|
||||
<constant name="TAIL_DIRECTION_SPECIFIC_CHILD" value="1" enum="TailDirection">
|
||||
Direction to the coordinates of specified bone child.
|
||||
</constant>
|
||||
<constant name="TAIL_DIRECTION_END" value="2" enum="TailDirection">
|
||||
Direction is not calculated.
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
||||
|
@ -39,6 +39,8 @@
|
||||
void PostImportPluginSkeletonRenamer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {
|
||||
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones"), true));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/unique_node/make_unique"), true));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::STRING, "retarget/bone_renamer/unique_node/skeleton_name"), "GeneralSkeleton"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,6 +139,38 @@ void PostImportPluginSkeletonRenamer::internal_process(InternalImportCategory p_
|
||||
nd->callp("_notify_skeleton_bones_renamed", argptrs, argcount, ce);
|
||||
}
|
||||
}
|
||||
|
||||
// Make unique skeleton.
|
||||
if (bool(p_options["retarget/bone_renamer/unique_node/make_unique"])) {
|
||||
String unique_name = String(p_options["retarget/bone_renamer/unique_node/skeleton_name"]);
|
||||
ERR_FAIL_COND_MSG(unique_name == String(), "Skeleton unique name cannot be empty.");
|
||||
|
||||
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
|
||||
while (nodes.size()) {
|
||||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
|
||||
List<StringName> anims;
|
||||
ap->get_animation_list(&anims);
|
||||
for (const StringName &name : anims) {
|
||||
Ref<Animation> anim = ap->get_animation(name);
|
||||
int track_len = anim->get_track_count();
|
||||
for (int i = 0; i < track_len; i++) {
|
||||
if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) {
|
||||
continue;
|
||||
}
|
||||
String track_path = String(anim->track_get_path(i).get_concatenated_names());
|
||||
Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path));
|
||||
if (node) {
|
||||
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
|
||||
if (track_skeleton && track_skeleton == skeleton) {
|
||||
anim->track_set_path(i, String("%") + unique_name + String(":") + anim->track_get_path(i).get_concatenated_subnames());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
skeleton->set_name(unique_name);
|
||||
skeleton->set_unique_name_in_owner(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
418
editor/import/post_import_plugin_skeleton_rest_fixer.cpp
Normal file
418
editor/import/post_import_plugin_skeleton_rest_fixer.cpp
Normal file
@ -0,0 +1,418 @@
|
||||
/*************************************************************************/
|
||||
/* post_import_plugin_skeleton_rest_fixer.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "post_import_plugin_skeleton_rest_fixer.h"
|
||||
|
||||
#include "editor/import/scene_import_settings.h"
|
||||
#include "scene/3d/importer_mesh_instance_3d.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/resources/animation.h"
|
||||
#include "scene/resources/bone_map.h"
|
||||
|
||||
void PostImportPluginSkeletonRestFixer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {
|
||||
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/overwrite_axis"), true));
|
||||
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/fix_silhouette/enable"), false));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "retarget/rest_fixer/fix_silhouette/threshold"), 15));
|
||||
|
||||
// TODO: PostImportPlugin need to be implemented such as validate_option(PropertyInfo &property, const Dictionary &p_options).
|
||||
// get_internal_option_visibility() is not sufficient because it can only retrieve options implemented in the core and can only read option values.
|
||||
// r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/filter", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::STRING_NAME, PROPERTY_HINT_ENUM, "Hips,Spine,Chest")), Array()));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/fix_silhouette/filter", PROPERTY_HINT_ARRAY_TYPE, "StringName"), Array()));
|
||||
}
|
||||
}
|
||||
|
||||
void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) {
|
||||
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
|
||||
// Prepare objects.
|
||||
Object *map = p_options["retarget/bone_map"].get_validated_object();
|
||||
if (!map) {
|
||||
return;
|
||||
}
|
||||
BoneMap *bone_map = Object::cast_to<BoneMap>(map);
|
||||
Ref<SkeletonProfile> profile = bone_map->get_profile();
|
||||
if (!profile.is_valid()) {
|
||||
return;
|
||||
}
|
||||
Skeleton3D *src_skeleton = Object::cast_to<Skeleton3D>(p_node);
|
||||
if (!src_skeleton) {
|
||||
return;
|
||||
}
|
||||
bool is_renamed = bool(p_options["retarget/bone_renamer/rename_bones"]);
|
||||
Array filter = p_options["retarget/rest_fixer/fix_silhouette/filter"];
|
||||
bool is_rest_changed = false;
|
||||
|
||||
// Build profile skeleton.
|
||||
Skeleton3D *prof_skeleton = memnew(Skeleton3D);
|
||||
{
|
||||
int prof_bone_len = profile->get_bone_size();
|
||||
// Add single bones.
|
||||
for (int i = 0; i < prof_bone_len; i++) {
|
||||
prof_skeleton->add_bone(profile->get_bone_name(i));
|
||||
prof_skeleton->set_bone_rest(i, profile->get_reference_pose(i));
|
||||
}
|
||||
// Set parents.
|
||||
for (int i = 0; i < prof_bone_len; i++) {
|
||||
int parent = profile->find_bone(profile->get_bone_parent(i));
|
||||
if (parent >= 0) {
|
||||
prof_skeleton->set_bone_parent(i, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Complement Rotation track for compatibility between defference rests.
|
||||
{
|
||||
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
|
||||
while (nodes.size()) {
|
||||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
|
||||
List<StringName> anims;
|
||||
ap->get_animation_list(&anims);
|
||||
for (const StringName &name : anims) {
|
||||
Ref<Animation> anim = ap->get_animation(name);
|
||||
int track_len = anim->get_track_count();
|
||||
|
||||
// Detect does the animetion have skeleton's TRS track.
|
||||
String track_path;
|
||||
bool found_skeleton = false;
|
||||
for (int i = 0; i < track_len; i++) {
|
||||
if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) {
|
||||
continue;
|
||||
}
|
||||
track_path = String(anim->track_get_path(i).get_concatenated_names());
|
||||
Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path));
|
||||
if (node) {
|
||||
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
|
||||
if (track_skeleton && track_skeleton == src_skeleton) {
|
||||
found_skeleton = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_skeleton) {
|
||||
// Search and insert rot track if it doesn't exist.
|
||||
for (int prof_idx = 0; prof_idx < prof_skeleton->get_bone_count(); prof_idx++) {
|
||||
String bone_name = is_renamed ? prof_skeleton->get_bone_name(prof_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_idx)));
|
||||
if (bone_name == String()) {
|
||||
continue;
|
||||
}
|
||||
int src_idx = src_skeleton->find_bone(bone_name);
|
||||
if (src_idx == -1) {
|
||||
continue;
|
||||
}
|
||||
String insert_path = track_path + ":" + bone_name;
|
||||
int rot_track = anim->find_track(insert_path, Animation::TYPE_ROTATION_3D);
|
||||
if (rot_track == -1) {
|
||||
int track = anim->add_track(Animation::TYPE_ROTATION_3D);
|
||||
anim->track_set_path(track, insert_path);
|
||||
anim->rotation_track_insert_key(track, 0, src_skeleton->get_bone_rest(src_idx).basis.get_rotation_quaternion());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix silhouette.
|
||||
Vector<Transform3D> silhouette_diff; // Transform values to be ignored when overwrite axis.
|
||||
silhouette_diff.resize(src_skeleton->get_bone_count());
|
||||
Transform3D *silhouette_diff_w = silhouette_diff.ptrw();
|
||||
if (bool(p_options["retarget/rest_fixer/fix_silhouette/enable"])) {
|
||||
LocalVector<Transform3D> old_skeleton_global_rest;
|
||||
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
|
||||
old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
|
||||
}
|
||||
|
||||
Vector<int> bones_to_process = prof_skeleton->get_parentless_bones();
|
||||
while (bones_to_process.size() > 0) {
|
||||
int prof_idx = bones_to_process[0];
|
||||
bones_to_process.erase(prof_idx);
|
||||
Vector<int> prof_children = prof_skeleton->get_bone_children(prof_idx);
|
||||
for (int i = 0; i < prof_children.size(); i++) {
|
||||
bones_to_process.push_back(prof_children[i]);
|
||||
}
|
||||
|
||||
// Calc virtual/looking direction with origins.
|
||||
bool is_filtered = false;
|
||||
for (int i = 0; i < filter.size(); i++) {
|
||||
if (String(filter[i]) == prof_skeleton->get_bone_name(prof_idx)) {
|
||||
is_filtered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_filtered) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int src_idx = src_skeleton->find_bone(is_renamed ? prof_skeleton->get_bone_name(prof_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_idx))));
|
||||
if (src_idx < 0 || profile->get_tail_direction(prof_idx) == SkeletonProfile::TAIL_DIRECTION_END) {
|
||||
continue;
|
||||
}
|
||||
Vector3 prof_tail;
|
||||
Vector3 src_tail;
|
||||
if (profile->get_tail_direction(prof_idx) == SkeletonProfile::TAIL_DIRECTION_AVERAGE_CHILDREN) {
|
||||
PackedInt32Array prof_bone_children = prof_skeleton->get_bone_children(prof_idx);
|
||||
int children_size = prof_bone_children.size();
|
||||
if (children_size == 0) {
|
||||
continue;
|
||||
}
|
||||
bool exist_all_children = true;
|
||||
for (int i = 0; i < children_size; i++) {
|
||||
int prof_child_idx = prof_bone_children[i];
|
||||
int src_child_idx = src_skeleton->find_bone(is_renamed ? prof_skeleton->get_bone_name(prof_child_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_child_idx))));
|
||||
if (src_child_idx < 0) {
|
||||
exist_all_children = false;
|
||||
break;
|
||||
}
|
||||
prof_tail = prof_tail + prof_skeleton->get_bone_global_rest(prof_child_idx).origin;
|
||||
src_tail = src_tail + src_skeleton->get_bone_global_rest(src_child_idx).origin;
|
||||
}
|
||||
if (!exist_all_children) {
|
||||
continue;
|
||||
}
|
||||
prof_tail = prof_tail / children_size;
|
||||
src_tail = src_tail / children_size;
|
||||
}
|
||||
if (profile->get_tail_direction(prof_idx) == SkeletonProfile::TAIL_DIRECTION_SPECIFIC_CHILD) {
|
||||
int prof_tail_idx = prof_skeleton->find_bone(profile->get_bone_tail(prof_idx));
|
||||
if (prof_tail_idx < 0) {
|
||||
continue;
|
||||
}
|
||||
int src_tail_idx = src_skeleton->find_bone(is_renamed ? prof_skeleton->get_bone_name(prof_tail_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_tail_idx))));
|
||||
if (src_tail_idx < 0) {
|
||||
continue;
|
||||
}
|
||||
prof_tail = prof_skeleton->get_bone_global_rest(prof_tail_idx).origin;
|
||||
src_tail = src_skeleton->get_bone_global_rest(src_tail_idx).origin;
|
||||
}
|
||||
|
||||
Vector3 prof_head = prof_skeleton->get_bone_global_rest(prof_idx).origin;
|
||||
Vector3 src_head = src_skeleton->get_bone_global_rest(src_idx).origin;
|
||||
|
||||
Vector3 prof_dir = prof_tail - prof_head;
|
||||
Vector3 src_dir = src_tail - src_head;
|
||||
|
||||
// Rotate rest.
|
||||
if (Math::abs(Math::rad2deg(src_dir.angle_to(prof_dir))) > float(p_options["retarget/rest_fixer/fix_silhouette/threshold"])) {
|
||||
// Get rotation difference.
|
||||
Vector3 up_vec; // Need to rotate other than roll axis.
|
||||
switch (Vector3(abs(src_dir.x), abs(src_dir.y), abs(src_dir.z)).min_axis_index()) {
|
||||
case Vector3::AXIS_X: {
|
||||
up_vec = Vector3(1, 0, 0);
|
||||
} break;
|
||||
case Vector3::AXIS_Y: {
|
||||
up_vec = Vector3(0, 1, 0);
|
||||
} break;
|
||||
case Vector3::AXIS_Z: {
|
||||
up_vec = Vector3(0, 0, 1);
|
||||
} break;
|
||||
}
|
||||
Basis src_b;
|
||||
src_b = src_b.looking_at(src_dir, up_vec);
|
||||
Basis prof_b;
|
||||
prof_b = src_b.looking_at(prof_dir, up_vec);
|
||||
if (prof_b.is_equal_approx(Basis())) {
|
||||
continue; // May not need to rotate.
|
||||
}
|
||||
Basis diff_b = prof_b * src_b.inverse();
|
||||
|
||||
// Apply rotation difference as global transform to skeleton.
|
||||
Basis src_pg;
|
||||
int src_parent = src_skeleton->get_bone_parent(src_idx);
|
||||
if (src_parent >= 0) {
|
||||
src_pg = src_skeleton->get_bone_global_rest(src_parent).basis;
|
||||
}
|
||||
Transform3D fixed_rest = Transform3D(src_pg.inverse() * diff_b * src_pg * src_skeleton->get_bone_rest(src_idx).basis, src_skeleton->get_bone_rest(src_idx).origin);
|
||||
src_skeleton->set_bone_rest(src_idx, fixed_rest);
|
||||
}
|
||||
}
|
||||
|
||||
// For skin modification in overwrite rest.
|
||||
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
|
||||
silhouette_diff_w[i] = old_skeleton_global_rest[i] * src_skeleton->get_bone_global_rest(i).inverse();
|
||||
}
|
||||
|
||||
is_rest_changed = true;
|
||||
}
|
||||
|
||||
// Overwrite axis.
|
||||
if (bool(p_options["retarget/rest_fixer/overwrite_axis"])) {
|
||||
LocalVector<Transform3D> old_skeleton_rest;
|
||||
LocalVector<Transform3D> old_skeleton_global_rest;
|
||||
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
|
||||
old_skeleton_rest.push_back(src_skeleton->get_bone_rest(i));
|
||||
old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
|
||||
}
|
||||
|
||||
Vector<Basis> diffs;
|
||||
diffs.resize(src_skeleton->get_bone_count());
|
||||
Basis *diffs_w = diffs.ptrw();
|
||||
|
||||
Vector<int> bones_to_process = src_skeleton->get_parentless_bones();
|
||||
while (bones_to_process.size() > 0) {
|
||||
int src_idx = bones_to_process[0];
|
||||
bones_to_process.erase(src_idx);
|
||||
Vector<int> src_children = src_skeleton->get_bone_children(src_idx);
|
||||
for (int i = 0; i < src_children.size(); i++) {
|
||||
bones_to_process.push_back(src_children[i]);
|
||||
}
|
||||
|
||||
Basis tgt_rot;
|
||||
StringName src_bone_name = is_renamed ? StringName(src_skeleton->get_bone_name(src_idx)) : bone_map->find_profile_bone_name(src_skeleton->get_bone_name(src_idx));
|
||||
if (src_bone_name != StringName()) {
|
||||
Basis src_pg;
|
||||
int src_parent_idx = src_skeleton->get_bone_parent(src_idx);
|
||||
if (src_parent_idx >= 0) {
|
||||
src_pg = src_skeleton->get_bone_global_rest(src_parent_idx).basis;
|
||||
}
|
||||
|
||||
int prof_idx = profile->find_bone(src_bone_name);
|
||||
if (prof_idx >= 0) {
|
||||
tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis; // Mapped bone uses reference pose.
|
||||
}
|
||||
/*
|
||||
// If there is rest-relative animation, this logic may be work fine, but currently not so...
|
||||
} else {
|
||||
// tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis; // Non-Mapped bone keeps global rest.
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (src_skeleton->get_bone_parent(src_idx) >= 0) {
|
||||
diffs_w[src_idx] = tgt_rot.inverse() * diffs[src_skeleton->get_bone_parent(src_idx)] * src_skeleton->get_bone_rest(src_idx).basis;
|
||||
} else {
|
||||
diffs_w[src_idx] = tgt_rot.inverse() * src_skeleton->get_bone_rest(src_idx).basis;
|
||||
}
|
||||
|
||||
Basis diff;
|
||||
if (src_skeleton->get_bone_parent(src_idx) >= 0) {
|
||||
diff = diffs[src_skeleton->get_bone_parent(src_idx)];
|
||||
}
|
||||
src_skeleton->set_bone_rest(src_idx, Transform3D(tgt_rot, diff.xform(src_skeleton->get_bone_rest(src_idx).origin)));
|
||||
}
|
||||
|
||||
// Fix skin.
|
||||
{
|
||||
TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D");
|
||||
while (nodes.size()) {
|
||||
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back());
|
||||
Ref<Skin> skin = mi->get_skin();
|
||||
if (skin.is_valid()) {
|
||||
Node *node = mi->get_node(mi->get_skeleton_path());
|
||||
if (node) {
|
||||
Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node);
|
||||
if (mesh_skeleton && node == src_skeleton) {
|
||||
int skin_len = skin->get_bind_count();
|
||||
for (int i = 0; i < skin_len; i++) {
|
||||
StringName bn = skin->get_bind_name(i);
|
||||
int bone_idx = src_skeleton->find_bone(bn);
|
||||
if (bone_idx >= 0) {
|
||||
Transform3D new_rest = silhouette_diff[i] * src_skeleton->get_bone_global_rest(bone_idx);
|
||||
skin->set_bind_pose(i, new_rest.inverse());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix animation.
|
||||
{
|
||||
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
|
||||
while (nodes.size()) {
|
||||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
|
||||
List<StringName> anims;
|
||||
ap->get_animation_list(&anims);
|
||||
for (const StringName &name : anims) {
|
||||
Ref<Animation> anim = ap->get_animation(name);
|
||||
int track_len = anim->get_track_count();
|
||||
for (int i = 0; i < track_len; i++) {
|
||||
if (anim->track_get_path(i).get_subname_count() != 1 || anim->track_get_type(i) != Animation::TYPE_ROTATION_3D) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (anim->track_is_compressed(i)) {
|
||||
continue; // TODO: Adopt to compressed track.
|
||||
}
|
||||
|
||||
String track_path = String(anim->track_get_path(i).get_concatenated_names());
|
||||
Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path));
|
||||
if (node) {
|
||||
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
|
||||
if (track_skeleton && track_skeleton == src_skeleton) {
|
||||
StringName bn = anim->track_get_path(i).get_subname(0);
|
||||
if (bn) {
|
||||
int bone_idx = src_skeleton->find_bone(bn);
|
||||
|
||||
Quaternion old_rest = old_skeleton_rest[bone_idx].basis.get_rotation_quaternion();
|
||||
Quaternion new_rest = src_skeleton->get_bone_rest(bone_idx).basis.get_rotation_quaternion();
|
||||
Quaternion old_pg;
|
||||
Quaternion new_pg;
|
||||
int parent_idx = src_skeleton->get_bone_parent(bone_idx);
|
||||
if (parent_idx >= 0) {
|
||||
old_pg = old_skeleton_global_rest[parent_idx].basis.get_rotation_quaternion();
|
||||
new_pg = src_skeleton->get_bone_global_rest(parent_idx).basis.get_rotation_quaternion();
|
||||
}
|
||||
|
||||
int key_len = anim->track_get_key_count(i);
|
||||
for (int j = 0; j < key_len; j++) {
|
||||
Quaternion qt = static_cast<Quaternion>(anim->track_get_key_value(i, j));
|
||||
anim->track_set_key_value(i, j, new_pg.inverse() * old_pg * qt * old_rest.inverse() * old_pg.inverse() * new_pg * new_rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_rest_changed = true;
|
||||
}
|
||||
|
||||
// Init skeleton pose to new rest.
|
||||
if (is_rest_changed) {
|
||||
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
|
||||
Transform3D fixed_rest = src_skeleton->get_bone_rest(i);
|
||||
src_skeleton->set_bone_pose_position(i, fixed_rest.origin);
|
||||
src_skeleton->set_bone_pose_rotation(i, fixed_rest.basis.get_rotation_quaternion());
|
||||
src_skeleton->set_bone_pose_scale(i, fixed_rest.basis.get_scale());
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(prof_skeleton);
|
||||
}
|
||||
}
|
||||
|
||||
PostImportPluginSkeletonRestFixer::PostImportPluginSkeletonRestFixer() {
|
||||
}
|
46
editor/import/post_import_plugin_skeleton_rest_fixer.h
Normal file
46
editor/import/post_import_plugin_skeleton_rest_fixer.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*************************************************************************/
|
||||
/* post_import_plugin_skeleton_rest_fixer.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef POST_IMPORT_PLUGIN_SKELETON_REST_FIXER_H
|
||||
#define POST_IMPORT_PLUGIN_SKELETON_REST_FIXER_H
|
||||
|
||||
#include "resource_importer_scene.h"
|
||||
|
||||
class PostImportPluginSkeletonRestFixer : public EditorScenePostImportPlugin {
|
||||
GDCLASS(PostImportPluginSkeletonRestFixer, EditorScenePostImportPlugin);
|
||||
|
||||
public:
|
||||
virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) override;
|
||||
virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) override;
|
||||
|
||||
PostImportPluginSkeletonRestFixer();
|
||||
};
|
||||
|
||||
#endif // POST_IMPORT_PLUGIN_SKELETON_REST_FIXER_H
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/import/post_import_plugin_skeleton_renamer.h"
|
||||
#include "editor/import/post_import_plugin_skeleton_rest_fixer.h"
|
||||
#include "editor/import/scene_import_settings.h"
|
||||
|
||||
void BoneMapperButton::fetch_textures() {
|
||||
@ -71,6 +72,10 @@ void BoneMapperButton::set_state(BoneMapState p_state) {
|
||||
}
|
||||
}
|
||||
|
||||
bool BoneMapperButton::is_require() const {
|
||||
return require;
|
||||
}
|
||||
|
||||
void BoneMapperButton::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
@ -79,8 +84,9 @@ void BoneMapperButton::_notification(int p_what) {
|
||||
}
|
||||
}
|
||||
|
||||
BoneMapperButton::BoneMapperButton(const StringName p_profile_bone_name, bool p_selected) {
|
||||
BoneMapperButton::BoneMapperButton(const StringName p_profile_bone_name, bool p_require, bool p_selected) {
|
||||
profile_bone_name = p_profile_bone_name;
|
||||
require = p_require;
|
||||
selected = p_selected;
|
||||
}
|
||||
|
||||
@ -89,7 +95,7 @@ BoneMapperButton::~BoneMapperButton() {
|
||||
|
||||
void BoneMapperItem::create_editor() {
|
||||
skeleton_bone_selector = memnew(EditorPropertyTextEnum);
|
||||
skeleton_bone_selector->setup(skeleton_bone_names);
|
||||
skeleton_bone_selector->setup(skeleton_bone_names, false, true);
|
||||
skeleton_bone_selector->set_label(profile_bone_name);
|
||||
skeleton_bone_selector->set_selectable(false);
|
||||
skeleton_bone_selector->set_object_and_property(bone_map.ptr(), "bone_map/" + String(profile_bone_name));
|
||||
@ -251,7 +257,7 @@ void BoneMapper::recreate_editor() {
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (profile->get_group(i) == profile->get_group_name(current_group_idx)) {
|
||||
BoneMapperButton *mb = memnew(BoneMapperButton(profile->get_bone_name(i), current_bone_idx == i));
|
||||
BoneMapperButton *mb = memnew(BoneMapperButton(profile->get_bone_name(i), profile->is_require(i), current_bone_idx == i));
|
||||
mb->connect("pressed", callable_mp(this, &BoneMapper::set_current_bone_idx), varray(i), CONNECT_DEFERRED);
|
||||
mb->set_h_grow_direction(GROW_DIRECTION_BOTH);
|
||||
mb->set_v_grow_direction(GROW_DIRECTION_BOTH);
|
||||
@ -284,8 +290,6 @@ void BoneMapper::recreate_items() {
|
||||
Ref<SkeletonProfile> profile = bone_map->get_profile();
|
||||
if (profile.is_valid()) {
|
||||
PackedStringArray skeleton_bone_names;
|
||||
skeleton_bone_names.push_back(String());
|
||||
|
||||
int len = skeleton->get_bone_count();
|
||||
for (int i = 0; i < len; i++) {
|
||||
skeleton_bone_names.push_back(skeleton->get_bone_name(i));
|
||||
@ -314,7 +318,11 @@ void BoneMapper::_update_state() {
|
||||
bone_mapper_buttons[i]->set_state(BoneMapperButton::BONE_MAP_STATE_ERROR);
|
||||
}
|
||||
} else {
|
||||
bone_mapper_buttons[i]->set_state(BoneMapperButton::BONE_MAP_STATE_UNSET);
|
||||
if (bone_mapper_buttons[i]->is_require()) {
|
||||
bone_mapper_buttons[i]->set_state(BoneMapperButton::BONE_MAP_STATE_ERROR);
|
||||
} else {
|
||||
bone_mapper_buttons[i]->set_state(BoneMapperButton::BONE_MAP_STATE_UNSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -396,9 +404,12 @@ void BoneMapEditor::_notification(int p_what) {
|
||||
create_editors();
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
if (!bone_mapper) {
|
||||
return;
|
||||
}
|
||||
remove_child(bone_mapper);
|
||||
bone_mapper->queue_delete();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -436,4 +447,8 @@ BoneMapEditorPlugin::BoneMapEditorPlugin() {
|
||||
Ref<PostImportPluginSkeletonRenamer> post_import_plugin_renamer;
|
||||
post_import_plugin_renamer.instantiate();
|
||||
add_scene_post_import_plugin(post_import_plugin_renamer);
|
||||
|
||||
Ref<PostImportPluginSkeletonRestFixer> post_import_plugin_rest_fixer;
|
||||
post_import_plugin_rest_fixer.instantiate();
|
||||
add_scene_post_import_plugin(post_import_plugin_rest_fixer);
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ public:
|
||||
private:
|
||||
StringName profile_bone_name;
|
||||
bool selected = false;
|
||||
bool require = false;
|
||||
|
||||
TextureRect *circle;
|
||||
|
||||
@ -65,7 +66,9 @@ public:
|
||||
StringName get_profile_bone_name() const;
|
||||
void set_state(BoneMapState p_state);
|
||||
|
||||
BoneMapperButton(const StringName p_profile_bone_name, bool p_selected);
|
||||
bool is_require() const;
|
||||
|
||||
BoneMapperButton(const StringName p_profile_bone_name, bool p_require, bool p_selected);
|
||||
~BoneMapperButton();
|
||||
};
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/physics_body_3d.h"
|
||||
#include "scene/resources/capsule_shape_3d.h"
|
||||
#include "scene/resources/skeleton_profile.h"
|
||||
#include "scene/resources/sphere_shape_3d.h"
|
||||
#include "scene/resources/surface_tool.h"
|
||||
|
||||
@ -250,6 +251,10 @@ void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) {
|
||||
create_physical_skeleton();
|
||||
break;
|
||||
}
|
||||
case SKELETON_OPTION_EXPORT_SKELETON_PROFILE: {
|
||||
export_skeleton_profile();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -451,6 +456,73 @@ PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_chi
|
||||
return physical_bone;
|
||||
}
|
||||
|
||||
void Skeleton3DEditor::export_skeleton_profile() {
|
||||
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
|
||||
file_dialog->set_title(TTR("Export Skeleton Profile As..."));
|
||||
|
||||
List<String> exts;
|
||||
ResourceLoader::get_recognized_extensions_for_type("SkeletonProfile", &exts);
|
||||
file_dialog->clear_filters();
|
||||
for (const String &K : exts) {
|
||||
file_dialog->add_filter("*." + K);
|
||||
}
|
||||
|
||||
file_dialog->popup_file_dialog();
|
||||
}
|
||||
|
||||
void Skeleton3DEditor::_file_selected(const String &p_file) {
|
||||
// Export SkeletonProfile.
|
||||
Ref<SkeletonProfile> sp(memnew(SkeletonProfile));
|
||||
|
||||
// Build SkeletonProfile.
|
||||
sp->set_group_size(1);
|
||||
|
||||
Vector<Vector2> handle_positions;
|
||||
Vector2 position_max;
|
||||
Vector2 position_min;
|
||||
|
||||
int len = skeleton->get_bone_count();
|
||||
sp->set_bone_size(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
sp->set_bone_name(i, skeleton->get_bone_name(i));
|
||||
int parent = skeleton->get_bone_parent(i);
|
||||
if (parent >= 0) {
|
||||
sp->set_bone_parent(i, skeleton->get_bone_name(parent));
|
||||
}
|
||||
sp->set_reference_pose(i, skeleton->get_bone_rest(i));
|
||||
|
||||
Transform3D grest = skeleton->get_bone_global_rest(i);
|
||||
handle_positions.append(Vector2(grest.origin.x, grest.origin.y));
|
||||
if (i == 0) {
|
||||
position_max = Vector2(grest.origin.x, grest.origin.y);
|
||||
position_min = Vector2(grest.origin.x, grest.origin.y);
|
||||
} else {
|
||||
position_max.x = MAX(grest.origin.x, position_max.x);
|
||||
position_max.y = MAX(grest.origin.y, position_max.y);
|
||||
position_min.x = MIN(grest.origin.x, position_min.x);
|
||||
position_min.y = MIN(grest.origin.y, position_min.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Layout handles provisionaly.
|
||||
Vector2 bound = Vector2(position_max.x - position_min.x, position_max.y - position_min.y);
|
||||
Vector2 center = Vector2((position_max.x + position_min.x) * 0.5, (position_max.y + position_min.y) * 0.5);
|
||||
float nrm = MAX(bound.x, bound.y);
|
||||
if (nrm > 0) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
handle_positions.write[i] = (handle_positions[i] - center) / nrm * 0.9;
|
||||
sp->set_handle_offset(i, Vector2(0.5 + handle_positions[i].x, 0.5 - handle_positions[i].y));
|
||||
}
|
||||
}
|
||||
|
||||
Error err = ResourceSaver::save(p_file, sp);
|
||||
|
||||
if (err != OK) {
|
||||
EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file: %s"), p_file));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Variant Skeleton3DEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
|
||||
TreeItem *selected = joint_tree->get_selected();
|
||||
|
||||
@ -631,6 +703,11 @@ void Skeleton3DEditor::create_editors() {
|
||||
Node3DEditor *ne = Node3DEditor::get_singleton();
|
||||
AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
|
||||
|
||||
// Create File dialog.
|
||||
file_dialog = memnew(EditorFileDialog);
|
||||
file_dialog->connect("file_selected", callable_mp(this, &Skeleton3DEditor::_file_selected));
|
||||
add_child(file_dialog);
|
||||
|
||||
// Create Top Menu Bar.
|
||||
separator = memnew(VSeparator);
|
||||
ne->add_control_to_menu_panel(separator);
|
||||
@ -649,6 +726,7 @@ void Skeleton3DEditor::create_editors() {
|
||||
p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/all_poses_to_rests", TTR("Apply all poses to rests")), SKELETON_OPTION_ALL_POSES_TO_RESTS);
|
||||
p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/selected_poses_to_rests", TTR("Apply selected poses to rests")), SKELETON_OPTION_SELECTED_POSES_TO_RESTS);
|
||||
p->add_item(TTR("Create physical skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);
|
||||
p->add_item(TTR("Export skeleton profile"), SKELETON_OPTION_EXPORT_SKELETON_PROFILE);
|
||||
|
||||
p->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_skeleton_option));
|
||||
set_bone_options_enabled(false);
|
||||
|
@ -101,6 +101,7 @@ class Skeleton3DEditor : public VBoxContainer {
|
||||
SKELETON_OPTION_ALL_POSES_TO_RESTS,
|
||||
SKELETON_OPTION_SELECTED_POSES_TO_RESTS,
|
||||
SKELETON_OPTION_CREATE_PHYSICAL_SKELETON,
|
||||
SKELETON_OPTION_EXPORT_SKELETON_PROFILE,
|
||||
};
|
||||
|
||||
struct BoneInfo {
|
||||
@ -155,6 +156,8 @@ class Skeleton3DEditor : public VBoxContainer {
|
||||
void create_physical_skeleton();
|
||||
PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
|
||||
|
||||
void export_skeleton_profile();
|
||||
|
||||
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
|
||||
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
|
||||
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
|
||||
|
@ -639,6 +639,7 @@ void Skeleton3D::remove_bone_child(int p_bone, int p_child) {
|
||||
}
|
||||
|
||||
Vector<int> Skeleton3D::get_parentless_bones() {
|
||||
_update_process_order();
|
||||
return parentless_bones;
|
||||
}
|
||||
|
||||
@ -765,8 +766,6 @@ void Skeleton3D::_make_dirty() {
|
||||
}
|
||||
|
||||
void Skeleton3D::localize_rests() {
|
||||
_update_process_order();
|
||||
|
||||
Vector<int> bones_to_process = get_parentless_bones();
|
||||
while (bones_to_process.size() > 0) {
|
||||
int current_bone_idx = bones_to_process[0];
|
||||
@ -958,7 +957,6 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() {
|
||||
|
||||
skin.instantiate();
|
||||
skin->set_bind_count(bones.size());
|
||||
_update_process_order(); // Just in case.
|
||||
|
||||
// Pose changed, rebuild cache of inverses.
|
||||
const Bone *bonesptr = bones.ptr();
|
||||
|
@ -50,6 +50,14 @@ bool BoneMap::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void BoneMap::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
HashMap<StringName, StringName>::ConstIterator E = bone_map.begin();
|
||||
while (E) {
|
||||
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bone_map/" + E->key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||
++E;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<SkeletonProfile> BoneMap::get_profile() const {
|
||||
return profile;
|
||||
}
|
||||
@ -153,6 +161,7 @@ void BoneMap::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("find_profile_bone_name", "skeleton_bone_name"), &BoneMap::find_profile_bone_name);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "profile", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonProfile"), "set_profile", "get_profile");
|
||||
ADD_ARRAY("bonemap", "bonemap");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("bone_map_updated"));
|
||||
ADD_SIGNAL(MethodInfo("profile_updated"));
|
||||
|
@ -46,6 +46,7 @@ protected:
|
||||
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||
bool _set(const StringName &p_path, const Variant &p_value);
|
||||
virtual void _validate_property(PropertyInfo &property) const override;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
|
@ -34,7 +34,7 @@ bool SkeletonProfile::_set(const StringName &p_path, const Variant &p_value) {
|
||||
ERR_FAIL_COND_V(is_read_only, false);
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("group/")) {
|
||||
if (path.begins_with("groups/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, groups.size(), false);
|
||||
@ -43,23 +43,35 @@ bool SkeletonProfile::_set(const StringName &p_path, const Variant &p_value) {
|
||||
set_group_name(which, p_value);
|
||||
} else if (what == "texture") {
|
||||
set_texture(which, p_value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path.begins_with("bone/")) {
|
||||
if (path.begins_with("bones/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, bones.size(), false);
|
||||
|
||||
if (what == "bone_name") {
|
||||
set_bone_name(which, p_value);
|
||||
} else if (what == "bone_parent") {
|
||||
set_bone_parent(which, p_value);
|
||||
} else if (what == "tail_direction") {
|
||||
set_tail_direction(which, static_cast<TailDirection>((int)p_value));
|
||||
} else if (what == "bone_tail") {
|
||||
set_bone_tail(which, p_value);
|
||||
} else if (what == "reference_pose") {
|
||||
set_reference_pose(which, p_value);
|
||||
} else if (what == "handle_offset") {
|
||||
set_handle_offset(which, p_value);
|
||||
} else if (what == "group") {
|
||||
set_group(which, p_value);
|
||||
} else if (what == "require") {
|
||||
set_require(which, p_value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -67,7 +79,7 @@ bool SkeletonProfile::_set(const StringName &p_path, const Variant &p_value) {
|
||||
bool SkeletonProfile::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("group/")) {
|
||||
if (path.begins_with("groups/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, groups.size(), false);
|
||||
@ -76,23 +88,35 @@ bool SkeletonProfile::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
r_ret = get_group_name(which);
|
||||
} else if (what == "texture") {
|
||||
r_ret = get_texture(which);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path.begins_with("bone/")) {
|
||||
if (path.begins_with("bones/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, bones.size(), false);
|
||||
|
||||
if (what == "bone_name") {
|
||||
r_ret = get_bone_name(which);
|
||||
} else if (what == "bone_parent") {
|
||||
r_ret = get_bone_parent(which);
|
||||
} else if (what == "tail_direction") {
|
||||
r_ret = get_tail_direction(which);
|
||||
} else if (what == "bone_tail") {
|
||||
r_ret = get_bone_tail(which);
|
||||
} else if (what == "reference_pose") {
|
||||
r_ret = get_reference_pose(which);
|
||||
} else if (what == "handle_offset") {
|
||||
r_ret = get_handle_offset(which);
|
||||
} else if (what == "group") {
|
||||
r_ret = get_group(which);
|
||||
} else if (what == "require") {
|
||||
r_ret = is_require(which);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -104,6 +128,13 @@ void SkeletonProfile::_validate_property(PropertyInfo &property) const {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PackedStringArray split = property.name.split("/");
|
||||
if (split.size() == 3 && split[0] == "bones") {
|
||||
if (split[2] == "bone_tail" && get_tail_direction(split[1].to_int()) != TAIL_DIRECTION_SPECIFIC_CHILD) {
|
||||
property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonProfile::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
@ -112,7 +143,7 @@ void SkeletonProfile::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
}
|
||||
String group_names = "";
|
||||
for (int i = 0; i < groups.size(); i++) {
|
||||
String path = "group/" + itos(i) + "/";
|
||||
String path = "groups/" + itos(i) + "/";
|
||||
p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "group_name"));
|
||||
p_list->push_back(PropertyInfo(Variant::OBJECT, path + "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"));
|
||||
if (i > 0) {
|
||||
@ -121,10 +152,19 @@ void SkeletonProfile::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
group_names = group_names + groups[i].group_name;
|
||||
}
|
||||
for (int i = 0; i < bones.size(); i++) {
|
||||
String path = "bone/" + itos(i) + "/";
|
||||
String path = "bones/" + itos(i) + "/";
|
||||
p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "bone_name"));
|
||||
p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "bone_parent"));
|
||||
p_list->push_back(PropertyInfo(Variant::INT, path + "tail_direction", PROPERTY_HINT_ENUM, "AverageChildren,SpecificChild,End"));
|
||||
p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "bone_tail"));
|
||||
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, path + "reference_pose"));
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR2, path + "handle_offset"));
|
||||
p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "group", PROPERTY_HINT_ENUM, group_names));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, path + "require"));
|
||||
}
|
||||
|
||||
for (PropertyInfo &E : *p_list) {
|
||||
_validate_property(E);
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,6 +224,18 @@ void SkeletonProfile::set_bone_size(int p_size) {
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int SkeletonProfile::find_bone(StringName p_bone_name) const {
|
||||
if (p_bone_name == StringName()) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < bones.size(); i++) {
|
||||
if (bones[i].bone_name == p_bone_name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
StringName SkeletonProfile::get_bone_name(int p_bone_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName());
|
||||
return bones[p_bone_idx].bone_name;
|
||||
@ -198,6 +250,63 @@ void SkeletonProfile::set_bone_name(int p_bone_idx, const StringName p_bone_name
|
||||
emit_signal("profile_updated");
|
||||
}
|
||||
|
||||
StringName SkeletonProfile::get_bone_parent(int p_bone_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName());
|
||||
return bones[p_bone_idx].bone_parent;
|
||||
}
|
||||
|
||||
void SkeletonProfile::set_bone_parent(int p_bone_idx, const StringName p_bone_parent) {
|
||||
if (is_read_only) {
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_INDEX(p_bone_idx, bones.size());
|
||||
bones.write[p_bone_idx].bone_parent = p_bone_parent;
|
||||
emit_signal("profile_updated");
|
||||
}
|
||||
|
||||
SkeletonProfile::TailDirection SkeletonProfile::get_tail_direction(int p_bone_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), TAIL_DIRECTION_AVERAGE_CHILDREN);
|
||||
return bones[p_bone_idx].tail_direction;
|
||||
}
|
||||
|
||||
void SkeletonProfile::set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction) {
|
||||
if (is_read_only) {
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_INDEX(p_bone_idx, bones.size());
|
||||
bones.write[p_bone_idx].tail_direction = p_tail_direction;
|
||||
emit_signal("profile_updated");
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
StringName SkeletonProfile::get_bone_tail(int p_bone_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName());
|
||||
return bones[p_bone_idx].bone_tail;
|
||||
}
|
||||
|
||||
void SkeletonProfile::set_bone_tail(int p_bone_idx, const StringName p_bone_tail) {
|
||||
if (is_read_only) {
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_INDEX(p_bone_idx, bones.size());
|
||||
bones.write[p_bone_idx].bone_tail = p_bone_tail;
|
||||
emit_signal("profile_updated");
|
||||
}
|
||||
|
||||
Transform3D SkeletonProfile::get_reference_pose(int p_bone_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), Transform3D());
|
||||
return bones[p_bone_idx].reference_pose;
|
||||
}
|
||||
|
||||
void SkeletonProfile::set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose) {
|
||||
if (is_read_only) {
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_INDEX(p_bone_idx, bones.size());
|
||||
bones.write[p_bone_idx].reference_pose = p_reference_pose;
|
||||
emit_signal("profile_updated");
|
||||
}
|
||||
|
||||
Vector2 SkeletonProfile::get_handle_offset(int p_bone_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), Vector2());
|
||||
return bones[p_bone_idx].handle_offset;
|
||||
@ -226,6 +335,20 @@ void SkeletonProfile::set_group(int p_bone_idx, const StringName p_group) {
|
||||
emit_signal("profile_updated");
|
||||
}
|
||||
|
||||
bool SkeletonProfile::is_require(int p_bone_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), false);
|
||||
return bones[p_bone_idx].require;
|
||||
}
|
||||
|
||||
void SkeletonProfile::set_require(int p_bone_idx, const bool p_require) {
|
||||
if (is_read_only) {
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_INDEX(p_bone_idx, bones.size());
|
||||
bones.write[p_bone_idx].require = p_require;
|
||||
emit_signal("profile_updated");
|
||||
}
|
||||
|
||||
bool SkeletonProfile::has_bone(StringName p_bone_name) {
|
||||
bool is_found = false;
|
||||
for (int i = 0; i < bones.size(); i++) {
|
||||
@ -250,19 +373,37 @@ void SkeletonProfile::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_bone_size", "size"), &SkeletonProfile::set_bone_size);
|
||||
ClassDB::bind_method(D_METHOD("get_bone_size"), &SkeletonProfile::get_bone_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("find_bone", "bone_name"), &SkeletonProfile::find_bone);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &SkeletonProfile::get_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_name", "bone_idx", "bone_name"), &SkeletonProfile::set_bone_name);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_bone_parent", "bone_idx"), &SkeletonProfile::get_bone_parent);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_parent", "bone_idx", "bone_parent"), &SkeletonProfile::set_bone_parent);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_tail_direction", "bone_idx"), &SkeletonProfile::get_tail_direction);
|
||||
ClassDB::bind_method(D_METHOD("set_tail_direction", "bone_idx", "tail_direction"), &SkeletonProfile::set_tail_direction);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_bone_tail", "bone_idx"), &SkeletonProfile::get_bone_tail);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_tail", "bone_idx", "bone_tail"), &SkeletonProfile::set_bone_tail);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_reference_pose", "bone_idx"), &SkeletonProfile::get_reference_pose);
|
||||
ClassDB::bind_method(D_METHOD("set_reference_pose", "bone_idx", "bone_name"), &SkeletonProfile::set_reference_pose);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_handle_offset", "bone_idx"), &SkeletonProfile::get_handle_offset);
|
||||
ClassDB::bind_method(D_METHOD("set_handle_offset", "bone_idx", "handle_offset"), &SkeletonProfile::set_handle_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_group", "bone_idx"), &SkeletonProfile::get_group);
|
||||
ClassDB::bind_method(D_METHOD("set_group", "bone_idx", "group"), &SkeletonProfile::set_group);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "group_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Groups,group/"), "set_group_size", "get_group_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Bones,bone/"), "set_bone_size", "get_bone_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "group_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Groups,groups/"), "set_group_size", "get_group_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Bones,bones/"), "set_bone_size", "get_bone_size");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("profile_updated"));
|
||||
|
||||
BIND_ENUM_CONSTANT(TAIL_DIRECTION_AVERAGE_CHILDREN);
|
||||
BIND_ENUM_CONSTANT(TAIL_DIRECTION_SPECIFIC_CHILD);
|
||||
BIND_ENUM_CONSTANT(TAIL_DIRECTION_END);
|
||||
}
|
||||
|
||||
SkeletonProfile::SkeletonProfile() {
|
||||
@ -284,226 +425,364 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
|
||||
bones.resize(56);
|
||||
|
||||
bones.write[0].bone_name = "Root";
|
||||
bones.write[0].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0);
|
||||
bones.write[0].handle_offset = Vector2(0.5, 0.91);
|
||||
bones.write[0].group = "Body";
|
||||
|
||||
bones.write[1].bone_name = "Hips";
|
||||
bones.write[1].bone_parent = "Root";
|
||||
bones.write[1].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD;
|
||||
bones.write[1].bone_tail = "Spine";
|
||||
bones.write[1].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0);
|
||||
bones.write[1].handle_offset = Vector2(0.5, 0.5);
|
||||
bones.write[1].group = "Body";
|
||||
bones.write[1].require = true;
|
||||
|
||||
bones.write[2].bone_name = "Spine";
|
||||
bones.write[2].bone_parent = "Hips";
|
||||
bones.write[2].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0);
|
||||
bones.write[2].handle_offset = Vector2(0.5, 0.43);
|
||||
bones.write[2].group = "Body";
|
||||
bones.write[2].require = true;
|
||||
|
||||
bones.write[3].bone_name = "Chest";
|
||||
bones.write[3].bone_parent = "Spine";
|
||||
bones.write[3].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0);
|
||||
bones.write[3].handle_offset = Vector2(0.5, 0.36);
|
||||
bones.write[3].group = "Body";
|
||||
|
||||
bones.write[4].bone_name = "UpperChest";
|
||||
bones.write[4].bone_parent = "Chest";
|
||||
bones.write[4].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0);
|
||||
bones.write[4].handle_offset = Vector2(0.5, 0.29);
|
||||
bones.write[4].group = "Body";
|
||||
|
||||
bones.write[5].bone_name = "Neck";
|
||||
bones.write[5].bone_parent = "UpperChest";
|
||||
bones.write[5].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD;
|
||||
bones.write[5].bone_tail = "Head";
|
||||
bones.write[5].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0);
|
||||
bones.write[5].handle_offset = Vector2(0.5, 0.23);
|
||||
bones.write[5].group = "Body";
|
||||
bones.write[5].require = true;
|
||||
|
||||
bones.write[6].bone_name = "Head";
|
||||
bones.write[6].bone_parent = "Neck";
|
||||
bones.write[6].tail_direction = TAIL_DIRECTION_END;
|
||||
bones.write[6].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0);
|
||||
bones.write[6].handle_offset = Vector2(0.5, 0.18);
|
||||
bones.write[6].group = "Body";
|
||||
bones.write[6].require = true;
|
||||
|
||||
bones.write[7].bone_name = "LeftEye";
|
||||
bones.write[7].bone_parent = "Head";
|
||||
bones.write[7].reference_pose = Transform3D(1, 0, 0, 0, 0, -1, 0, 1, 0, 0.05, 0.15, 0);
|
||||
bones.write[7].handle_offset = Vector2(0.6, 0.46);
|
||||
bones.write[7].group = "Face";
|
||||
|
||||
bones.write[8].bone_name = "RightEye";
|
||||
bones.write[8].bone_parent = "Head";
|
||||
bones.write[8].reference_pose = Transform3D(1, 0, 0, 0, 0, -1, 0, 1, 0, -0.05, 0.15, 0);
|
||||
bones.write[8].handle_offset = Vector2(0.37, 0.46);
|
||||
bones.write[8].group = "Face";
|
||||
|
||||
bones.write[9].bone_name = "Jaw";
|
||||
bones.write[9].bone_parent = "Head";
|
||||
bones.write[9].reference_pose = Transform3D(-1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0.05, 0.05);
|
||||
bones.write[9].handle_offset = Vector2(0.46, 0.75);
|
||||
bones.write[9].group = "Face";
|
||||
|
||||
bones.write[10].bone_name = "LeftShoulder";
|
||||
bones.write[10].bone_parent = "UpperChest";
|
||||
bones.write[10].reference_pose = Transform3D(0, 1, 0, 0, 0, 1, 1, 0, 0, 0.05, 0.1, 0);
|
||||
bones.write[10].handle_offset = Vector2(0.55, 0.235);
|
||||
bones.write[10].group = "Body";
|
||||
bones.write[10].require = true;
|
||||
|
||||
bones.write[11].bone_name = "LeftUpperArm";
|
||||
bones.write[11].bone_parent = "LeftShoulder";
|
||||
bones.write[11].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.05, 0);
|
||||
bones.write[11].handle_offset = Vector2(0.6, 0.24);
|
||||
bones.write[11].group = "Body";
|
||||
bones.write[11].require = true;
|
||||
|
||||
bones.write[12].bone_name = "LeftLowerArm";
|
||||
bones.write[12].bone_parent = "LeftUpperArm";
|
||||
bones.write[12].reference_pose = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0.25, 0);
|
||||
bones.write[12].handle_offset = Vector2(0.7, 0.24);
|
||||
bones.write[12].group = "Body";
|
||||
bones.write[12].require = true;
|
||||
|
||||
bones.write[13].bone_name = "LeftHand";
|
||||
bones.write[13].bone_parent = "LeftLowerArm";
|
||||
bones.write[13].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD;
|
||||
bones.write[13].bone_tail = "LeftMiddleProximal";
|
||||
bones.write[13].reference_pose = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0.25, 0);
|
||||
bones.write[13].handle_offset = Vector2(0.82, 0.235);
|
||||
bones.write[13].group = "Body";
|
||||
bones.write[13].require = true;
|
||||
|
||||
bones.write[14].bone_name = "LeftThumbProximal";
|
||||
bones.write[14].bone_name = "LeftThumbMetacarpal";
|
||||
bones.write[14].bone_parent = "LeftHand";
|
||||
bones.write[14].reference_pose = Transform3D(0, -0.577, 0.816, 0.707, 0.577, 0.408, -0.707, 0.577, 0.408, -0.025, 0, 0);
|
||||
bones.write[14].handle_offset = Vector2(0.4, 0.8);
|
||||
bones.write[14].group = "LeftHand";
|
||||
|
||||
bones.write[15].bone_name = "LeftThumbIntermediate";
|
||||
bones.write[15].bone_name = "LeftThumbProximal";
|
||||
bones.write[15].bone_parent = "LeftThumbMetacarpal";
|
||||
bones.write[15].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0);
|
||||
bones.write[15].handle_offset = Vector2(0.3, 0.69);
|
||||
bones.write[15].group = "LeftHand";
|
||||
|
||||
bones.write[16].bone_name = "LeftThumbDistal";
|
||||
bones.write[16].bone_parent = "LeftThumbProximal";
|
||||
bones.write[16].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0);
|
||||
bones.write[16].handle_offset = Vector2(0.23, 0.555);
|
||||
bones.write[16].group = "LeftHand";
|
||||
|
||||
bones.write[17].bone_name = "LeftIndexProximal";
|
||||
bones.write[17].bone_parent = "LeftHand";
|
||||
bones.write[17].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.025, 0.075, 0);
|
||||
bones.write[17].handle_offset = Vector2(0.413, 0.52);
|
||||
bones.write[17].group = "LeftHand";
|
||||
|
||||
bones.write[18].bone_name = "LeftIndexIntermediate";
|
||||
bones.write[18].bone_parent = "LeftIndexProximal";
|
||||
bones.write[18].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0);
|
||||
bones.write[18].handle_offset = Vector2(0.403, 0.36);
|
||||
bones.write[18].group = "LeftHand";
|
||||
|
||||
bones.write[19].bone_name = "LeftIndexDistal";
|
||||
bones.write[19].bone_parent = "LeftIndexIntermediate";
|
||||
bones.write[19].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0);
|
||||
bones.write[19].handle_offset = Vector2(0.403, 0.255);
|
||||
bones.write[19].group = "LeftHand";
|
||||
|
||||
bones.write[20].bone_name = "LeftMiddleProximal";
|
||||
bones.write[20].bone_parent = "LeftHand";
|
||||
bones.write[20].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0);
|
||||
bones.write[20].handle_offset = Vector2(0.5, 0.51);
|
||||
bones.write[20].group = "LeftHand";
|
||||
|
||||
bones.write[21].bone_name = "LeftMiddleIntermediate";
|
||||
bones.write[21].bone_parent = "LeftMiddleProximal";
|
||||
bones.write[21].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0);
|
||||
bones.write[21].handle_offset = Vector2(0.5, 0.345);
|
||||
bones.write[21].group = "LeftHand";
|
||||
|
||||
bones.write[22].bone_name = "LeftMiddleDistal";
|
||||
bones.write[22].bone_parent = "LeftMiddleIntermediate";
|
||||
bones.write[22].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0);
|
||||
bones.write[22].handle_offset = Vector2(0.5, 0.22);
|
||||
bones.write[22].group = "LeftHand";
|
||||
|
||||
bones.write[23].bone_name = "LeftRingProximal";
|
||||
bones.write[23].bone_parent = "LeftHand";
|
||||
bones.write[23].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.025, 0.075, 0);
|
||||
bones.write[23].handle_offset = Vector2(0.586, 0.52);
|
||||
bones.write[23].group = "LeftHand";
|
||||
|
||||
bones.write[24].bone_name = "LeftRingIntermediate";
|
||||
bones.write[24].bone_parent = "LeftRingProximal";
|
||||
bones.write[24].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0);
|
||||
bones.write[24].handle_offset = Vector2(0.59, 0.36);
|
||||
bones.write[24].group = "LeftHand";
|
||||
|
||||
bones.write[25].bone_name = "LeftRingDistal";
|
||||
bones.write[25].bone_parent = "LeftRingIntermediate";
|
||||
bones.write[25].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0);
|
||||
bones.write[25].handle_offset = Vector2(0.591, 0.25);
|
||||
bones.write[25].group = "LeftHand";
|
||||
|
||||
bones.write[26].bone_name = "LeftLittleProximal";
|
||||
bones.write[26].bone_parent = "LeftHand";
|
||||
bones.write[26].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.05, 0.05, 0);
|
||||
bones.write[26].handle_offset = Vector2(0.663, 0.543);
|
||||
bones.write[26].group = "LeftHand";
|
||||
|
||||
bones.write[27].bone_name = "LeftLittleIntermediate";
|
||||
bones.write[27].bone_parent = "LeftLittleProximal";
|
||||
bones.write[27].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0);
|
||||
bones.write[27].handle_offset = Vector2(0.672, 0.415);
|
||||
bones.write[27].group = "LeftHand";
|
||||
|
||||
bones.write[28].bone_name = "LeftLittleDistal";
|
||||
bones.write[28].bone_parent = "LeftLittleIntermediate";
|
||||
bones.write[28].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0);
|
||||
bones.write[28].handle_offset = Vector2(0.672, 0.32);
|
||||
bones.write[28].group = "LeftHand";
|
||||
|
||||
bones.write[29].bone_name = "RightShoulder";
|
||||
bones.write[29].bone_parent = "UpperChest";
|
||||
bones.write[29].reference_pose = Transform3D(0, -1, 0, 0, 0, 1, -1, 0, 0, -0.05, 0.1, 0);
|
||||
bones.write[29].handle_offset = Vector2(0.45, 0.235);
|
||||
bones.write[29].group = "Body";
|
||||
bones.write[29].require = true;
|
||||
|
||||
bones.write[30].bone_name = "RightUpperArm";
|
||||
bones.write[30].bone_parent = "RightShoulder";
|
||||
bones.write[30].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.05, 0);
|
||||
bones.write[30].handle_offset = Vector2(0.4, 0.24);
|
||||
bones.write[30].group = "Body";
|
||||
bones.write[30].require = true;
|
||||
|
||||
bones.write[31].bone_name = "RightLowerArm";
|
||||
bones.write[31].bone_parent = "RightUpperArm";
|
||||
bones.write[31].reference_pose = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0.25, 0);
|
||||
bones.write[31].handle_offset = Vector2(0.3, 0.24);
|
||||
bones.write[31].group = "Body";
|
||||
bones.write[31].require = true;
|
||||
|
||||
bones.write[32].bone_name = "RightHand";
|
||||
bones.write[32].bone_parent = "RightLowerArm";
|
||||
bones.write[32].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD;
|
||||
bones.write[32].bone_tail = "RightMiddleProximal";
|
||||
bones.write[32].reference_pose = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0.25, 0);
|
||||
bones.write[32].handle_offset = Vector2(0.18, 0.235);
|
||||
bones.write[32].group = "Body";
|
||||
bones.write[32].require = true;
|
||||
|
||||
bones.write[33].bone_name = "RightThumbProximal";
|
||||
bones.write[33].bone_name = "RightThumbMetacarpal";
|
||||
bones.write[33].bone_parent = "RightHand";
|
||||
bones.write[33].reference_pose = Transform3D(0, 0.577, -0.816, -0.707, 0.577, 0.408, 0.707, 0.577, 0.408, 0.025, 0, 0);
|
||||
bones.write[33].handle_offset = Vector2(0.6, 0.8);
|
||||
bones.write[33].group = "RightHand";
|
||||
|
||||
bones.write[34].bone_name = "RightThumbIntermediate";
|
||||
bones.write[34].bone_name = "RightThumbProximal";
|
||||
bones.write[34].bone_parent = "RightThumbMetacarpal";
|
||||
bones.write[34].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0);
|
||||
bones.write[34].handle_offset = Vector2(0.7, 0.69);
|
||||
bones.write[34].group = "RightHand";
|
||||
|
||||
bones.write[35].bone_name = "RightThumbDistal";
|
||||
bones.write[35].bone_parent = "RightThumbProximal";
|
||||
bones.write[35].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0);
|
||||
bones.write[35].handle_offset = Vector2(0.77, 0.555);
|
||||
bones.write[35].group = "RightHand";
|
||||
|
||||
bones.write[36].bone_name = "RightIndexProximal";
|
||||
bones.write[36].bone_parent = "RightHand";
|
||||
bones.write[36].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.025, 0.075, 0);
|
||||
bones.write[36].handle_offset = Vector2(0.587, 0.52);
|
||||
bones.write[36].group = "RightHand";
|
||||
|
||||
bones.write[37].bone_name = "RightIndexIntermediate";
|
||||
bones.write[37].bone_parent = "RightIndexProximal";
|
||||
bones.write[37].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0);
|
||||
bones.write[37].handle_offset = Vector2(0.597, 0.36);
|
||||
bones.write[37].group = "RightHand";
|
||||
|
||||
bones.write[38].bone_name = "RightIndexDistal";
|
||||
bones.write[38].bone_parent = "RightIndexIntermediate";
|
||||
bones.write[38].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0);
|
||||
bones.write[38].handle_offset = Vector2(0.597, 0.255);
|
||||
bones.write[38].group = "RightHand";
|
||||
|
||||
bones.write[39].bone_name = "RightMiddleProximal";
|
||||
bones.write[39].bone_parent = "RightHand";
|
||||
bones.write[39].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0);
|
||||
bones.write[39].handle_offset = Vector2(0.5, 0.51);
|
||||
bones.write[39].group = "RightHand";
|
||||
|
||||
bones.write[40].bone_name = "RightMiddleIntermediate";
|
||||
bones.write[40].bone_parent = "RightMiddleProximal";
|
||||
bones.write[40].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0);
|
||||
bones.write[40].handle_offset = Vector2(0.5, 0.345);
|
||||
bones.write[40].group = "RightHand";
|
||||
|
||||
bones.write[41].bone_name = "RightMiddleDistal";
|
||||
bones.write[41].bone_parent = "RightMiddleIntermediate";
|
||||
bones.write[41].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0);
|
||||
bones.write[41].handle_offset = Vector2(0.5, 0.22);
|
||||
bones.write[41].group = "RightHand";
|
||||
|
||||
bones.write[42].bone_name = "RightRingProximal";
|
||||
bones.write[42].bone_parent = "RightHand";
|
||||
bones.write[42].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.025, 0.075, 0);
|
||||
bones.write[42].handle_offset = Vector2(0.414, 0.52);
|
||||
bones.write[42].group = "RightHand";
|
||||
|
||||
bones.write[43].bone_name = "RightRingIntermediate";
|
||||
bones.write[43].bone_parent = "RightRingProximal";
|
||||
bones.write[43].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0);
|
||||
bones.write[43].handle_offset = Vector2(0.41, 0.36);
|
||||
bones.write[43].group = "RightHand";
|
||||
|
||||
bones.write[44].bone_name = "RightRingDistal";
|
||||
bones.write[44].bone_parent = "RightRingIntermediate";
|
||||
bones.write[44].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0);
|
||||
bones.write[44].handle_offset = Vector2(0.409, 0.25);
|
||||
bones.write[44].group = "RightHand";
|
||||
|
||||
bones.write[45].bone_name = "RightLittleProximal";
|
||||
bones.write[45].bone_parent = "RightHand";
|
||||
bones.write[45].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.05, 0.05, 0);
|
||||
bones.write[45].handle_offset = Vector2(0.337, 0.543);
|
||||
bones.write[45].group = "RightHand";
|
||||
|
||||
bones.write[46].bone_name = "RightLittleIntermediate";
|
||||
bones.write[46].bone_parent = "RightLittleProximal";
|
||||
bones.write[46].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0);
|
||||
bones.write[46].handle_offset = Vector2(0.328, 0.415);
|
||||
bones.write[46].group = "RightHand";
|
||||
|
||||
bones.write[47].bone_name = "RightLittleDistal";
|
||||
bones.write[47].bone_parent = "RightLittleIntermediate";
|
||||
bones.write[47].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0);
|
||||
bones.write[47].handle_offset = Vector2(0.328, 0.32);
|
||||
bones.write[47].group = "RightHand";
|
||||
|
||||
bones.write[48].bone_name = "LeftUpperLeg";
|
||||
bones.write[48].bone_parent = "Hips";
|
||||
bones.write[48].reference_pose = Transform3D(-1, 0, 0, 0, -1, 0, 0, 0, 1, 0.1, 0, 0);
|
||||
bones.write[48].handle_offset = Vector2(0.549, 0.49);
|
||||
bones.write[48].group = "Body";
|
||||
bones.write[48].require = true;
|
||||
|
||||
bones.write[49].bone_name = "LeftLowerLeg";
|
||||
bones.write[49].bone_parent = "LeftUpperLeg";
|
||||
bones.write[49].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.375, 0);
|
||||
bones.write[49].handle_offset = Vector2(0.548, 0.683);
|
||||
bones.write[49].group = "Body";
|
||||
bones.write[49].require = true;
|
||||
|
||||
bones.write[50].bone_name = "LeftFoot";
|
||||
bones.write[50].bone_parent = "LeftLowerLeg";
|
||||
bones.write[50].reference_pose = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0.375, 0);
|
||||
bones.write[50].handle_offset = Vector2(0.545, 0.9);
|
||||
bones.write[50].group = "Body";
|
||||
bones.write[50].require = true;
|
||||
|
||||
bones.write[51].bone_name = "LeftToes";
|
||||
bones.write[51].bone_parent = "LeftFoot";
|
||||
bones.write[51].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.15, 0);
|
||||
bones.write[51].handle_offset = Vector2(0.545, 0.95);
|
||||
bones.write[51].group = "Body";
|
||||
|
||||
bones.write[52].bone_name = "RightUpperLeg";
|
||||
bones.write[52].bone_parent = "Hips";
|
||||
bones.write[52].reference_pose = Transform3D(-1, 0, 0, 0, -1, 0, 0, 0, 1, -0.1, 0, 0);
|
||||
bones.write[52].handle_offset = Vector2(0.451, 0.49);
|
||||
bones.write[52].group = "Body";
|
||||
bones.write[52].require = true;
|
||||
|
||||
bones.write[53].bone_name = "RightLowerLeg";
|
||||
bones.write[53].bone_parent = "RightUpperLeg";
|
||||
bones.write[53].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.375, 0);
|
||||
bones.write[53].handle_offset = Vector2(0.452, 0.683);
|
||||
bones.write[53].group = "Body";
|
||||
bones.write[53].require = true;
|
||||
|
||||
bones.write[54].bone_name = "RightFoot";
|
||||
bones.write[54].bone_parent = "RightLowerLeg";
|
||||
bones.write[54].reference_pose = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0.375, 0);
|
||||
bones.write[54].handle_offset = Vector2(0.455, 0.9);
|
||||
bones.write[54].group = "Body";
|
||||
bones.write[54].require = true;
|
||||
|
||||
bones.write[55].bone_name = "RightToes";
|
||||
bones.write[55].bone_parent = "RightFoot";
|
||||
bones.write[55].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.15, 0);
|
||||
bones.write[55].handle_offset = Vector2(0.455, 0.95);
|
||||
bones.write[55].group = "Body";
|
||||
}
|
||||
|
@ -36,6 +36,13 @@
|
||||
class SkeletonProfile : public Resource {
|
||||
GDCLASS(SkeletonProfile, Resource);
|
||||
|
||||
public:
|
||||
enum TailDirection {
|
||||
TAIL_DIRECTION_AVERAGE_CHILDREN,
|
||||
TAIL_DIRECTION_SPECIFIC_CHILD,
|
||||
TAIL_DIRECTION_END
|
||||
};
|
||||
|
||||
protected:
|
||||
// Note: SkeletonProfileHumanoid which extends SkeletonProfile exists to unify standard bone names.
|
||||
// That is what is_read_only is for, so don't make it public.
|
||||
@ -48,8 +55,13 @@ protected:
|
||||
|
||||
struct SkeletonProfileBone {
|
||||
StringName bone_name;
|
||||
StringName bone_parent;
|
||||
TailDirection tail_direction = TAIL_DIRECTION_AVERAGE_CHILDREN;
|
||||
StringName bone_tail;
|
||||
Transform3D reference_pose;
|
||||
Vector2 handle_offset;
|
||||
StringName group;
|
||||
bool require = false;
|
||||
};
|
||||
|
||||
Vector<SkeletonProfileGroup> groups;
|
||||
@ -74,15 +86,32 @@ public:
|
||||
int get_bone_size();
|
||||
void set_bone_size(int p_size);
|
||||
|
||||
int find_bone(const StringName p_bone_name) const;
|
||||
|
||||
StringName get_bone_name(int p_bone_idx) const;
|
||||
void set_bone_name(int p_bone_idx, const StringName p_bone_name);
|
||||
|
||||
StringName get_bone_parent(int p_bone_idx) const;
|
||||
void set_bone_parent(int p_bone_idx, const StringName p_bone_parent);
|
||||
|
||||
TailDirection get_tail_direction(int p_bone_idx) const;
|
||||
void set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction);
|
||||
|
||||
StringName get_bone_tail(int p_bone_idx) const;
|
||||
void set_bone_tail(int p_bone_idx, const StringName p_bone_tail);
|
||||
|
||||
Transform3D get_reference_pose(int p_bone_idx) const;
|
||||
void set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose);
|
||||
|
||||
Vector2 get_handle_offset(int p_bone_idx) const;
|
||||
void set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset);
|
||||
|
||||
StringName get_group(int p_bone_idx) const;
|
||||
void set_group(int p_bone_idx, const StringName p_group);
|
||||
|
||||
bool is_require(int p_bone_idx) const;
|
||||
void set_require(int p_bone_idx, const bool p_require);
|
||||
|
||||
bool has_bone(StringName p_bone_name);
|
||||
|
||||
SkeletonProfile();
|
||||
@ -97,4 +126,6 @@ public:
|
||||
~SkeletonProfileHumanoid();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(SkeletonProfile::TailDirection);
|
||||
|
||||
#endif // SKELETON_PROFILE_H
|
||||
|
Loading…
Reference in New Issue
Block a user