summaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/bone_map_editor_plugin.cpp439
-rw-r--r--editor/plugins/bone_map_editor_plugin.h176
2 files changed, 615 insertions, 0 deletions
diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp
new file mode 100644
index 0000000000..fffadae3eb
--- /dev/null
+++ b/editor/plugins/bone_map_editor_plugin.cpp
@@ -0,0 +1,439 @@
+/*************************************************************************/
+/* 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/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;
+ }
+}
+
+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_selected) {
+ profile_bone_name = p_profile_bone_name;
+ selected = p_selected;
+}
+
+BoneMapperButton::~BoneMapperButton() {
+}
+
+void BoneMapperItem::create_editor() {
+ skeleton_bone_selector = memnew(EditorPropertyTextEnum);
+ skeleton_bone_selector->setup(skeleton_bone_names);
+ 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<BoneMap> &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<SkeletonProfile> profile = bone_map->get_profile();
+ if (profile.is_valid()) {
+ SkeletonProfileHumanoid *hmn = Object::cast_to<SkeletonProfileHumanoid>(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<Texture2D>());
+ }
+
+ 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), 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<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));
+ }
+
+ 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 {
+ 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<BoneMap> &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<Skeleton3D>(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: {
+ remove_child(bone_mapper);
+ bone_mapper->queue_delete();
+ }
+ }
+}
+
+BoneMapEditor::BoneMapEditor(Ref<BoneMap> &p_bone_map) {
+ bone_map = p_bone_map;
+}
+
+BoneMapEditor::~BoneMapEditor() {
+}
+
+bool EditorInspectorPluginBoneMap::can_handle(Object *p_object) {
+ return Object::cast_to<BoneMap>(p_object) != nullptr;
+}
+
+void EditorInspectorPluginBoneMap::parse_begin(Object *p_object) {
+ BoneMap *bm = Object::cast_to<BoneMap>(p_object);
+ if (!bm) {
+ return;
+ }
+ Ref<BoneMap> 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<EditorInspectorPluginBoneMap> inspector_plugin;
+ inspector_plugin.instantiate();
+ add_inspector_plugin(inspector_plugin);
+
+ Ref<PostImportPluginSkeletonRenamer> post_import_plugin_renamer;
+ post_import_plugin_renamer.instantiate();
+ add_scene_post_import_plugin(post_import_plugin_renamer);
+}
diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h
new file mode 100644
index 0000000000..0ec9f74373
--- /dev/null
+++ b/editor/plugins/bone_map_editor_plugin.h
@@ -0,0 +1,176 @@
+/*************************************************************************/
+/* bone_map_editor_plugin.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 BONE_MAP_EDITOR_H
+#define BONE_MAP_EDITOR_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "editor/editor_properties.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/gui/color_rect.h"
+#include "scene/gui/dialogs.h"
+#include "scene/resources/bone_map.h"
+#include "scene/resources/texture.h"
+
+class BoneMapperButton : public TextureButton {
+ GDCLASS(BoneMapperButton, TextureButton);
+
+public:
+ enum BoneMapState {
+ BONE_MAP_STATE_UNSET,
+ BONE_MAP_STATE_SET,
+ BONE_MAP_STATE_ERROR
+ };
+
+private:
+ StringName profile_bone_name;
+ bool selected = false;
+
+ TextureRect *circle;
+
+ void fetch_textures();
+
+protected:
+ void _notification(int p_what);
+
+public:
+ StringName get_profile_bone_name() const;
+ void set_state(BoneMapState p_state);
+
+ BoneMapperButton(const StringName p_profile_bone_name, bool p_selected);
+ ~BoneMapperButton();
+};
+
+class BoneMapperItem : public VBoxContainer {
+ GDCLASS(BoneMapperItem, VBoxContainer);
+
+ int button_id = -1;
+ StringName profile_bone_name;
+
+ PackedStringArray skeleton_bone_names;
+ Ref<BoneMap> bone_map;
+
+ EditorPropertyTextEnum *skeleton_bone_selector;
+
+ void _update_property();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+ virtual void _value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing);
+ virtual void create_editor();
+
+public:
+ void assign_button_id(int p_button_id);
+
+ BoneMapperItem(Ref<BoneMap> &p_bone_map, PackedStringArray p_skeleton_bone_names, const StringName &p_profile_bone_name = StringName());
+ ~BoneMapperItem();
+};
+
+class BoneMapper : public VBoxContainer {
+ GDCLASS(BoneMapper, VBoxContainer);
+
+ Skeleton3D *skeleton;
+ Ref<BoneMap> bone_map;
+
+ Vector<BoneMapperItem *> bone_mapper_items;
+
+ VBoxContainer *mapper_item_vbox;
+ HSeparator *separator;
+
+ int current_group_idx = 0;
+ int current_bone_idx = -1;
+
+ AspectRatioContainer *bone_mapper_field;
+ EditorPropertyEnum *profile_group_selector;
+ ColorRect *profile_bg;
+ TextureRect *profile_texture;
+ Vector<BoneMapperButton *> bone_mapper_buttons;
+
+ void create_editor();
+ void recreate_editor();
+ void clear_items();
+ void recreate_items();
+ void update_group_idx();
+ void _update_state();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+ virtual void _value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing);
+
+public:
+ void set_current_group_idx(int p_group_idx);
+ int get_current_group_idx() const;
+ void set_current_bone_idx(int p_bone_idx);
+ int get_current_bone_idx() const;
+
+ BoneMapper(Skeleton3D *p_skeleton, Ref<BoneMap> &p_bone_map);
+ ~BoneMapper();
+};
+
+class BoneMapEditor : public VBoxContainer {
+ GDCLASS(BoneMapEditor, VBoxContainer);
+
+ Skeleton3D *skeleton;
+ Ref<BoneMap> bone_map;
+ BoneMapper *bone_mapper;
+
+ void fetch_objects();
+ void clear_editors();
+ void create_editors();
+
+protected:
+ void _notification(int p_what);
+
+public:
+ BoneMapEditor(Ref<BoneMap> &p_bone_map);
+ ~BoneMapEditor();
+};
+
+class EditorInspectorPluginBoneMap : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorPluginBoneMap, EditorInspectorPlugin);
+ BoneMapEditor *editor;
+
+public:
+ virtual bool can_handle(Object *p_object) override;
+ virtual void parse_begin(Object *p_object) override;
+};
+
+class BoneMapEditorPlugin : public EditorPlugin {
+ GDCLASS(BoneMapEditorPlugin, EditorPlugin);
+
+public:
+ virtual String get_name() const override { return "BoneMap"; }
+ BoneMapEditorPlugin();
+};
+
+#endif // BONE_MAP_EDITOR_H