Просмотр исходного кода

Merge pull request #39126 from TwistedTwigleg/GSOC_2020_Working_Branch

Skeleton and Skeleton inspector low-level changes
Rémi Verschelde 5 лет назад
Родитель
Сommit
8db8577f10

+ 39 - 0
doc/classes/Skeleton3D.xml

@@ -31,6 +31,16 @@
 				[i]Deprecated soon.[/i]
 			</description>
 		</method>
+		<method name="bone_transform_to_world_transform">
+			<return type="Transform">
+			</return>
+			<argument index="0" name="bone_transform" type="Transform">
+			</argument>
+			<description>
+				Takes the given bone pose/transform and converts it to a world transform, relative to the [Skeleton3D] node.
+				This is useful for using the bone transform in calculations with transforms from [Node3D]-based nodes.
+			</description>
+		</method>
 		<method name="clear_bones">
 			<return type="void">
 			</return>
@@ -42,6 +52,7 @@
 			<return type="void">
 			</return>
 			<description>
+				Removes the global pose override on all bones in the skeleton.
 			</description>
 		</method>
 		<method name="find_bone" qualifiers="const">
@@ -136,12 +147,14 @@
 			<argument index="0" name="bone_idx" type="int">
 			</argument>
 			<description>
+				Returns whether the bone rest for the bone at [code]bone_idx[/code] is disabled.
 			</description>
 		</method>
 		<method name="localize_rests">
 			<return type="void">
 			</return>
 			<description>
+				Returns all bones in the skeleton to their rest poses.
 			</description>
 		</method>
 		<method name="physical_bones_add_collision_exception">
@@ -150,6 +163,8 @@
 			<argument index="0" name="exception" type="RID">
 			</argument>
 			<description>
+				Adds a collision exception to the physical bone.
+				Works just like the [RigidBody3D] node.
 			</description>
 		</method>
 		<method name="physical_bones_remove_collision_exception">
@@ -158,6 +173,8 @@
 			<argument index="0" name="exception" type="RID">
 			</argument>
 			<description>
+				Removes a collision exception to the physical bone.
+				Works just like the [RigidBody3D] node.
 			</description>
 		</method>
 		<method name="physical_bones_start_simulation">
@@ -166,12 +183,15 @@
 			<argument index="0" name="bones" type="StringName[]" default="[  ]">
 			</argument>
 			<description>
+				Tells the [PhysicalBone3D] nodes in the Skeleton to start simulating and reacting to the physics world.
+				Optionally, a list of bone names can be passed-in, allowing only the passed-in bones to be simulated.
 			</description>
 		</method>
 		<method name="physical_bones_stop_simulation">
 			<return type="void">
 			</return>
 			<description>
+				Tells the [PhysicalBone3D] nodes in the Skeleton to stop simulating.
 			</description>
 		</method>
 		<method name="register_skin">
@@ -180,6 +200,7 @@
 			<argument index="0" name="skin" type="Skin">
 			</argument>
 			<description>
+				Binds the given Skin to the Skeleton.
 			</description>
 		</method>
 		<method name="set_bone_custom_pose">
@@ -190,6 +211,8 @@
 			<argument index="1" name="custom_pose" type="Transform">
 			</argument>
 			<description>
