diff options
Diffstat (limited to 'editor')
-rw-r--r-- | editor/editor_node.cpp | 2 | ||||
-rw-r--r-- | editor/icons/BoneMapHumanBody.svg | 1 | ||||
-rw-r--r-- | editor/icons/BoneMapHumanFace.svg | 1 | ||||
-rw-r--r-- | editor/icons/BoneMapHumanLeftHand.svg | 1 | ||||
-rw-r--r-- | editor/icons/BoneMapHumanRightHand.svg | 1 | ||||
-rw-r--r-- | editor/icons/BoneMapperHandle.svg | 1 | ||||
-rw-r--r-- | editor/icons/BoneMapperHandleCircle.svg | 1 | ||||
-rw-r--r-- | editor/icons/BoneMapperHandleSelected.svg | 1 | ||||
-rw-r--r-- | editor/import/post_import_plugin_skeleton_renamer.cpp | 144 | ||||
-rw-r--r-- | editor/import/post_import_plugin_skeleton_renamer.h | 46 | ||||
-rw-r--r-- | editor/import/resource_importer_scene.cpp | 77 | ||||
-rw-r--r-- | editor/import/resource_importer_scene.h | 2 | ||||
-rw-r--r-- | editor/import/scene_import_settings.cpp | 11 | ||||
-rw-r--r-- | editor/import/scene_import_settings.h | 1 | ||||
-rw-r--r-- | editor/plugins/bone_map_editor_plugin.cpp | 439 | ||||
-rw-r--r-- | editor/plugins/bone_map_editor_plugin.h | 176 |
16 files changed, 892 insertions, 13 deletions
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index b196cadcb1..bf0d126de4 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -134,6 +134,7 @@ #include "editor/plugins/audio_stream_editor_plugin.h" #include "editor/plugins/audio_stream_randomizer_editor_plugin.h" #include "editor/plugins/bit_map_editor_plugin.h" +#include "editor/plugins/bone_map_editor_plugin.h" #include "editor/plugins/camera_3d_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/collision_polygon_2d_editor_plugin.h" @@ -7146,6 +7147,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(GradientTexture2DEditorPlugin)); add_editor_plugin(memnew(BitMapEditorPlugin)); add_editor_plugin(memnew(RayCast2DEditorPlugin)); + add_editor_plugin(memnew(BoneMapEditorPlugin)); for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) { add_editor_plugin(EditorPlugins::create(i)); diff --git a/editor/icons/BoneMapHumanBody.svg b/editor/icons/BoneMapHumanBody.svg new file mode 100644 index 0000000000..2c2c5db1f6 --- /dev/null +++ b/editor/icons/BoneMapHumanBody.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m926.5 217.162c-11.5-2-26.03 4.547-37.5 6.5-15.723 2.678-25.238 3.24-33.333 5.167-1.227.292-3.103.763-5.792.958 0 0-.019.16-.052.437-36.819.994-106.823-6.062-138.156-2.062-23.816 3.041-86.334-5.667-105.667-6-13.911-.239-59.292-4.583-71.75-2.5-.667-4.083-1.5-10.75.95-17.468 14.881-7.246 27.229-21.569 35.341-38.467.922 4.424 6.252 4.929 12.459-14.231 5.662-17.478 2.324-22.254-2.313-22.525.172-2.056.279-4.105.313-6.142.788-48.041-15-78.667-69-78.667s-69.787 30.626-69 78.667c.033 2.036.141 4.086.313 6.142-4.637.271-7.975 5.048-2.313 22.525 6.207 19.16 11.537 18.655 12.459 14.231 8.113 16.897 20.461 31.221 35.342 38.467 2.449 6.718 1.617 13.385.949 17.468-12.457-2.083-57.838 2.261-71.75 2.5-19.332.333-81.85 9.041-105.666 6-31.333-4-101.337 3.056-138.156 2.062-.033-.276-.053-.437-.053-.437-2.689-.195-4.564-.666-5.791-.958-8.096-1.927-17.611-2.489-33.334-5.167-11.469-1.953-26-8.5-37.5-6.5-3.367.586 6 9.834 15.5 12.334 13.635 3.588 25.25 10.666 36 13.166-2.25 3.75-15.59 7.063-23 12-5.336 3.557 6.5 6.5 12 5 20.842-5.684 22.973.389 37.514-9.019 30.078 4.078 102.537 20.514 122.154 14.186 12.457-4.018 100.332 7.083 142.332 5.833 6.039-.18 1.656 65.563 2 73.5 3 69-16.842 133.135-18.666 169.667-1.92 38.42-3.42 57.919 7.666 131.333 6.967 46.126-2.521 82.079-2 94 6 137 29 172 4 221-14 27.44 67.449 26.958 65 9-3.012-22.092-12.666-22.333-10.666-46.333 1.896-22.768 16.049-151.298 8.666-206.667-2-15 0-26 2-66 2.355-47.101 7-88 14-123 7 35 11.645 75.899 14 123 2 40 4 51 2 66-7.383 55.369 6.77 183.899 8.667 206.667 2 24-7.654 24.241-10.667 46.333-2.449 17.958 79 18.44 65-9-25-49-2-84 4-221 .522-11.921-8.966-47.874-2-94 11.086-73.414 9.586-92.913 7.667-131.333-1.824-36.532-21.667-100.667-18.667-169.667.345-7.938-4.039-73.68 2-73.5 42 1.25 129.876-9.852 142.333-5.833 19.616 6.328 92.076-10.107 122.153-14.186 14.541 9.407 16.673 3.335 37.514 9.019 5.5 1.5 17.336-1.443 12-5-7.409-4.937-20.75-8.25-23-12 10.75-2.5 22.366-9.578 36.001-13.166 9.5-2.5 18.866-11.748 15.499-12.334z" fill="#b2b2b2"/></svg> diff --git a/editor/icons/BoneMapHumanFace.svg b/editor/icons/BoneMapHumanFace.svg new file mode 100644 index 0000000000..6cb21140bc --- /dev/null +++ b/editor/icons/BoneMapHumanFace.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m788.105 552.967c17.995-57.892 31.896-124.566 30.875-198.071-3.758-270.403-249.846-251.479-295.568-244.947-359.868 51.409-219.047 452.358-220.453 496.426-4.899 153.499 83.686 170.991 161.665 215.554 2.646 1.512 7.259 1.786 13.313 1.111 7.223 25.179 11.762 59.035 9.548 75.638-3.266 24.495 209.021 24.495 209.021 0 0-62.883 12.233-124.363 33.827-188.89 7.143-2.284 16.054-7.601 25.963-16.95 13.681-12.908 34.839-21.774 45.726-63.145 15.615-59.338 3.869-76.074-13.917-76.726z" fill="#b2b2b2"/></svg> diff --git a/editor/icons/BoneMapHumanLeftHand.svg b/editor/icons/BoneMapHumanLeftHand.svg new file mode 100644 index 0000000000..08c68bb4be --- /dev/null +++ b/editor/icons/BoneMapHumanLeftHand.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m703.906 786.098c7.046-66.929 28.135-153.363 18.529-260.192-1.143-12.71-4.5-48.282-4.46-82.732.025-21.174-2.111-48.505-1.975-64.174.167-19.333-.428-41.584-.625-55.755-1.052-75.44-13.225-85.827-30.813-85.827-17.246 0-26.77 14.266-27.062 84.582-.061 14.42.5 51 .5 58.5 0 17.508-.333 34.167 0 53.5.447 25.955-4.279 68-9 68-3.902 0-8.099-39.299-9.575-76.999-.756-19.326-3.219-58.336-2.6-70.102 1.759-33.413.474-58.914 1.537-90.165 3.183-93.607-13.016-111.729-34.695-111.729-21.973 0-35.979 57.688-34.849 114.224.128 6.394-1.165 50.739.188 89.859.754 21.811-1.07 49.627-1.683 69.67-1.095 35.768-5.755 63.896-8.869 63.896-2.641 0-4.135-32.584-5.456-65.706-.859-21.557-4.468-58.477-3.664-83.616 1.886-59.012-1.139-110.226-1.063-121.501.635-94.955-14.66-123.101-36.052-123.101-21.476 0-37.188 30.192-36.6 123.343.067 10.53-2.62 99.926-1.759 121.816.865 21.992-2.773 65.062-3.517 84.818-1.299 34.521-6.49 63.947-9.124 63.947-3.281 0-10.794-25.638-11.724-60.965-.587-22.275 1.231-50.99.624-70.688-1.257-40.707-3.175-64.631-3.877-99.708-1.945-97.182-16.352-106.289-38.142-106.289-17.957 0-32.453 28.673-32.657 115.03-.065 27.702-2.429 62.626-.315 94.329.805 12.081-.622 42.512-1.875 73.894-.799 20.007-1.102 47.501-1.137 63.775-.17 78.595-26.712 133.424-36.555 131.308-30.333-6.521-51.648-43.918-71.219-117.307-10.551-39.566-36.667-71.149-69.9-77.813-25.9-5.193-19.783 46.161-1.319 125.293 8.65 37.068 27.909 86.227 39.566 122.655 31.653 98.917 125.574 188.563 160.903 228.546 17.146 19.403 236.894 19.403 264.59 0 11.525-8.07 43.087-101.557 45.724-126.616z" fill="#b2b2b2"/></svg> diff --git a/editor/icons/BoneMapHumanRightHand.svg b/editor/icons/BoneMapHumanRightHand.svg new file mode 100644 index 0000000000..4e40af35d8 --- /dev/null +++ b/editor/icons/BoneMapHumanRightHand.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m320.094 786.098c-7.046-66.929-28.135-153.363-18.529-260.192 1.143-12.71 4.5-48.282 4.46-82.732-.025-21.174 2.111-48.505 1.975-64.174-.167-19.333.428-41.584.625-55.755 1.052-75.44 13.225-85.827 30.813-85.827 17.246 0 26.77 14.266 27.062 84.582.061 14.42-.5 51-.5 58.5 0 17.508.333 34.167 0 53.5-.447 25.955 4.279 68 9 68 3.902 0 8.099-39.299 9.575-76.999.756-19.326 3.219-58.336 2.6-70.102-1.759-33.413-.474-58.914-1.537-90.165-3.183-93.607 13.016-111.729 34.695-111.729 21.973 0 35.979 57.688 34.849 114.224-.128 6.394 1.165 50.739-.188 89.859-.754 21.811 1.07 49.627 1.683 69.67 1.095 35.768 5.755 63.896 8.869 63.896 2.641 0 4.135-32.584 5.456-65.706.859-21.557 4.468-58.477 3.664-83.616-1.886-59.012 1.139-110.226 1.063-121.501-.635-94.955 14.66-123.101 36.052-123.101 21.476 0 37.188 30.192 36.6 123.343-.067 10.53 2.62 99.926 1.759 121.816-.865 21.992 2.773 65.062 3.517 84.818 1.299 34.521 6.49 63.947 9.124 63.947 3.281 0 10.794-25.638 11.724-60.965.587-22.275-1.231-50.99-.624-70.688 1.257-40.707 3.176-64.631 3.877-99.708 1.945-97.182 16.352-106.289 38.142-106.289 17.957 0 32.453 28.673 32.657 115.03.065 27.702 2.429 62.626.314 94.329-.805 12.081.622 42.512 1.875 73.894.799 20.007 1.102 47.501 1.137 63.775.171 78.595 26.713 133.424 36.556 131.308 30.333-6.521 51.648-43.918 71.219-117.307 10.551-39.566 36.667-71.149 69.9-77.813 25.9-5.193 19.783 46.161 1.318 125.293-8.649 37.068-27.909 86.227-39.566 122.655-31.652 98.917-125.573 188.563-160.902 228.546-17.146 19.403-236.894 19.403-264.59 0-11.525-8.07-43.087-101.557-45.724-126.616z" fill="#b2b2b2"/></svg> diff --git a/editor/icons/BoneMapperHandle.svg b/editor/icons/BoneMapperHandle.svg new file mode 100644 index 0000000000..8c7d7e1d70 --- /dev/null +++ b/editor/icons/BoneMapperHandle.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 12 12" height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" fill-opacity=".2941" r="5"/><circle cx="6" cy="6" fill="#fff" r="4"/></svg> diff --git a/editor/icons/BoneMapperHandleCircle.svg b/editor/icons/BoneMapperHandleCircle.svg new file mode 100644 index 0000000000..ecf97669b8 --- /dev/null +++ b/editor/icons/BoneMapperHandleCircle.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 12 12" height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" fill="#fff" r="3"/></svg> diff --git a/editor/icons/BoneMapperHandleSelected.svg b/editor/icons/BoneMapperHandleSelected.svg new file mode 100644 index 0000000000..729a443f6e --- /dev/null +++ b/editor/icons/BoneMapperHandleSelected.svg @@ -0,0 +1 @@ +<svg enable-background="new -506.5 517.5 12 12" height="12" viewBox="-506.5 517.5 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><circle cx="-500.5" cy="523.5" fill-opacity=".2941" r="5"/><g fill="#fff"><circle cx="-500.5" cy="523.5" r="4"/><path d="m-499.5 517.5h5v5h-1v-4h-4z"/><path d="m-494.5 524.5v5h-5v-1h4v-4z"/><path d="m-501.5 529.5h-5v-5h1v4h4z"/><path d="m-506.5 522.5v-5h5v1h-4v4z"/></g></svg> diff --git a/editor/import/post_import_plugin_skeleton_renamer.cpp b/editor/import/post_import_plugin_skeleton_renamer.cpp new file mode 100644 index 0000000000..b0c4bc8c30 --- /dev/null +++ b/editor/import/post_import_plugin_skeleton_renamer.cpp @@ -0,0 +1,144 @@ +/*************************************************************************/ +/* post_import_plugin_skeleton_renamer.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 "post_import_plugin_skeleton_renamer.h" + +#include "editor/import/scene_import_settings.h" +#include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/3d/skeleton_3d.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/bone_map.h" + +void PostImportPluginSkeletonRenamer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) { + if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones"), true)); + } +} + +void PostImportPluginSkeletonRenamer::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) { + if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { + // Prepare objects. + Object *map = p_options["retarget/bone_map"].get_validated_object(); + if (!map || !bool(p_options["retarget/bone_renamer/rename_bones"])) { + return; + } + BoneMap *bone_map = Object::cast_to<BoneMap>(map); + Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node); + + // Rename bones in Skeleton3D. + { + int len = skeleton->get_bone_count(); + for (int i = 0; i < len; i++) { + StringName bn = bone_map->find_profile_bone_name(skeleton->get_bone_name(i)); + if (bn) { + skeleton->set_bone_name(i, bn); + } + } + } + + // Rename bones in Skin. + { + TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D"); + while (nodes.size()) { + ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back()); + Ref<Skin> skin = mi->get_skin(); + if (skin.is_valid()) { + Node *node = mi->get_node(mi->get_skeleton_path()); + if (node) { + Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node); + if (mesh_skeleton && node == skeleton) { + int len = skin->get_bind_count(); + for (int i = 0; i < len; i++) { + StringName bn = bone_map->find_profile_bone_name(skin->get_bind_name(i)); + if (bn) { + skin->set_bind_name(i, bn); + } + } + } + } + } + } + } + + // Rename bones in AnimationPlayer. + { + TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer"); + while (nodes.size()) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); + List<StringName> anims; + ap->get_animation_list(&anims); + for (const StringName &name : anims) { + Ref<Animation> anim = ap->get_animation(name); + int len = anim->get_track_count(); + for (int i = 0; i < len; i++) { + if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) { + continue; + } + String track_path = String(anim->track_get_path(i).get_concatenated_names()); + Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); + if (node) { + Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); + if (track_skeleton && track_skeleton == skeleton) { + StringName bn = bone_map->find_profile_bone_name(anim->track_get_path(i).get_subname(0)); + if (bn) { + anim->track_set_path(i, track_path + ":" + bn); + } + } + } + } + } + } + } + + // Rename bones in all Nodes by calling method. + { + Vector<Variant> vargs; + vargs.push_back(p_base_scene); + vargs.push_back(skeleton); + vargs.push_back(bone_map); + const Variant **argptrs = (const Variant **)alloca(sizeof(const Variant **) * vargs.size()); + const Variant *args = vargs.ptr(); + uint32_t argcount = vargs.size(); + for (uint32_t i = 0; i < argcount; i++) { + argptrs[i] = &args[i]; + } + + TypedArray<Node> nodes = p_base_scene->find_children("*"); + while (nodes.size()) { + Node *nd = Object::cast_to<Node>(nodes.pop_back()); + Callable::CallError ce; + nd->callp("_notify_skeleton_bones_renamed", argptrs, argcount, ce); + } + } + } +} + +PostImportPluginSkeletonRenamer::PostImportPluginSkeletonRenamer() { +} diff --git a/editor/import/post_import_plugin_skeleton_renamer.h b/editor/import/post_import_plugin_skeleton_renamer.h new file mode 100644 index 0000000000..73cbabd1c5 --- /dev/null +++ b/editor/import/post_import_plugin_skeleton_renamer.h @@ -0,0 +1,46 @@ +/*************************************************************************/ +/* post_import_plugin_skeleton_renamer.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 POST_IMPORT_PLUGIN_SKELETON_RENAMER_H +#define POST_IMPORT_PLUGIN_SKELETON_RENAMER_H + +#include "resource_importer_scene.h" + +class PostImportPluginSkeletonRenamer : public EditorScenePostImportPlugin { + GDCLASS(PostImportPluginSkeletonRenamer, EditorScenePostImportPlugin); + +public: + virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) override; + virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) override; + + PostImportPluginSkeletonRenamer(); +}; + +#endif // POST_IMPORT_PLUGIN_SKELETON_RENAMER_H diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 171ef5bf4c..a9c43e573f 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -232,6 +232,7 @@ void EditorScenePostImportPlugin::_bind_methods() { BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MATERIAL); BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION); BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE); + BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE); BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MAX); } @@ -766,6 +767,27 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< } { + //make sure this is unique + node_settings = node_settings.duplicate(true); + //fill node settings for this node with default values + List<ImportOption> iopts; + if (Object::cast_to<ImporterMeshInstance3D>(p_node)) { + get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE, &iopts); + } else if (Object::cast_to<AnimationPlayer>(p_node)) { + get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts); + } else if (Object::cast_to<Skeleton3D>(p_node)) { + get_internal_import_options(INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, &iopts); + } else { + get_internal_import_options(INTERNAL_IMPORT_CATEGORY_NODE, &iopts); + } + for (const ImportOption &E : iopts) { + if (!node_settings.has(E.option.name)) { + node_settings[E.option.name] = E.default_value; + } + } + } + + { ObjectID node_id = p_node->get_instance_id(); for (int i = 0; i < post_importer_plugins.size(); i++) { post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_NODE, p_root, p_node, Ref<Resource>(), node_settings); @@ -785,6 +807,16 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< } } + if (Object::cast_to<Skeleton3D>(p_node)) { + ObjectID node_id = p_node->get_instance_id(); + for (int i = 0; i < post_importer_plugins.size(); i++) { + post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, p_root, p_node, Ref<Resource>(), node_settings); + if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue + break; + } + } + } + if (Object::cast_to<ImporterMeshInstance3D>(p_node)) { ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node); @@ -799,6 +831,16 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< if (!mat_id.is_empty() && p_material_data.has(mat_id)) { Dictionary matdata = p_material_data[mat_id]; + { + //fill node settings for this node with default values + List<ImportOption> iopts; + get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MATERIAL, &iopts); + for (const ImportOption &E : iopts) { + if (!matdata.has(E.option.name)) { + matdata[E.option.name] = E.default_value; + } + } + } for (int j = 0; j < post_importer_plugins.size(); j++) { post_importer_plugins.write[j]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL, p_root, p_node, mat, matdata); @@ -966,19 +1008,6 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< if (Object::cast_to<AnimationPlayer>(p_node)) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node); - { - //make sure this is unique - node_settings = node_settings.duplicate(true); - //fill node settings for this node with default values - List<ImportOption> iopts; - get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts); - for (const ImportOption &E : iopts) { - if (!node_settings.has(E.option.name)) { - node_settings[E.option.name] = E.default_value; - } - } - } - for (int i = 0; i < post_importer_plugins.size(); i++) { post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, p_root, p_node, Ref<Resource>(), node_settings); } @@ -1385,6 +1414,10 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"), false)); } } break; + case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: { + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "retarget/bone_map", PROPERTY_HINT_RESOURCE_TYPE, "BoneMap", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Variant())); + } break; default: { } } @@ -1499,6 +1532,12 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor } } } break; + case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: { + const bool use_retarget = p_options["retarget/bone_map"].get_validated_object() != nullptr; + if (p_option != "retarget/bone_map" && p_option.begins_with("retarget/")) { + return use_retarget; + } + } break; default: { } } @@ -1534,6 +1573,8 @@ bool ResourceImporterScene::get_internal_option_update_view_required(InternalImp } break; case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: { } break; + case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: { + } break; default: { } } @@ -1622,6 +1663,16 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m if (!mesh_id.is_empty() && p_mesh_data.has(mesh_id)) { Dictionary mesh_settings = p_mesh_data[mesh_id]; + { + //fill node settings for this node with default values + List<ImportOption> iopts; + get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MESH, &iopts); + for (const ImportOption &E : iopts) { + if (!mesh_settings.has(E.option.name)) { + mesh_settings[E.option.name] = E.default_value; + } + } + } if (mesh_settings.has("generate/shadow_meshes")) { int shadow_meshes = mesh_settings["generate/shadow_meshes"]; diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 16cf3d651d..c143e86bd4 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -106,6 +106,7 @@ public: INTERNAL_IMPORT_CATEGORY_MATERIAL, INTERNAL_IMPORT_CATEGORY_ANIMATION, INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, + INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, INTERNAL_IMPORT_CATEGORY_MAX }; @@ -259,6 +260,7 @@ public: INTERNAL_IMPORT_CATEGORY_MATERIAL = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL, INTERNAL_IMPORT_CATEGORY_ANIMATION = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION, INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, + INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, INTERNAL_IMPORT_CATEGORY_MAX = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MAX }; diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 99d1658405..af145c22b4 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -339,6 +339,8 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) { category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE; } else if (Object::cast_to<AnimationPlayer>(p_node)) { category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE; + } else if (Object::cast_to<Skeleton3D>(p_node)) { + category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE; } else { category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE; } @@ -617,6 +619,13 @@ SceneImportSettings *SceneImportSettings::get_singleton() { return singleton; } +Node *SceneImportSettings::get_selected_node() { + if (selected_id == "") { + return nullptr; + } + return node_map[selected_id].node; +} + void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) { selecting = true; scene_import_settings_data->hide_options = false; @@ -657,6 +666,8 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) { scene_import_settings_data->hide_options = editing_animation; } else if (Object::cast_to<AnimationPlayer>(nd.node)) { scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE; + } else if (Object::cast_to<Skeleton3D>(nd.node)) { + scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE; } else { scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE; scene_import_settings_data->hide_options = editing_animation; diff --git a/editor/import/scene_import_settings.h b/editor/import/scene_import_settings.h index 81d13166ab..b5cf82f64b 100644 --- a/editor/import/scene_import_settings.h +++ b/editor/import/scene_import_settings.h @@ -201,6 +201,7 @@ public: void update_view(); void open_settings(const String &p_path, bool p_for_animation = false); static SceneImportSettings *get_singleton(); + Node *get_selected_node(); SceneImportSettings(); ~SceneImportSettings(); }; 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 |