/*************************************************************************/ /* bone_map_editor_plugin.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 "bone_map_editor_plugin.h" #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() { if (selected) { set_normal_texture(get_theme_icon(SNAME("BoneMapperHandleSelected"), SNAME("EditorIcons"))); } else { set_normal_texture(get_theme_icon(SNAME("BoneMapperHandle"), SNAME("EditorIcons"))); } set_offset(SIDE_LEFT, 0); set_offset(SIDE_RIGHT, 0); set_offset(SIDE_TOP, 0); set_offset(SIDE_BOTTOM, 0); circle = memnew(TextureRect); circle->set_texture(get_theme_icon(SNAME("BoneMapperHandleCircle"), SNAME("EditorIcons"))); add_child(circle); set_state(BONE_MAP_STATE_UNSET); } StringName BoneMapperButton::get_profile_bone_name() const { return profile_bone_name; } void BoneMapperButton::set_state(BoneMapState p_state) { switch (p_state) { case BONE_MAP_STATE_UNSET: { circle->set_modulate(EditorSettings::get_singleton()->get("editors/bone_mapper/handle_colors/unset")); } break; case BONE_MAP_STATE_SET: { circle->set_modulate(EditorSettings::get_singleton()->get("editors/bone_mapper/handle_colors/set")); } break; case BONE_MAP_STATE_ERROR: { circle->set_modulate(EditorSettings::get_singleton()->get("editors/bone_mapper/handle_colors/error")); } break; default: { } break; } } bool BoneMapperButton::is_require() const { return require; } void BoneMapperButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { fetch_textures(); } break; } } 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; } BoneMapperButton::~BoneMapperButton() { } void BoneMapperItem::create_editor() { skeleton_bone_selector = memnew(EditorPropertyTextEnum); 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)); skeleton_bone_selector->update_property(); skeleton_bone_selector->connect("property_changed", callable_mp(this, &BoneMapperItem::_value_changed)); add_child(skeleton_bone_selector); } void BoneMapperItem::_update_property() { if (skeleton_bone_selector->get_edited_object() && skeleton_bone_selector->get_edited_property()) { skeleton_bone_selector->update_property(); } } void BoneMapperItem::_value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { bone_map->set(p_property, p_value); } void BoneMapperItem::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { create_editor(); bone_map->connect("bone_map_updated", callable_mp(this, &BoneMapperItem::_update_property)); } break; case NOTIFICATION_EXIT_TREE: { if (!bone_map.is_null() && bone_map->is_connected("bone_map_updated", callable_mp(this, &BoneMapperItem::_update_property))) { bone_map->disconnect("bone_map_updated", callable_mp(this, &BoneMapperItem::_update_property)); } } break; } } void BoneMapperItem::_bind_methods() { } BoneMapperItem::BoneMapperItem(Ref &p_bone_map, PackedStringArray p_skeleton_bone_names, const StringName &p_profile_bone_name) { bone_map = p_bone_map; skeleton_bone_names = p_skeleton_bone_names; profile_bone_name = p_profile_bone_name; } BoneMapperItem::~BoneMapperItem() { } void BoneMapper::create_editor() { profile_group_selector = memnew(EditorPropertyEnum); profile_group_selector->set_label("Group"); profile_group_selector->set_selectable(false); profile_group_selector->set_object_and_property(this, "current_group_idx"); profile_group_selector->update_property(); profile_group_selector->connect("property_changed", callable_mp(this, &BoneMapper::_value_changed)); add_child(profile_group_selector); bone_mapper_field = memnew(AspectRatioContainer); bone_mapper_field->set_stretch_mode(AspectRatioContainer::STRETCH_FIT); bone_mapper_field->set_custom_minimum_size(Vector2(0, 256.0) * EDSCALE); bone_mapper_field->set_h_size_flags(Control::SIZE_FILL); add_child(bone_mapper_field); profile_bg = memnew(ColorRect); profile_bg->set_color(Color(0, 0, 0, 1)); profile_bg->set_h_size_flags(Control::SIZE_FILL); profile_bg->set_v_size_flags(Control::SIZE_FILL); bone_mapper_field->add_child(profile_bg); profile_texture = memnew(TextureRect); profile_texture->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); profile_texture->set_ignore_texture_size(true); profile_texture->set_h_size_flags(Control::SIZE_FILL); profile_texture->set_v_size_flags(Control::SIZE_FILL); bone_mapper_field->add_child(profile_texture); mapper_item_vbox = memnew(VBoxContainer); add_child(mapper_item_vbox); separator = memnew(HSeparator); add_child(separator); recreate_items(); } void BoneMapper::update_group_idx() { if (!bone_map->get_profile().is_valid()) { return; } PackedStringArray group_names; int len = bone_map->get_profile()->get_group_size(); for (int i = 0; i < len; i++) { group_names.push_back(bone_map->get_profile()->get_group_name(i)); } if (current_group_idx >= len) { current_group_idx = 0; } if (len > 0) { profile_group_selector->setup(group_names); profile_group_selector->update_property(); profile_group_selector->set_read_only(false); } } void BoneMapper::set_current_group_idx(int p_group_idx) { current_group_idx = p_group_idx; recreate_editor(); } int BoneMapper::get_current_group_idx() const { return current_group_idx; } void BoneMapper::set_current_bone_idx(int p_bone_idx) { current_bone_idx = p_bone_idx; recreate_editor(); } int BoneMapper::get_current_bone_idx() const { return current_bone_idx; } void BoneMapper::recreate_editor() { // Clear buttons. int len = bone_mapper_buttons.size(); for (int i = 0; i < len; i++) { profile_texture->remove_child(bone_mapper_buttons[i]); memdelete(bone_mapper_buttons[i]); } bone_mapper_buttons.clear(); // Organize mapper items. len = bone_mapper_items.size(); for (int i = 0; i < len; i++) { bone_mapper_items[i]->set_visible(current_bone_idx == i); } Ref profile = bone_map->get_profile(); if (profile.is_valid()) { SkeletonProfileHumanoid *hmn = Object::cast_to(profile.ptr()); if (hmn) { StringName hmn_group_name = profile->get_group_name(current_group_idx); if (hmn_group_name == "Body") { profile_texture->set_texture(get_theme_icon(SNAME("BoneMapHumanBody"), SNAME("EditorIcons"))); } else if (hmn_group_name == "Face") { profile_texture->set_texture(get_theme_icon(SNAME("BoneMapHumanFace"), SNAME("EditorIcons"))); } else if (hmn_group_name == "LeftHand") { profile_texture->set_texture(get_theme_icon(SNAME("BoneMapHumanLeftHand"), SNAME("EditorIcons"))); } else if (hmn_group_name == "RightHand") { profile_texture->set_texture(get_theme_icon(SNAME("BoneMapHumanRightHand"), SNAME("EditorIcons"))); } } else { profile_texture->set_texture(profile->get_texture(current_group_idx)); } } else { profile_texture->set_texture(Ref()); } if (!profile.is_valid()) { return; } 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), 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); Vector2 vc = profile->get_handle_offset(i); bone_mapper_buttons.push_back(mb); profile_texture->add_child(mb); mb->set_anchor(SIDE_LEFT, vc.x); mb->set_anchor(SIDE_RIGHT, vc.x); mb->set_anchor(SIDE_TOP, vc.y); mb->set_anchor(SIDE_BOTTOM, vc.y); } } _update_state(); } void BoneMapper::clear_items() { // Clear items. int len = bone_mapper_items.size(); for (int i = 0; i < len; i++) { mapper_item_vbox->remove_child(bone_mapper_items[i]); memdelete(bone_mapper_items[i]); } bone_mapper_items.clear(); } void BoneMapper::recreate_items() { clear_items(); // Create items by profile. Ref profile = bone_map->get_profile(); if (profile.is_valid()) { PackedStringArray skeleton_bone_names; int len = skeleton->get_bone_count(); for (int i = 0; i < len; i++) { skeleton_bone_names.push_back(skeleton->get_bone_name(i)); } len = profile->get_bone_size(); for (int i = 0; i < len; i++) { StringName bn = profile->get_bone_name(i); bone_mapper_items.append(memnew(BoneMapperItem(bone_map, skeleton_bone_names, bn))); mapper_item_vbox->add_child(bone_mapper_items[i]); } } update_group_idx(); recreate_editor(); } void BoneMapper::_update_state() { int len = bone_mapper_buttons.size(); for (int i = 0; i < len; i++) { StringName sbn = bone_map->get_skeleton_bone_name(bone_mapper_buttons[i]->get_profile_bone_name()); if (skeleton->find_bone(sbn) >= 0) { if (bone_map->get_skeleton_bone_name_count(sbn) == 1) { bone_mapper_buttons[i]->set_state(BoneMapperButton::BONE_MAP_STATE_SET); } else { bone_mapper_buttons[i]->set_state(BoneMapperButton::BONE_MAP_STATE_ERROR); } } else { 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); } } } } void BoneMapper::_value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { set(p_property, p_value); recreate_editor(); } void BoneMapper::_bind_methods() { ClassDB::bind_method(D_METHOD("set_current_group_idx", "current_group_idx"), &BoneMapper::set_current_group_idx); ClassDB::bind_method(D_METHOD("get_current_group_idx"), &BoneMapper::get_current_group_idx); ClassDB::bind_method(D_METHOD("set_current_bone_idx", "current_bone_idx"), &BoneMapper::set_current_bone_idx); ClassDB::bind_method(D_METHOD("get_current_bone_idx"), &BoneMapper::get_current_bone_idx); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_group_idx"), "set_current_group_idx", "get_current_group_idx"); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_bone_idx"), "set_current_bone_idx", "get_current_bone_idx"); } void BoneMapper::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { create_editor(); bone_map->connect("bone_map_updated", callable_mp(this, &BoneMapper::_update_state)); bone_map->connect("profile_updated", callable_mp(this, &BoneMapper::recreate_items)); } break; case NOTIFICATION_EXIT_TREE: { clear_items(); if (!bone_map.is_null()) { if (bone_map->is_connected("bone_map_updated", callable_mp(this, &BoneMapper::_update_state))) { bone_map->disconnect("bone_map_updated", callable_mp(this, &BoneMapper::_update_state)); } if (bone_map->is_connected("profile_updated", callable_mp(this, &BoneMapper::recreate_items))) { bone_map->disconnect("profile_updated", callable_mp(this, &BoneMapper::recreate_items)); } } } } } BoneMapper::BoneMapper(Skeleton3D *p_skeleton, Ref &p_bone_map) { skeleton = p_skeleton; bone_map = p_bone_map; } BoneMapper::~BoneMapper() { } void BoneMapEditor::create_editors() { if (!skeleton) { return; } bone_mapper = memnew(BoneMapper(skeleton, bone_map)); add_child(bone_mapper); } void BoneMapEditor::fetch_objects() { // Hackey... but it may be the easist way to get a selected object from "ImporterScene". SceneImportSettings *si = SceneImportSettings::get_singleton(); if (!si) { return; } Node *selected = si->get_selected_node(); if (selected) { Skeleton3D *sk = Object::cast_to(selected); if (!sk) { return; } skeleton = sk; } else { // Editor should not exist. skeleton = nullptr; } } void BoneMapEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { fetch_objects(); create_editors(); } break; case NOTIFICATION_EXIT_TREE: { if (!bone_mapper) { return; } remove_child(bone_mapper); bone_mapper->queue_delete(); } break; } } BoneMapEditor::BoneMapEditor(Ref &p_bone_map) { bone_map = p_bone_map; } BoneMapEditor::~BoneMapEditor() { } bool EditorInspectorPluginBoneMap::can_handle(Object *p_object) { return Object::cast_to(p_object) != nullptr; } void EditorInspectorPluginBoneMap::parse_begin(Object *p_object) { BoneMap *bm = Object::cast_to(p_object); if (!bm) { return; } Ref r(bm); editor = memnew(BoneMapEditor(r)); add_custom_control(editor); } BoneMapEditorPlugin::BoneMapEditorPlugin() { // Register properties in editor settings. EDITOR_DEF("editors/bone_mapper/handle_colors/set", Color(0.1, 0.6, 0.25)); EDITOR_DEF("editors/bone_mapper/handle_colors/error", Color(0.8, 0.2, 0.2)); EDITOR_DEF("editors/bone_mapper/handle_colors/unset", Color(0.3, 0.3, 0.3)); Ref inspector_plugin; inspector_plugin.instantiate(); add_inspector_plugin(inspector_plugin); Ref post_import_plugin_renamer; post_import_plugin_renamer.instantiate(); add_scene_post_import_plugin(post_import_plugin_renamer); Ref post_import_plugin_rest_fixer; post_import_plugin_rest_fixer.instantiate(); add_scene_post_import_plugin(post_import_plugin_rest_fixer); }