+				Sets the custom pose transform, [code]custom_pose[/code], for the bone at [code]bone_idx[/code]. This pose is an addition to the bone rest pose.
+				[b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.
 			</description>
 		</method>
 		<method name="set_bone_disable_rest">
@@ -200,6 +223,7 @@
 			<argument index="1" name="disable" type="bool">
 			</argument>
 			<description>
+				Disables the rest pose for the bone at [code]bone_idx[/code] if [code]true[/code], enables the bone rest if [code]false[/code].
 			</description>
 		</method>
 		<method name="set_bone_global_pose_override">
@@ -214,6 +238,9 @@
 			<argument index="3" name="persistent" type="bool" default="false">
 			</argument>
 			<description>
+				Sets the global pose transform, [code]pose[/code], for the bone at [code]bone_idx[/code].
+				[code]amount[/code] is the interpolation strengh that will be used when applying the pose, and [code]persistent[/code] determines if the applied pose will remain.
+				[b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.
 			</description>
 		</method>
 		<method name="set_bone_parent">
@@ -237,6 +264,7 @@
 			</argument>
 			<description>
 				Returns the pose transform for bone [code]bone_idx[/code].
+				[b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.
 			</description>
 		</method>
 		<method name="set_bone_rest">
@@ -267,6 +295,17 @@
 			<argument index="0" name="bone_idx" type="int">
 			</argument>
 			<description>
+				Unparents the bone at [code]bone_idx[/code] and sets its rest position to that of it's parent prior to being reset.
+			</description>
+		</method>
+		<method name="world_transform_to_bone_transform">
+			<return type="Transform">
+			</return>
+			<argument index="0" name="world_transform" type="Transform">
+			</argument>
+			<description>
+				Takes the given world transform, relative to the [Skeleton3D], and converts it to a bone pose/transform.
+				This is useful for using setting bone poses using transforms from [Node3D]-based nodes.
 			</description>
 		</method>
 	</methods>

+ 33 - 20
editor/editor_properties.cpp

@@ -1283,14 +1283,25 @@ void EditorPropertyVector3::_value_changed(double val, const String &p_name) {
 }
 
 void EditorPropertyVector3::update_property() {
-	Vector3 val = get_edited_object()->get(get_edited_property());
+	update_using_vector(get_edited_object()->get(get_edited_property()));
+}
+
+void EditorPropertyVector3::update_using_vector(Vector3 p_vector) {
 	setting = true;
-	spin[0]->set_value(val.x);
-	spin[1]->set_value(val.y);
-	spin[2]->set_value(val.z);
+	spin[0]->set_value(p_vector.x);
+	spin[1]->set_value(p_vector.y);
+	spin[2]->set_value(p_vector.z);
 	setting = false;
 }
 
+Vector3 EditorPropertyVector3::get_vector() {
+	Vector3 v3;
+	v3.x = spin[0]->get_value();
+	v3.y = spin[1]->get_value();
+	v3.z = spin[2]->get_value();
+	return v3;
+}
+
 void EditorPropertyVector3::_notification(int p_what) {
 	if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
 		Color base = get_theme_color("accent_color", "Editor");
@@ -1434,7 +1445,7 @@ EditorPropertyVector2i::EditorPropertyVector2i(bool p_force_wide) {
 	setting = false;
 }
 
-///////////////////// RECT2 /////////////////////////
+///////////////////// RECT2i /////////////////////////
 
 void EditorPropertyRect2i::_value_changed(double val, const String &p_name) {
 	if (setting) {
@@ -1520,7 +1531,7 @@ EditorPropertyRect2i::EditorPropertyRect2i(bool p_force_wide) {
 	setting = false;
 }
 
-///////////////////// VECTOR3 /////////////////////////
+///////////////////// VECTOR3i /////////////////////////
 
 void EditorPropertyVector3i::_value_changed(double val, const String &p_name) {
 	if (setting) {
@@ -2029,21 +2040,23 @@ void EditorPropertyTransform::_value_changed(double val, const String &p_name) {
 }
 
 void EditorPropertyTransform::update_property() {
-	Transform val = get_edited_object()->get(get_edited_property());
-	setting = true;
-	spin[0]->set_value(val.basis[0][0]);
-	spin[1]->set_value(val.basis[1][0]);
-	spin[2]->set_value(val.basis[2][0]);
-	spin[3]->set_value(val.basis[0][1]);
-	spin[4]->set_value(val.basis[1][1]);
-	spin[5]->set_value(val.basis[2][1]);
-	spin[6]->set_value(val.basis[0][2]);
-	spin[7]->set_value(val.basis[1][2]);
-	spin[8]->set_value(val.basis[2][2]);
-	spin[9]->set_value(val.origin[0]);
-	spin[10]->set_value(val.origin[1]);
-	spin[11]->set_value(val.origin[2]);
+	update_using_transform(get_edited_object()->get(get_edited_property()));
+}
 
+void EditorPropertyTransform::update_using_transform(Transform p_transform) {
+	setting = true;
+	spin[0]->set_value(p_transform.basis[0][0]);
+	spin[1]->set_value(p_transform.basis[1][0]);
+	spin[2]->set_value(p_transform.basis[2][0]);
+	spin[3]->set_value(p_transform.basis[0][1]);
+	spin[4]->set_value(p_transform.basis[1][1]);
+	spin[5]->set_value(p_transform.basis[2][1]);
+	spin[6]->set_value(p_transform.basis[0][2]);
+	spin[7]->set_value(p_transform.basis[1][2]);
+	spin[8]->set_value(p_transform.basis[2][2]);
+	spin[9]->set_value(p_transform.origin[0]);
+	spin[10]->set_value(p_transform.origin[1]);
+	spin[11]->set_value(p_transform.origin[2]);
 	setting = false;
 }
 

+ 3 - 0
editor/editor_properties.h

@@ -392,6 +392,8 @@ protected:
 
 public:
 	virtual void update_property();
+	virtual void update_using_vector(Vector3 p_vector);
+	virtual Vector3 get_vector();
 	void setup(double p_min, double p_max, double p_step, bool p_no_slider);
 	EditorPropertyVector3(bool p_force_wide = false);
 };
@@ -536,6 +538,7 @@ protected:
 
 public:
 	virtual void update_property();
+	virtual void update_using_transform(Transform p_transform);
 	void setup(double p_min, double p_max, double p_step, bool p_no_slider);
 	EditorPropertyTransform();
 };

+ 95 - 192
editor/plugins/skeleton_3d_editor_plugin.cpp

@@ -62,125 +62,56 @@ void BoneTransformEditor::create_editors() {
 	enabled_checkbox->set_visible(toggle_enabled);
 	section->get_vbox()->add_child(enabled_checkbox);
 
-	Label *l1 = memnew(Label(TTR("Translation")));
-	section->get_vbox()->add_child(l1);
-
-	translation_grid = memnew(GridContainer());
-	translation_grid->set_columns(TRANSLATION_COMPONENTS);
-	section->get_vbox()->add_child(translation_grid);
-
-	Label *l2 = memnew(Label(TTR("Rotation Degrees")));
-	section->get_vbox()->add_child(l2);
-
-	rotation_grid = memnew(GridContainer());
-	rotation_grid->set_columns(ROTATION_DEGREES_COMPONENTS);
-	section->get_vbox()->add_child(rotation_grid);
-
-	Label *l3 = memnew(Label(TTR("Scale")));
-	section->get_vbox()->add_child(l3);
-
-	scale_grid = memnew(GridContainer());
-	scale_grid->set_columns(SCALE_COMPONENTS);
-	section->get_vbox()->add_child(scale_grid);
-
-	Label *l4 = memnew(Label(TTR("Transform")));
-	section->get_vbox()->add_child(l4);
-
-	transform_grid = memnew(GridContainer());
-	transform_grid->set_columns(TRANSFORM_CONTROL_COMPONENTS);
-	section->get_vbox()->add_child(transform_grid);
-
-	static const char *desc[TRANSFORM_COMPONENTS] = { "x", "y", "z", "x", "y", "z", "x", "y", "z", "x", "y", "z" };
-
-	for (int i = 0; i < TRANSFORM_CONTROL_COMPONENTS; ++i) {
-		translation_slider[i] = memnew(EditorSpinSlider());
-		translation_slider[i]->set_label(desc[i]);
-		setup_spinner(translation_slider[i], false);
-		translation_grid->add_child(translation_slider[i]);
-
-		rotation_slider[i] = memnew(EditorSpinSlider());
-		rotation_slider[i]->set_label(desc[i]);
-		setup_spinner(rotation_slider[i], false);
-		rotation_grid->add_child(rotation_slider[i]);
-
-		scale_slider[i] = memnew(EditorSpinSlider());
-		scale_slider[i]->set_label(desc[i]);
-		setup_spinner(scale_slider[i], false);
-		scale_grid->add_child(scale_slider[i]);
-	}
-
-	for (int i = 0; i < TRANSFORM_COMPONENTS; ++i) {
-		transform_slider[i] = memnew(EditorSpinSlider());
-		transform_slider[i]->set_label(desc[i]);
-		setup_spinner(transform_slider[i], true);
-		transform_grid->add_child(transform_slider[i]);
-	}
-}
-
-void BoneTransformEditor::setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner) {
-	spinner->set_flat(true);
-	spinner->set_min(-10000);
-	spinner->set_max(10000);
-	spinner->set_step(0.001f);
-	spinner->set_hide_slider(true);
-	spinner->set_allow_greater(true);
-	spinner->set_allow_lesser(true);
-	spinner->set_h_size_flags(SIZE_EXPAND_FILL);
-
-	spinner->connect_compat("value_changed", this, "_value_changed", varray(is_transform_spinner));
+	// Translation property
+	translation_property = memnew(EditorPropertyVector3());
+	translation_property->setup(-10000, 10000, 0.001f, true);
+	translation_property->set_label("Translation");
+	translation_property->set_use_folding(true);
+	translation_property->set_read_only(false);
+	translation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
+	section->get_vbox()->add_child(translation_property);
+
+	// Rotation property
+	rotation_property = memnew(EditorPropertyVector3());
+	rotation_property->setup(-10000, 10000, 0.001f, true);
+	rotation_property->set_label("Rotation Degrees");
+	rotation_property->set_use_folding(true);
+	rotation_property->set_read_only(false);
+	rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
+	section->get_vbox()->add_child(rotation_property);
+
+	// Scale property
+	scale_property = memnew(EditorPropertyVector3());
+	scale_property->setup(-10000, 10000, 0.001f, true);
+	scale_property->set_label("Scale");
+	scale_property->set_use_folding(true);
+	scale_property->set_read_only(false);
+	scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
+	section->get_vbox()->add_child(scale_property);
+
+	// Transform/Matrix section
+	transform_section = memnew(EditorInspectorSection);
+	transform_section->setup("trf_properties_transform", "Matrix", this, section_color, true);
+	section->get_vbox()->add_child(transform_section);
+
+	// Transform/Matrix property
+	transform_property = memnew(EditorPropertyTransform());
+	transform_property->setup(-10000, 10000, 0.001f, true);
+	transform_property->set_label("Transform");
+	transform_property->set_use_folding(true);
+	transform_property->set_read_only(false);
+	transform_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_transform));
+	transform_section->get_vbox()->add_child(transform_property);
 }
 
 void BoneTransformEditor::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_ENTER_TREE: {
 			create_editors();
-			key_button->connect_compat("pressed", this, "_key_button_pressed");
-			enabled_checkbox->connect_compat("toggled", this, "_checkbox_toggled");
+			key_button->connect("pressed", callable_mp(this, &BoneTransformEditor::_key_button_pressed));
+			enabled_checkbox->connect("toggled", callable_mp(this, &BoneTransformEditor::_checkbox_toggled));
 			[[fallthrough]];
 		}
-		case NOTIFICATION_THEME_CHANGED: {
-			const Color base = get_theme_color("accent_color", "Editor");
-			const Color bg_color = get_theme_color("property_color", "Editor");
-			const Color bg_lbl_color(bg_color.r, bg_color.g, bg_color.b, 0.5);
-
-			for (int i = 0; i < TRANSLATION_COMPONENTS; i++) {
-				Color c = base;
-				c.set_hsv(float(i % TRANSLATION_COMPONENTS) / TRANSLATION_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
-				if (!translation_slider[i]) {
-					continue;
-				}
-				translation_slider[i]->set_custom_label_color(true, c);
-			}
-
-			for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) {
-				Color c = base;
-				c.set_hsv(float(i % ROTATION_DEGREES_COMPONENTS) / ROTATION_DEGREES_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
-				if (!rotation_slider[i]) {
-					continue;
-				}
-				rotation_slider[i]->set_custom_label_color(true, c);
-			}
-
-			for (int i = 0; i < SCALE_COMPONENTS; i++) {
-				Color c = base;
-				c.set_hsv(float(i % SCALE_COMPONENTS) / SCALE_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
-				if (!scale_slider[i]) {
-					continue;
-				}
-				scale_slider[i]->set_custom_label_color(true, c);
-			}
-
-			for (int i = 0; i < TRANSFORM_COMPONENTS; i++) {
-				Color c = base;
-				c.set_hsv(float(i % TRANSFORM_COMPONENTS) / TRANSFORM_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
-				if (!transform_slider[i]) {
-					continue;
-				}
-				transform_slider[i]->set_custom_label_color(true, c);
-			}
-
-			break;
-		}
 		case NOTIFICATION_SORT_CHILDREN: {
 			const Ref<Font> font = get_theme_font("font", "Tree");
 
@@ -189,8 +120,8 @@ void BoneTransformEditor::_notification(int p_what) {
 			buffer.y += font->get_height();
 			buffer.y += get_theme_constant("vseparation", "Tree");
 
-			const float vector_height = translation_grid->get_size().y;
-			const float transform_height = transform_grid->get_size().y;
+			const float vector_height = translation_property->get_size().y;
+			const float transform_height = transform_property->get_size().y;
 			const float button_height = key_button->get_size().y;
 
 			const float width = get_size().x - get_theme_constant("inspector_margin", "Editor");
@@ -202,10 +133,10 @@ void BoneTransformEditor::_notification(int p_what) {
 			}
 
 			if (section->get_vbox()->is_visible()) {
-				input_rects.push_back(Rect2(translation_grid->get_position() + buffer, Size2(width, vector_height)));
-				input_rects.push_back(Rect2(rotation_grid->get_position() + buffer, Size2(width, vector_height)));
-				input_rects.push_back(Rect2(scale_grid->get_position() + buffer, Size2(width, vector_height)));
-				input_rects.push_back(Rect2(transform_grid->get_position() + buffer, Size2(width, transform_height)));
+				input_rects.push_back(Rect2(translation_property->get_position() + buffer, Size2(width, vector_height)));
+				input_rects.push_back(Rect2(rotation_property->get_position() + buffer, Size2(width, vector_height)));
+				input_rects.push_back(Rect2(scale_property->get_position() + buffer, Size2(width, vector_height)));
+				input_rects.push_back(Rect2(transform_property->get_position() + buffer, Size2(width, transform_height)));
 			} else {
 				const int32_t start = input_rects.size();
 				const int32_t empty_input_rect_elements = 4;
@@ -234,49 +165,53 @@ void BoneTransformEditor::_notification(int p_what) {
 	}
 }
 
-void BoneTransformEditor::_value_changed(const double p_value, const bool p_from_transform) {
+void BoneTransformEditor::_value_changed(const double p_value) {
 	if (updating)
 		return;
 
-	if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") {
-		const Transform tform = compute_transform(p_from_transform);
+	Transform tform = compute_transform_from_vector3s();
+	_change_transform(tform);
+}
 
+void BoneTransformEditor::_value_changed_vector3(const String p_property_name, const Vector3 p_vector, const StringName p_edited_property_name, const bool p_boolean) {
+	if (updating)
+		return;
+	Transform tform = compute_transform_from_vector3s();
+	_change_transform(tform);
+}
+
+Transform BoneTransformEditor::compute_transform_from_vector3s() const {
+	// Convert rotation from degrees to radians.
+	Vector3 prop_rotation = rotation_property->get_vector();
+	prop_rotation.x = Math::deg2rad(prop_rotation.x);
+	prop_rotation.y = Math::deg2rad(prop_rotation.y);
+	prop_rotation.z = Math::deg2rad(prop_rotation.z);
+
+	return Transform(
+			Basis(prop_rotation, scale_property->get_vector()),
+			translation_property->get_vector());
+}
+
+void BoneTransformEditor::_value_changed_transform(const String p_property_name, const Transform p_transform, const StringName p_edited_property_name, const bool p_boolean) {
+	if (updating)
+		return;
+	_change_transform(p_transform);
+}
+
+void BoneTransformEditor::_change_transform(Transform p_new_transform) {
+	if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") {
 		undo_redo->create_action(TTR("Set Custom Bone Pose Transform"), UndoRedo::MERGE_ENDS);
 		undo_redo->add_undo_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), skeleton->get_bone_custom_pose(property.get_slicec('/', 1).to_int()));
-		undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), tform);
+		undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), p_new_transform);
 		undo_redo->commit_action();
 	} else if (property.get_slicec('/', 0) == "bones") {
-		const Transform tform = compute_transform(p_from_transform);
-
 		undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
 		undo_redo->add_undo_property(skeleton, property, skeleton->get(property));
-		undo_redo->add_do_property(skeleton, property, tform);
+		undo_redo->add_do_property(skeleton, property, p_new_transform);
 		undo_redo->commit_action();
 	}
 }
 
-Transform BoneTransformEditor::compute_transform(const bool p_from_transform) const {
-	// Last modified was a raw transform column...
-	if (p_from_transform) {
-		Transform tform;
-
-		for (int i = 0; i < BASIS_COMPONENTS; ++i) {
-			tform.basis[i / BASIS_SPLIT_COMPONENTS][i % BASIS_SPLIT_COMPONENTS] = transform_slider[i]->get_value();
-		}
-
-		for (int i = 0; i < TRANSLATION_COMPONENTS; ++i) {
-			tform.origin[i] = transform_slider[i + BASIS_COMPONENTS]->get_value();
-		}
-
-		return tform;
-	}
-
-	return Transform(
-			Basis(Vector3(Math::deg2rad(rotation_slider[0]->get_value()), Math::deg2rad(rotation_slider[1]->get_value()), Math::deg2rad(rotation_slider[2]->get_value())),
-					Vector3(scale_slider[0]->get_value(), scale_slider[1]->get_value(), scale_slider[2]->get_value())),
-			Vector3(translation_slider[0]->get_value(), translation_slider[1]->get_value(), translation_slider[2]->get_value()));
-}
-
 void BoneTransformEditor::update_enabled_checkbox() {
 	if (enabled_checkbox) {
 		const String path = "bones/" + property.get_slicec('/', 1) + "/enabled";
@@ -285,12 +220,6 @@ void BoneTransformEditor::update_enabled_checkbox() {
 	}
 }
 
-void BoneTransformEditor::_bind_methods() {
-	ClassDB::bind_method(D_METHOD("_value_changed", "value"), &BoneTransformEditor::_value_changed);
-	ClassDB::bind_method(D_METHOD("_key_button_pressed"), &BoneTransformEditor::_key_button_pressed);
-	ClassDB::bind_method(D_METHOD("_checkbox_toggled", "toggled"), &BoneTransformEditor::_checkbox_toggled);
-}
-
 void BoneTransformEditor::_update_properties() {
 	if (updating)
 		return;
@@ -318,47 +247,22 @@ void BoneTransformEditor::_update_custom_pose_properties() {
 }
 
 void BoneTransformEditor::_update_transform_properties(Transform tform) {
-	Quat rot = tform.get_basis();
-	Vector3 rot_rad = rot.get_euler();
-	Vector3 rot_degrees = Vector3(Math::rad2deg(rot_rad.x), Math::rad2deg(rot_rad.y), Math::rad2deg(rot_rad.z));
-	Vector3 tr = tform.get_origin();
+	Basis rotation_basis = tform.get_basis();
+	Vector3 rotation_radians = rotation_basis.get_rotation_euler();
+	Vector3 rotation_degrees = Vector3(Math::rad2deg(rotation_radians.x), Math::rad2deg(rotation_radians.y), Math::rad2deg(rotation_radians.z));
+	Vector3 translation = tform.get_origin();
 	Vector3 scale = tform.basis.get_scale();
 
-	for (int i = 0; i < TRANSLATION_COMPONENTS; i++) {
-		translation_slider[i]->set_value(tr[i]);
-	}
-
-	for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) {
-		rotation_slider[i]->set_value(rot_degrees[i]);
-	}
-
-	for (int i = 0; i < SCALE_COMPONENTS; i++) {
-		scale_slider[i]->set_value(scale[i]);
-	}
-
-	transform_slider[0]->set_value(tform.get_basis()[Vector3::AXIS_X].x);
-	transform_slider[1]->set_value(tform.get_basis()[Vector3::AXIS_X].y);
-	transform_slider[2]->set_value(tform.get_basis()[Vector3::AXIS_X].z);
-	transform_slider[3]->set_value(tform.get_basis()[Vector3::AXIS_Y].x);
-	transform_slider[4]->set_value(tform.get_basis()[Vector3::AXIS_Y].y);
-	transform_slider[5]->set_value(tform.get_basis()[Vector3::AXIS_Y].z);
-	transform_slider[6]->set_value(tform.get_basis()[Vector3::AXIS_Z].x);
-	transform_slider[7]->set_value(tform.get_basis()[Vector3::AXIS_Z].y);
-	transform_slider[8]->set_value(tform.get_basis()[Vector3::AXIS_Z].z);
-
-	for (int i = 0; i < TRANSLATION_COMPONENTS; i++) {
-		transform_slider[BASIS_COMPONENTS + i]->set_value(tform.get_origin()[i]);
-	}
+	translation_property->update_using_vector(translation);
+	rotation_property->update_using_vector(rotation_degrees);
+	scale_property->update_using_vector(scale);
+	transform_property->update_using_transform(tform);
 
 	update_enabled_checkbox();
 	updating = false;
 }
 
 BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) :
-		translation_slider(),
-		rotation_slider(),
-		scale_slider(),
-		transform_slider(),
 		skeleton(p_skeleton),
 		key_button(nullptr),
 		enabled_checkbox(nullptr),
@@ -397,7 +301,7 @@ void BoneTransformEditor::_key_button_pressed() {
 		return;
 
 	// Need to normalize the basis before you key it
-	Transform tform = compute_transform(true);
+	Transform tform = compute_transform_from_vector3s();
 	tform.orthonormalize();
 	AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(skeleton, name, tform);
 }
@@ -627,8 +531,7 @@ void Skeleton3DEditor::update_joint_tree() {
 	items.insert(-1, root);
 
 	const Vector<int> &joint_porder = skeleton->get_bone_process_orders();
-
-	Ref<Texture> bone_icon = get_theme_icon("Skeleton3D", "EditorIcons");
+	Ref<Texture> bone_icon = get_theme_icon("BoneAttachment3D", "EditorIcons");
 
 	for (int i = 0; i < joint_porder.size(); ++i) {
 		const int b_idx = joint_porder[i];
@@ -714,11 +617,11 @@ void Skeleton3DEditor::_notification(int p_what) {
 			update_joint_tree();
 			update_editors();
 
-			get_tree()->connect_compat("node_removed", this, "_node_removed", Vector<Variant>(), Object::CONNECT_ONESHOT);
-			joint_tree->connect_compat("item_selected", this, "_joint_tree_selection_changed");
-			joint_tree->connect_compat("item_rmb_selected", this, "_joint_tree_rmb_select");
+			get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT);
+			joint_tree->connect("item_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_selection_changed));
+			joint_tree->connect("item_rmb_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_rmb_select));
 #ifdef TOOLS_ENABLED
-			skeleton->connect_compat("pose_updated", this, "_update_properties");
+			skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));
 #endif // TOOLS_ENABLED
 
 			break;

+ 17 - 23
editor/plugins/skeleton_3d_editor_plugin.h

@@ -41,30 +41,19 @@ class PhysicalBone3D;
 class Skeleton3DEditorPlugin;
 class Button;
 class CheckBox;
+class EditorPropertyTransform;
+class EditorPropertyVector3;
 
 class BoneTransformEditor : public VBoxContainer {
 	GDCLASS(BoneTransformEditor, VBoxContainer);
 
-	static const int32_t TRANSLATION_COMPONENTS = 3;
-	static const int32_t ROTATION_DEGREES_COMPONENTS = 3;
-	static const int32_t SCALE_COMPONENTS = 3;
-	static const int32_t BASIS_COMPONENTS = 9;
-	static const int32_t BASIS_SPLIT_COMPONENTS = 3;
-	static const int32_t TRANSFORM_COMPONENTS = 12;
-	static const int32_t TRANSFORM_SPLIT_COMPONENTS = 3;
-	static const int32_t TRANSFORM_CONTROL_COMPONENTS = 3;
-
 	EditorInspectorSection *section;
 
-	GridContainer *translation_grid;
-	GridContainer *rotation_grid;
-	GridContainer *scale_grid;
-	GridContainer *transform_grid;
-
-	EditorSpinSlider *translation_slider[TRANSLATION_COMPONENTS];
-	EditorSpinSlider *rotation_slider[ROTATION_DEGREES_COMPONENTS];
-	EditorSpinSlider *scale_slider[SCALE_COMPONENTS];
-	EditorSpinSlider *transform_slider[TRANSFORM_COMPONENTS];
+	EditorPropertyVector3 *translation_property;
+	EditorPropertyVector3 *rotation_property;
+	EditorPropertyVector3 *scale_property;
+	EditorInspectorSection *transform_section;
+	EditorPropertyTransform *transform_property;
 
 	Rect2 background_rects[5];
 
@@ -83,17 +72,22 @@ class BoneTransformEditor : public VBoxContainer {
 	String label;
 
 	void create_editors();
-	void setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner);
 
-	void _value_changed(const double p_value, const bool p_from_transform);
-
-	Transform compute_transform(const bool p_from_transform) const;
+	// Called when one of the EditorSpinSliders are changed.
+	void _value_changed(const double p_value);
+	// Called when the one of the EditorPropertyVector3 are updated.
+	void _value_changed_vector3(const String p_property_name, const Vector3 p_vector, const StringName p_edited_property_name, const bool p_boolean);
+	// Called when the transform_property is updated.
+	void _value_changed_transform(const String p_property_name, const Transform p_transform, const StringName p_edited_property_name, const bool p_boolean);
+	// Changes the transform to the given transform and updates the UI accordingly.
+	void _change_transform(Transform p_new_transform);
+	// Creates a Transform using the EditorPropertyVector3 properties.
+	Transform compute_transform_from_vector3s() const;
 
 	void update_enabled_checkbox();
 
 protected:
 	void _notification(int p_what);
-	static void _bind_methods();
 
 public:
 	BoneTransformEditor(Skeleton3D *p_skeleton);

+ 14 - 0
scene/3d/skeleton_3d.cpp

@@ -67,6 +67,8 @@ SkinReference::~SkinReference() {
 	RS::get_singleton()->free(skeleton);
 }
 
+///////////////////////////////////////
+
 bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
 	String path = p_path;
 
@@ -853,6 +855,15 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
 	return skin_ref;
 }
 
+// helper functions
+Transform Skeleton3D::bone_transform_to_world_transform(Transform p_bone_transform) {
+	return get_global_transform() * p_bone_transform;
+}
+
+Transform Skeleton3D::world_transform_to_bone_transform(Transform p_world_transform) {
+	return get_global_transform().affine_inverse() * p_world_transform;
+}
+
 void Skeleton3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_bone_process_orders"), &Skeleton3D::get_bone_process_orders);
 	ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone);
@@ -892,6 +903,9 @@ void Skeleton3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose);
 	ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose);
 
+	ClassDB::bind_method(D_METHOD("bone_transform_to_world_transform", "bone_transform"), &Skeleton3D::bone_transform_to_world_transform);
+	ClassDB::bind_method(D_METHOD("world_transform_to_bone_transform", "world_transform"), &Skeleton3D::world_transform_to_bone_transform);
+
 #ifndef _3D_DISABLED
 
 	ClassDB::bind_method(D_METHOD("set_animate_physical_bones"), &Skeleton3D::set_animate_physical_bones);

+ 9 - 5
scene/3d/skeleton_3d.h

@@ -71,10 +71,6 @@ class Skeleton3D : public Node3D {
 private:
 	friend class SkinReference;
 
-	Set<SkinReference *> skin_bindings;
-
-	void _skin_changed();
-
 	struct Bone {
 		String name;
 
@@ -116,6 +112,10 @@ private:
 		}
 	};
 
+	Set<SkinReference *> skin_bindings;
+
+	void _skin_changed();
+
 	bool animate_physical_bones;
 	Vector<Bone> bones;
 	Vector<int> process_order;
@@ -200,6 +200,10 @@ public:
 
 	Ref<SkinReference> register_skin(const Ref<Skin> &p_skin);
 
+	// Helper functions
+	Transform bone_transform_to_world_transform(Transform p_transform);
+	Transform world_transform_to_bone_transform(Transform p_transform);
+
 #ifndef _3D_DISABLED
 	// Physical bone API
 
@@ -213,7 +217,7 @@ public:
 	PhysicalBone3D *get_physical_bone_parent(int p_bone);
 
 private:
-	/// This is a slow API os it's cached
+	/// This is a slow API, so it's cached
 	PhysicalBone3D *_get_physical_bone_parent(int p_bone);
 	void _rebuild_physical_bones_cache();