You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-08 12:40:44 +00:00
Add per-bone meta to Skeleton3D
Individual bones are not represented as `Node`s in Godot, in order to support meta functionality for them the skeleton has to carry the information similarly to how other per-bone properties are handled. - Also adds support for GLTF import/export
This commit is contained in:
@@ -52,7 +52,7 @@
|
||||
#include "scene/resources/skeleton_profile.h"
|
||||
#include "scene/resources/surface_tool.h"
|
||||
|
||||
void BoneTransformEditor::create_editors() {
|
||||
void BonePropertiesEditor::create_editors() {
|
||||
section = memnew(EditorInspectorSection);
|
||||
section->setup("trf_properties", label, this, Color(0.0f, 0.0f, 0.0f), true);
|
||||
section->unfold();
|
||||
@@ -61,7 +61,7 @@ void BoneTransformEditor::create_editors() {
|
||||
enabled_checkbox = memnew(EditorPropertyCheck());
|
||||
enabled_checkbox->set_label("Pose Enabled");
|
||||
enabled_checkbox->set_selectable(false);
|
||||
enabled_checkbox->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
|
||||
enabled_checkbox->connect("property_changed", callable_mp(this, &BonePropertiesEditor::_value_changed));
|
||||
section->get_vbox()->add_child(enabled_checkbox);
|
||||
|
||||
// Position property.
|
||||
@@ -69,8 +69,8 @@ void BoneTransformEditor::create_editors() {
|
||||
position_property->setup(-10000, 10000, 0.001, true);
|
||||
position_property->set_label("Position");
|
||||
position_property->set_selectable(false);
|
||||
position_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
|
||||
position_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
|
||||
position_property->connect("property_changed", callable_mp(this, &BonePropertiesEditor::_value_changed));
|
||||
position_property->connect("property_keyed", callable_mp(this, &BonePropertiesEditor::_property_keyed));
|
||||
section->get_vbox()->add_child(position_property);
|
||||
|
||||
// Rotation property.
|
||||
@@ -78,8 +78,8 @@ void BoneTransformEditor::create_editors() {
|
||||
rotation_property->setup(-10000, 10000, 0.001, true);
|
||||
rotation_property->set_label("Rotation");
|
||||
rotation_property->set_selectable(false);
|
||||
rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
|
||||
rotation_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
|
||||
rotation_property->connect("property_changed", callable_mp(this, &BonePropertiesEditor::_value_changed));
|
||||
rotation_property->connect("property_keyed", callable_mp(this, &BonePropertiesEditor::_property_keyed));
|
||||
section->get_vbox()->add_child(rotation_property);
|
||||
|
||||
// Scale property.
|
||||
@@ -87,8 +87,8 @@ void BoneTransformEditor::create_editors() {
|
||||
scale_property->setup(-10000, 10000, 0.001, true, true);
|
||||
scale_property->set_label("Scale");
|
||||
scale_property->set_selectable(false);
|
||||
scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
|
||||
scale_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
|
||||
scale_property->connect("property_changed", callable_mp(this, &BonePropertiesEditor::_value_changed));
|
||||
scale_property->connect("property_keyed", callable_mp(this, &BonePropertiesEditor::_property_keyed));
|
||||
section->get_vbox()->add_child(scale_property);
|
||||
|
||||
// Transform/Matrix section.
|
||||
@@ -102,50 +102,136 @@ void BoneTransformEditor::create_editors() {
|
||||
rest_matrix->set_label("Transform");
|
||||
rest_matrix->set_selectable(false);
|
||||
rest_section->get_vbox()->add_child(rest_matrix);
|
||||
|
||||
// Bone Metadata property
|
||||
meta_section = memnew(EditorInspectorSection);
|
||||
meta_section->setup("bone_meta", TTR("Bone Metadata"), this, Color(.0f, .0f, .0f), true);
|
||||
section->get_vbox()->add_child(meta_section);
|
||||
|
||||
add_metadata_button = EditorInspector::create_inspector_action_button(TTR("Add Bone Metadata"));
|
||||
add_metadata_button->connect(SceneStringName(pressed), callable_mp(this, &BonePropertiesEditor::_show_add_meta_dialog));
|
||||
section->get_vbox()->add_child(add_metadata_button);
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->connect("version_changed", callable_mp(this, &BonePropertiesEditor::_update_properties));
|
||||
undo_redo->connect("history_changed", callable_mp(this, &BonePropertiesEditor::_update_properties));
|
||||
}
|
||||
|
||||
void BoneTransformEditor::_notification(int p_what) {
|
||||
void BonePropertiesEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
const Color section_color = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor));
|
||||
section->set_bg_color(section_color);
|
||||
rest_section->set_bg_color(section_color);
|
||||
add_metadata_button->set_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void BoneTransformEditor::_value_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing) {
|
||||
if (updating) {
|
||||
void BonePropertiesEditor::_value_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing) {
|
||||
if (updating || !skeleton) {
|
||||
return;
|
||||
}
|
||||
if (skeleton) {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
|
||||
undo_redo->add_undo_property(skeleton, p_property, skeleton->get(p_property));
|
||||
undo_redo->add_do_property(skeleton, p_property, p_value);
|
||||
|
||||
Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
|
||||
if (se) {
|
||||
undo_redo->add_do_method(se, "update_joint_tree");
|
||||
undo_redo->add_undo_method(se, "update_joint_tree");
|
||||
}
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
|
||||
undo_redo->add_undo_property(skeleton, p_property, skeleton->get(p_property));
|
||||
undo_redo->add_do_property(skeleton, p_property, p_value);
|
||||
|
||||
undo_redo->commit_action();
|
||||
Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
|
||||
if (se) {
|
||||
undo_redo->add_do_method(se, "update_joint_tree");
|
||||
undo_redo->add_undo_method(se, "update_joint_tree");
|
||||
}
|
||||
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) :
|
||||
void BonePropertiesEditor::_meta_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing) {
|
||||
if (!skeleton || p_property.get_slicec('/', 2) != "bone_meta") {
|
||||
return;
|
||||
}
|
||||
|
||||
int bone = p_property.get_slicec('/', 1).to_int();
|
||||
if (bone >= skeleton->get_bone_count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String key = p_property.get_slicec('/', 3);
|
||||
if (!skeleton->has_bone_meta(1, key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(vformat(TTR("Modify metadata '%s' for bone '%s'"), key, skeleton->get_bone_name(bone)));
|
||||
undo_redo->add_do_property(skeleton, p_property, p_value);
|
||||
undo_redo->add_do_method(meta_editors[p_property], "update_property");
|
||||
undo_redo->add_undo_property(skeleton, p_property, skeleton->get_bone_meta(bone, key));
|
||||
undo_redo->add_undo_method(meta_editors[p_property], "update_property");
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void BonePropertiesEditor::_meta_deleted(const String &p_property) {
|
||||
if (!skeleton || p_property.get_slicec('/', 2) != "bone_meta") {
|
||||
return;
|
||||
}
|
||||
|
||||
int bone = p_property.get_slicec('/', 1).to_int();
|
||||
if (bone >= skeleton->get_bone_count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String key = p_property.get_slicec('/', 3);
|
||||
if (!skeleton->has_bone_meta(1, key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(vformat(TTR("Remove metadata '%s' from bone '%s'"), key, skeleton->get_bone_name(bone)));
|
||||
undo_redo->add_do_property(skeleton, p_property, Variant());
|
||||
undo_redo->add_undo_property(skeleton, p_property, skeleton->get_bone_meta(bone, key));
|
||||
undo_redo->commit_action();
|
||||
|
||||
emit_signal(SNAME("property_deleted"), p_property);
|
||||
}
|
||||
|
||||
void BonePropertiesEditor::_show_add_meta_dialog() {
|
||||
if (!add_meta_dialog) {
|
||||
add_meta_dialog = memnew(AddMetadataDialog());
|
||||
add_meta_dialog->connect(SceneStringName(confirmed), callable_mp(this, &BonePropertiesEditor::_add_meta_confirm));
|
||||
add_child(add_meta_dialog);
|
||||
}
|
||||
|
||||
int bone = Skeleton3DEditor::get_singleton()->get_selected_bone();
|
||||
StringName dialog_title = skeleton->get_bone_name(bone);
|
||||
|
||||
List<StringName> existing_meta_keys;
|
||||
skeleton->get_bone_meta_list(bone, &existing_meta_keys);
|
||||
add_meta_dialog->open(dialog_title, existing_meta_keys);
|
||||
}
|
||||
|
||||
void BonePropertiesEditor::_add_meta_confirm() {
|
||||
int bone = Skeleton3DEditor::get_singleton()->get_selected_bone();
|
||||
String name = add_meta_dialog->get_meta_name();
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(vformat(TTR("Add metadata '%s' to bone '%s'"), name, skeleton->get_bone_name(bone)));
|
||||
undo_redo->add_do_method(skeleton, "set_bone_meta", bone, name, add_meta_dialog->get_meta_defval());
|
||||
undo_redo->add_undo_method(skeleton, "set_bone_meta", bone, name, Variant());
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
BonePropertiesEditor::BonePropertiesEditor(Skeleton3D *p_skeleton) :
|
||||
skeleton(p_skeleton) {
|
||||
create_editors();
|
||||
}
|
||||
|
||||
void BoneTransformEditor::set_keyable(const bool p_keyable) {
|
||||
void BonePropertiesEditor::set_keyable(const bool p_keyable) {
|
||||
position_property->set_keying(p_keyable);
|
||||
rotation_property->set_keying(p_keyable);
|
||||
scale_property->set_keying(p_keyable);
|
||||
}
|
||||
|
||||
void BoneTransformEditor::set_target(const String &p_prop) {
|
||||
void BonePropertiesEditor::set_target(const String &p_prop) {
|
||||
enabled_checkbox->set_object_and_property(skeleton, p_prop + "enabled");
|
||||
enabled_checkbox->update_property();
|
||||
|
||||
@@ -162,7 +248,7 @@ void BoneTransformEditor::set_target(const String &p_prop) {
|
||||
rest_matrix->update_property();
|
||||
}
|
||||
|
||||
void BoneTransformEditor::_property_keyed(const String &p_path, bool p_advance) {
|
||||
void BonePropertiesEditor::_property_keyed(const String &p_path, bool p_advance) {
|
||||
AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
|
||||
if (!te || !te->has_keying()) {
|
||||
return;
|
||||
@@ -183,16 +269,17 @@ void BoneTransformEditor::_property_keyed(const String &p_path, bool p_advance)
|
||||
}
|
||||
}
|
||||
|
||||
void BoneTransformEditor::_update_properties() {
|
||||
void BonePropertiesEditor::_update_properties() {
|
||||
if (!skeleton) {
|
||||
return;
|
||||
}
|
||||
int selected = Skeleton3DEditor::get_singleton()->get_selected_bone();
|
||||
List<PropertyInfo> props;
|
||||
HashSet<StringName> meta_seen;
|
||||
skeleton->get_property_list(&props);
|
||||
for (const PropertyInfo &E : props) {
|
||||
PackedStringArray split = E.name.split("/");
|
||||
if (split.size() == 3 && split[0] == "bones") {
|
||||
if (split.size() >= 3 && split[0] == "bones") {
|
||||
if (split[1].to_int() == selected) {
|
||||
if (split[2] == "enabled") {
|
||||
enabled_checkbox->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
|
||||
@@ -224,9 +311,35 @@ void BoneTransformEditor::_update_properties() {
|
||||
rest_matrix->update_editor_property_status();
|
||||
rest_matrix->queue_redraw();
|
||||
}
|
||||
if (split[2] == "bone_meta") {
|
||||
meta_seen.insert(E.name);
|
||||
if (!meta_editors.find(E.name)) {
|
||||
EditorProperty *editor = EditorInspectorDefaultPlugin::get_editor_for_property(skeleton, E.type, E.name, PROPERTY_HINT_NONE, "", E.usage);
|
||||
editor->set_label(split[3]);
|
||||
editor->set_object_and_property(skeleton, E.name);
|
||||
editor->set_deletable(true);
|
||||
editor->set_selectable(false);
|
||||
editor->connect("property_changed", callable_mp(this, &BonePropertiesEditor::_meta_changed));
|
||||
editor->connect("property_deleted", callable_mp(this, &BonePropertiesEditor::_meta_deleted));
|
||||
|
||||
meta_section->get_vbox()->add_child(editor);
|
||||
editor->update_property();
|
||||
editor->update_editor_property_status();
|
||||
editor->queue_redraw();
|
||||
|
||||
meta_editors[E.name] = editor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// UI for any bone metadata prop not seen during the iteration has to be deleted
|
||||
for (KeyValue<StringName, EditorProperty *> iter : meta_editors) {
|
||||
if (!meta_seen.has(iter.key)) {
|
||||
callable_mp((Node *)meta_section->get_vbox(), &Node::remove_child).call_deferred(iter.value);
|
||||
meta_editors.remove(meta_editors.find(iter.key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Skeleton3DEditor *Skeleton3DEditor::singleton = nullptr;
|
||||
@@ -992,7 +1105,7 @@ void Skeleton3DEditor::create_editors() {
|
||||
SET_DRAG_FORWARDING_GCD(joint_tree, Skeleton3DEditor);
|
||||
s_con->add_child(joint_tree);
|
||||
|
||||
pose_editor = memnew(BoneTransformEditor(skeleton));
|
||||
pose_editor = memnew(BonePropertiesEditor(skeleton));
|
||||
pose_editor->set_label(TTR("Bone Transform"));
|
||||
pose_editor->set_visible(false);
|
||||
add_child(pose_editor);
|
||||
|
||||
Reference in New Issue
Block a user