From 8b1de7108e2cbbf5d87288bbb84c1f79280e4933 Mon Sep 17 00:00:00 2001 From: ElfWitch Date: Tue, 24 Oct 2023 11:46:00 -0700 Subject: [PATCH] Add inherit_scale to bones Add inherit_scale to bones Adds inherit_scale property to bones in Skeleton3D. Setting this to false will allow the bone to be scaled independently of its parent. --- doc/classes/Skeleton3D.xml | 15 ++++++ editor/plugins/skeleton_3d_editor_plugin.cpp | 15 ++++++ editor/plugins/skeleton_3d_editor_plugin.h | 1 + scene/3d/skeleton_3d.cpp | 56 ++++++++++++++++++-- scene/3d/skeleton_3d.h | 4 ++ 5 files changed, 86 insertions(+), 5 deletions(-) diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index b4c42ea399f7..86577032f07f 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -168,6 +168,13 @@ Returns whether the bone pose for the bone at [param bone_idx] is enabled. + + + + + Returns whether the bone should inherit the scale of its parent bone. + + @@ -244,6 +251,14 @@ [b]Note:[/b] The pose transform needs to be a global pose! To convert a world transform from a [Node3D] to a global bone pose, multiply the [method Transform3D.affine_inverse] of the node's [member Node3D.global_transform] by the desired world transform. + + + + + + Sets whether the parent's scale should be inherited by the bone. + + diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 20b91d8bfde0..7b3603714c06 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -91,6 +91,13 @@ void BoneTransformEditor::create_editors() { scale_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed)); section->get_vbox()->add_child(scale_property); + // Inherit Scale property. + inherit_scale_property = memnew(EditorPropertyCheck()); + inherit_scale_property->set_label("Inherit Scale"); + inherit_scale_property->set_selectable(false); + inherit_scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed)); + section->get_vbox()->add_child(inherit_scale_property); + // Transform/Matrix section. rest_section = memnew(EditorInspectorSection); rest_section->setup("trf_properties_transform", "Rest", this, Color(0.0f, 0.0f, 0.0f), true); @@ -151,6 +158,9 @@ void BoneTransformEditor::set_target(const String &p_prop) { scale_property->set_object_and_property(skeleton, p_prop + "scale"); scale_property->update_property(); + inherit_scale_property->set_object_and_property(skeleton, p_prop + "inherit_scale"); + inherit_scale_property->update_property(); + rest_matrix->set_object_and_property(skeleton, p_prop + "rest"); rest_matrix->update_property(); } @@ -207,6 +217,11 @@ void BoneTransformEditor::_update_properties() { scale_property->update_property(); scale_property->queue_redraw(); } + if (split[2] == "inherit_bone") { + scale_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY); + scale_property->update_property(); + scale_property->queue_redraw(); + } if (split[2] == "rest") { rest_matrix->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY); rest_matrix->update_property(); diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h index 3cc7c8549292..d6aa9c89eeda 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.h +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -59,6 +59,7 @@ class BoneTransformEditor : public VBoxContainer { EditorPropertyVector3 *position_property = nullptr; EditorPropertyQuaternion *rotation_property = nullptr; EditorPropertyVector3 *scale_property = nullptr; + EditorPropertyCheck *inherit_scale_property = nullptr; EditorInspectorSection *rest_section = nullptr; EditorPropertyTransform3D *rest_matrix = nullptr; diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 445c1003b5dd..6010a7911e20 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -95,6 +95,8 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { set_bone_pose_rotation(which, p_value); } else if (what == "scale") { set_bone_pose_scale(which, p_value); + } else if (what == "inherit_scale") { + set_bone_inherit_scale(which, p_value); } else { return false; } @@ -128,6 +130,8 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { r_ret = get_bone_pose_rotation(which); } else if (what == "scale") { r_ret = get_bone_pose_scale(which); + } else if (what == "inherit_scale") { + r_ret = is_bone_inherit_scale(which); } else { return false; } @@ -145,6 +149,7 @@ void Skeleton3D::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("position"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + PNAME("rotation"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("scale"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::BOOL, prep + PNAME("inherit_scale"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } for (PropertyInfo &E : *p_list) { @@ -171,6 +176,9 @@ void Skeleton3D::_validate_property(PropertyInfo &p_property) const { if (split[2] == "scale") { p_property.usage |= PROPERTY_USAGE_READ_ONLY; } + if (split[2] == "inherit_scale") { + p_property.usage |= PROPERTY_USAGE_READ_ONLY; + } } else if (!is_bone_enabled(split[1].to_int())) { if (split[2] == "position") { p_property.usage |= PROPERTY_USAGE_READ_ONLY; @@ -181,6 +189,9 @@ void Skeleton3D::_validate_property(PropertyInfo &p_property) const { if (split[2] == "scale") { p_property.usage |= PROPERTY_USAGE_READ_ONLY; } + if (split[2] == "inherit_scale") { + p_property.usage |= PROPERTY_USAGE_READ_ONLY; + } } } } @@ -587,6 +598,23 @@ void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) { } } +bool Skeleton3D::is_bone_inherit_scale(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, false); + return bones[p_bone].inherit_scale; +} + +void Skeleton3D::set_bone_inherit_scale(int p_bone, bool inherit_scale) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + bones.write[p_bone].inherit_scale = inherit_scale; + bones.write[p_bone].pose_cache_dirty = true; + if (is_inside_tree()) { + _make_dirty(); + } +} + Vector3 Skeleton3D::get_bone_pose_position(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3()); @@ -909,6 +937,21 @@ void Skeleton3D::force_update_all_bone_transforms() { rest_dirty = false; } +// Applies transform to child while preserving child scale, and corrects rotation if inherit is false. +// Otherwise, just does a regular transformation. +Transform3D transform_local_scale(Transform3D parent, Transform3D child, bool inherit) { + if (inherit == true) { + return parent * child; + } else { + // apply full transform + Transform3D output = parent * child; + + // replace basis with custom scaled + output.basis = Basis(parent.basis.get_rotation_quaternion() * child.basis.get_rotation_quaternion(), child.basis.get_scale()); + return output; + } +} + void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone_idx, bone_size); @@ -929,23 +972,23 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { Transform3D pose = b.pose_cache; if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * pose; - b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose; + b.pose_global = transform_local_scale(bonesptr[b.parent].pose_global, pose, b.inherit_scale); + b.pose_global_no_override = transform_local_scale(bonesptr[b.parent].pose_global_no_override, pose, b.inherit_scale); } else { b.pose_global = pose; b.pose_global_no_override = pose; } } else { if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * b.rest; - b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest; + b.pose_global = transform_local_scale(bonesptr[b.parent].pose_global, b.rest, b.inherit_scale); + b.pose_global_no_override = transform_local_scale(bonesptr[b.parent].pose_global_no_override, b.rest, b.inherit_scale); } else { b.pose_global = b.rest; b.pose_global_no_override = b.rest; } } if (rest_dirty) { - b.global_rest = b.parent >= 0 ? bonesptr[b.parent].global_rest * b.rest : b.rest; + b.global_rest = b.parent >= 0 ? transform_local_scale(bonesptr[b.parent].global_rest, b.rest, b.inherit_scale) : b.rest; } if (b.global_pose_override_amount >= CMP_EPSILON) { @@ -1000,6 +1043,9 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation); ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale); + ClassDB::bind_method(D_METHOD("is_bone_inherit_scale", "bone_idx"), &Skeleton3D::is_bone_inherit_scale); + ClassDB::bind_method(D_METHOD("set_bone_inherit_scale", "bone_idx", "inherit_scale"), &Skeleton3D::set_bone_inherit_scale, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_bone_pose_position", "bone_idx"), &Skeleton3D::get_bone_pose_position); ClassDB::bind_method(D_METHOD("get_bone_pose_rotation", "bone_idx"), &Skeleton3D::get_bone_pose_rotation); ClassDB::bind_method(D_METHOD("get_bone_pose_scale", "bone_idx"), &Skeleton3D::get_bone_pose_scale); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 7d4df1d1f2b8..1c29626f17ab 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -91,6 +91,7 @@ class Skeleton3D : public Node3D { Quaternion pose_rotation; Vector3 pose_scale = Vector3(1, 1, 1); + bool inherit_scale = true; Transform3D pose_global; Transform3D pose_global_no_override; @@ -192,6 +193,9 @@ class Skeleton3D : public Node3D { void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation); void set_bone_pose_scale(int p_bone, const Vector3 &p_scale); + bool is_bone_inherit_scale(int p_bone) const; + void set_bone_inherit_scale(int p_bone, bool inherit_scale); + Transform3D get_bone_pose(int p_bone) const; Vector3 get_bone_pose_position(int p_bone) const;