Path: blob/master/editor/scene/3d/skeleton_3d_editor_plugin.cpp
9903 views
/**************************************************************************/1/* skeleton_3d_editor_plugin.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "skeleton_3d_editor_plugin.h"3132#include "core/io/resource_saver.h"33#include "editor/animation/animation_player_editor_plugin.h"34#include "editor/editor_node.h"35#include "editor/editor_string_names.h"36#include "editor/editor_undo_redo_manager.h"37#include "editor/inspector/editor_properties.h"38#include "editor/inspector/editor_properties_vector.h"39#include "editor/scene/3d/node_3d_editor_plugin.h"40#include "editor/settings/editor_settings.h"41#include "editor/themes/editor_scale.h"42#include "scene/3d/mesh_instance_3d.h"43#include "scene/3d/physics/collision_shape_3d.h"44#include "scene/3d/physics/physical_bone_3d.h"45#include "scene/3d/physics/physical_bone_simulator_3d.h"46#include "scene/gui/separator.h"47#include "scene/gui/texture_rect.h"48#include "scene/resources/3d/capsule_shape_3d.h"49#include "scene/resources/skeleton_profile.h"50#include "scene/resources/surface_tool.h"5152void BonePropertiesEditor::create_editors() {53section = memnew(EditorInspectorSection);54section->setup("trf_properties", label, this, Color(0.0f, 0.0f, 0.0f), true);55section->unfold();56add_child(section);5758enabled_checkbox = memnew(EditorPropertyCheck());59enabled_checkbox->set_label("Pose Enabled");60enabled_checkbox->set_selectable(false);61enabled_checkbox->connect("property_changed", callable_mp(this, &BonePropertiesEditor::_value_changed));62section->get_vbox()->add_child(enabled_checkbox);6364// Position property.65position_property = memnew(EditorPropertyVector3());66position_property->setup(-10000, 10000, 0.001, true);67position_property->set_label("Position");68position_property->set_selectable(false);69position_property->connect("property_changed", callable_mp(this, &BonePropertiesEditor::_value_changed));70position_property->connect("property_keyed", callable_mp(this, &BonePropertiesEditor::_property_keyed));71section->get_vbox()->add_child(position_property);7273// Rotation property.74rotation_property = memnew(EditorPropertyQuaternion());75rotation_property->setup(-10000, 10000, 0.001, true);76rotation_property->set_label("Rotation");77rotation_property->set_selectable(false);78rotation_property->connect("property_changed", callable_mp(this, &BonePropertiesEditor::_value_changed));79rotation_property->connect("property_keyed", callable_mp(this, &BonePropertiesEditor::_property_keyed));80section->get_vbox()->add_child(rotation_property);8182// Scale property.83scale_property = memnew(EditorPropertyVector3());84scale_property->setup(-10000, 10000, 0.001, true, true);85scale_property->set_label("Scale");86scale_property->set_selectable(false);87scale_property->connect("property_changed", callable_mp(this, &BonePropertiesEditor::_value_changed));88scale_property->connect("property_keyed", callable_mp(this, &BonePropertiesEditor::_property_keyed));89section->get_vbox()->add_child(scale_property);9091// Transform/Matrix section.92rest_section = memnew(EditorInspectorSection);93rest_section->setup("trf_properties_transform", "Rest", this, Color(0.0f, 0.0f, 0.0f), true);94section->get_vbox()->add_child(rest_section);9596// Transform/Matrix property.97rest_matrix = memnew(EditorPropertyTransform3D());98rest_matrix->setup(-10000, 10000, 0.001, true);99rest_matrix->set_label("Transform");100rest_matrix->set_selectable(false);101rest_section->get_vbox()->add_child(rest_matrix);102103// Bone Metadata property104meta_section = memnew(EditorInspectorSection);105meta_section->setup("bone_meta", TTR("Bone Metadata"), this, Color(.0f, .0f, .0f), true);106section->get_vbox()->add_child(meta_section);107108EditorInspectorActionButton *add_metadata_button = memnew(EditorInspectorActionButton(TTRC("Add Bone Metadata"), SNAME("Add")));109add_metadata_button->connect(SceneStringName(pressed), callable_mp(this, &BonePropertiesEditor::_show_add_meta_dialog));110section->get_vbox()->add_child(add_metadata_button);111112EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();113undo_redo->connect("version_changed", callable_mp(this, &BonePropertiesEditor::_update_properties));114undo_redo->connect("history_changed", callable_mp(this, &BonePropertiesEditor::_update_properties));115}116117void BonePropertiesEditor::_notification(int p_what) {118switch (p_what) {119case NOTIFICATION_THEME_CHANGED: {120const Color section_color = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor));121section->set_bg_color(section_color);122rest_section->set_bg_color(section_color);123} break;124}125}126127void BonePropertiesEditor::_value_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing) {128if (updating || !skeleton) {129return;130}131132EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();133undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);134undo_redo->add_undo_property(skeleton, p_property, skeleton->get(p_property));135undo_redo->add_do_property(skeleton, p_property, p_value);136137Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();138if (se) {139undo_redo->add_do_method(se, "update_joint_tree");140undo_redo->add_undo_method(se, "update_joint_tree");141}142143undo_redo->commit_action();144}145146void BonePropertiesEditor::_meta_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing) {147if (!skeleton || p_property.get_slicec('/', 2) != "bone_meta") {148return;149}150151int bone = p_property.get_slicec('/', 1).to_int();152if (bone >= skeleton->get_bone_count()) {153return;154}155156String key = p_property.get_slicec('/', 3);157if (!skeleton->has_bone_meta(bone, key)) {158return;159}160161EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();162undo_redo->create_action(vformat(TTR("Modify metadata '%s' for bone '%s'"), key, skeleton->get_bone_name(bone)));163undo_redo->add_do_property(skeleton, p_property, p_value);164undo_redo->add_do_method(meta_editors[p_property], "update_property");165undo_redo->add_undo_property(skeleton, p_property, skeleton->get_bone_meta(bone, key));166undo_redo->add_undo_method(meta_editors[p_property], "update_property");167undo_redo->commit_action();168}169170void BonePropertiesEditor::_meta_deleted(const String &p_property) {171if (!skeleton || p_property.get_slicec('/', 2) != "bone_meta") {172return;173}174175int bone = p_property.get_slicec('/', 1).to_int();176if (bone >= skeleton->get_bone_count()) {177return;178}179180String key = p_property.get_slicec('/', 3);181if (!skeleton->has_bone_meta(bone, key)) {182return;183}184185EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();186undo_redo->create_action(vformat(TTR("Remove metadata '%s' from bone '%s'"), key, skeleton->get_bone_name(bone)));187undo_redo->add_do_property(skeleton, p_property, Variant());188undo_redo->add_undo_property(skeleton, p_property, skeleton->get_bone_meta(bone, key));189undo_redo->commit_action();190191emit_signal(SNAME("property_deleted"), p_property);192}193194void BonePropertiesEditor::_show_add_meta_dialog() {195if (!add_meta_dialog) {196add_meta_dialog = memnew(AddMetadataDialog());197add_meta_dialog->connect(SceneStringName(confirmed), callable_mp(this, &BonePropertiesEditor::_add_meta_confirm));198add_child(add_meta_dialog);199}200201int bone = Skeleton3DEditor::get_singleton()->get_selected_bone();202StringName dialog_title = skeleton->get_bone_name(bone);203204List<StringName> existing_meta_keys;205skeleton->get_bone_meta_list(bone, &existing_meta_keys);206add_meta_dialog->open(dialog_title, existing_meta_keys);207}208209void BonePropertiesEditor::_add_meta_confirm() {210int bone = Skeleton3DEditor::get_singleton()->get_selected_bone();211String name = add_meta_dialog->get_meta_name();212EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();213undo_redo->create_action(vformat(TTR("Add metadata '%s' to bone '%s'"), name, skeleton->get_bone_name(bone)));214undo_redo->add_do_method(skeleton, "set_bone_meta", bone, name, add_meta_dialog->get_meta_defval());215undo_redo->add_undo_method(skeleton, "set_bone_meta", bone, name, Variant());216undo_redo->commit_action();217}218219BonePropertiesEditor::BonePropertiesEditor(Skeleton3D *p_skeleton) :220skeleton(p_skeleton) {221create_editors();222}223224void BonePropertiesEditor::set_keyable(const bool p_keyable) {225position_property->set_keying(p_keyable);226rotation_property->set_keying(p_keyable);227scale_property->set_keying(p_keyable);228}229230void BonePropertiesEditor::set_target(const String &p_prop) {231enabled_checkbox->set_object_and_property(skeleton, p_prop + "enabled");232enabled_checkbox->update_property();233234position_property->set_object_and_property(skeleton, p_prop + "position");235position_property->update_property();236237rotation_property->set_object_and_property(skeleton, p_prop + "rotation");238rotation_property->update_property();239240scale_property->set_object_and_property(skeleton, p_prop + "scale");241scale_property->update_property();242243rest_matrix->set_object_and_property(skeleton, p_prop + "rest");244rest_matrix->update_property();245}246247void BonePropertiesEditor::_property_keyed(const String &p_path, bool p_advance) {248AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();249if (!te || !te->has_keying()) {250return;251}252te->_clear_selection();253PackedStringArray split = p_path.split("/");254if (split.size() == 3 && split[0] == "bones") {255int bone_idx = split[1].to_int();256if (split[2] == "position") {257te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_POSITION_3D, (Vector3)skeleton->get(p_path) / skeleton->get_motion_scale());258}259if (split[2] == "rotation") {260te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_ROTATION_3D, skeleton->get(p_path));261}262if (split[2] == "scale") {263te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_SCALE_3D, skeleton->get(p_path));264}265}266}267268void BonePropertiesEditor::_update_properties() {269if (!skeleton) {270return;271}272int selected = Skeleton3DEditor::get_singleton()->get_selected_bone();273List<PropertyInfo> props;274HashSet<StringName> meta_seen;275skeleton->get_property_list(&props);276for (const PropertyInfo &E : props) {277PackedStringArray split = E.name.split("/");278if (split.size() >= 3 && split[0] == "bones") {279if (split[1].to_int() == selected) {280if (split[2] == "enabled") {281enabled_checkbox->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);282enabled_checkbox->update_property();283enabled_checkbox->update_editor_property_status();284enabled_checkbox->queue_redraw();285}286if (split[2] == "position") {287position_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);288position_property->update_property();289position_property->update_editor_property_status();290position_property->queue_redraw();291}292if (split[2] == "rotation") {293rotation_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);294rotation_property->update_property();295rotation_property->update_editor_property_status();296rotation_property->queue_redraw();297}298if (split[2] == "scale") {299scale_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);300scale_property->update_property();301scale_property->update_editor_property_status();302scale_property->queue_redraw();303}304if (split[2] == "rest") {305rest_matrix->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);306rest_matrix->update_property();307rest_matrix->update_editor_property_status();308rest_matrix->queue_redraw();309}310if (split[2] == "bone_meta") {311meta_seen.insert(E.name);312if (!meta_editors.find(E.name)) {313EditorProperty *editor = EditorInspectorDefaultPlugin::get_editor_for_property(skeleton, E.type, E.name, PROPERTY_HINT_NONE, "", E.usage);314editor->set_label(split[3]);315editor->set_object_and_property(skeleton, E.name);316editor->set_deletable(true);317editor->set_selectable(false);318editor->connect("property_changed", callable_mp(this, &BonePropertiesEditor::_meta_changed));319editor->connect("property_deleted", callable_mp(this, &BonePropertiesEditor::_meta_deleted));320321meta_section->get_vbox()->add_child(editor);322editor->update_property();323editor->update_editor_property_status();324editor->queue_redraw();325326meta_editors[E.name] = editor;327}328}329}330}331}332// UI for any bone metadata prop not seen during the iteration has to be deleted333for (KeyValue<StringName, EditorProperty *> iter : meta_editors) {334if (!meta_seen.has(iter.key)) {335callable_mp((Node *)meta_section->get_vbox(), &Node::remove_child).call_deferred(iter.value);336meta_editors.remove(meta_editors.find(iter.key));337}338}339}340341Skeleton3DEditor *Skeleton3DEditor::singleton = nullptr;342343void Skeleton3DEditor::set_keyable(const bool p_keyable) {344keyable = p_keyable;345if (p_keyable) {346animation_hb->show();347} else {348animation_hb->hide();349}350}351352void Skeleton3DEditor::set_bone_options_enabled(const bool p_bone_options_enabled) {353skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_RESET_SELECTED_POSES, !p_bone_options_enabled);354skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_SELECTED_POSES_TO_RESTS, !p_bone_options_enabled);355}356357void Skeleton3DEditor::_bind_methods() {358ClassDB::bind_method(D_METHOD("update_all"), &Skeleton3DEditor::update_all);359ClassDB::bind_method(D_METHOD("update_joint_tree"), &Skeleton3DEditor::update_joint_tree);360}361362void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) {363if (!skeleton) {364return;365}366367switch (p_skeleton_option) {368case SKELETON_OPTION_RESET_ALL_POSES: {369reset_pose(true);370break;371}372case SKELETON_OPTION_RESET_SELECTED_POSES: {373reset_pose(false);374break;375}376case SKELETON_OPTION_ALL_POSES_TO_RESTS: {377pose_to_rest(true);378break;379}380case SKELETON_OPTION_SELECTED_POSES_TO_RESTS: {381pose_to_rest(false);382break;383}384case SKELETON_OPTION_CREATE_PHYSICAL_SKELETON: {385create_physical_skeleton();386break;387}388case SKELETON_OPTION_EXPORT_SKELETON_PROFILE: {389export_skeleton_profile();390break;391}392}393}394395void Skeleton3DEditor::reset_pose(const bool p_all_bones) {396if (!skeleton) {397return;398}399const int bone_count = skeleton->get_bone_count();400if (!bone_count) {401return;402}403404EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();405ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);406if (p_all_bones) {407for (int i = 0; i < bone_count; i++) {408ur->add_undo_method(skeleton, "set_bone_pose_position", i, skeleton->get_bone_pose_position(i));409ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i));410ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i));411}412ur->add_do_method(skeleton, "reset_bone_poses");413} else {414// Todo: Do method with multiple bone selection.415if (selected_bone == -1) {416ur->commit_action();417return;418}419ur->add_undo_method(skeleton, "set_bone_pose_position", selected_bone, skeleton->get_bone_pose_position(selected_bone));420ur->add_undo_method(skeleton, "set_bone_pose_rotation", selected_bone, skeleton->get_bone_pose_rotation(selected_bone));421ur->add_undo_method(skeleton, "set_bone_pose_scale", selected_bone, skeleton->get_bone_pose_scale(selected_bone));422ur->add_do_method(skeleton, "reset_bone_pose", selected_bone);423}424425ur->add_undo_method(this, "update_joint_tree");426ur->add_do_method(this, "update_joint_tree");427428ur->commit_action();429}430431void Skeleton3DEditor::insert_keys(const bool p_all_bones) {432if (!skeleton) {433return;434}435436bool pos_enabled = key_loc_button->is_pressed();437bool rot_enabled = key_rot_button->is_pressed();438bool scl_enabled = key_scale_button->is_pressed();439440int bone_len = skeleton->get_bone_count();441Node *root = EditorNode::get_singleton()->get_tree()->get_root();442String path = String(root->get_path_to(skeleton));443444AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();445te->make_insert_queue();446for (int i = 0; i < bone_len; i++) {447const String name = skeleton->get_bone_name(i);448449if (name.is_empty()) {450continue;451}452453if (pos_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_POSITION_3D))) {454te->insert_transform_key(skeleton, name, Animation::TYPE_POSITION_3D, skeleton->get_bone_pose_position(i) / skeleton->get_motion_scale());455}456if (rot_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_ROTATION_3D))) {457te->insert_transform_key(skeleton, name, Animation::TYPE_ROTATION_3D, skeleton->get_bone_pose_rotation(i));458}459if (scl_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_SCALE_3D))) {460te->insert_transform_key(skeleton, name, Animation::TYPE_SCALE_3D, skeleton->get_bone_pose_scale(i));461}462}463te->commit_insert_queue();464}465466void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) {467if (!skeleton) {468return;469}470const int bone_count = skeleton->get_bone_count();471if (!bone_count) {472return;473}474475EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();476ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);477if (p_all_bones) {478for (int i = 0; i < bone_count; i++) {479ur->add_do_method(skeleton, "set_bone_rest", i, skeleton->get_bone_pose(i));480ur->add_undo_method(skeleton, "set_bone_rest", i, skeleton->get_bone_rest(i));481}482} else {483// Todo: Do method with multiple bone selection.484if (selected_bone == -1) {485ur->commit_action();486return;487}488ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_pose(selected_bone));489ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));490}491492ur->add_undo_method(this, "update_joint_tree");493ur->add_do_method(this, "update_joint_tree");494495ur->commit_action();496}497498void Skeleton3DEditor::create_physical_skeleton() {499EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();500ERR_FAIL_NULL(get_tree());501Node *owner = get_tree()->get_edited_scene_root();502503const int bone_count = skeleton->get_bone_count();504505if (!bone_count) {506EditorNode::get_singleton()->show_warning(vformat(TTR("Cannot create a physical skeleton for a Skeleton3D node with no bones.")));507return;508}509510Vector<BoneInfo> bones_infos;511bones_infos.resize(bone_count);512513ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL);514515PhysicalBoneSimulator3D *simulator = memnew(PhysicalBoneSimulator3D);516ur->add_do_method(skeleton, "add_child", simulator);517ur->add_do_method(simulator, "set_owner", owner);518ur->add_do_method(simulator, "set_name", "PhysicalBoneSimulator3D");519for (int bone_id = 0; bone_count > bone_id; ++bone_id) {520const int parent = skeleton->get_bone_parent(bone_id);521522if (parent < 0) {523bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id);524} else {525const int parent_parent = skeleton->get_bone_parent(parent);526527bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);528529// Create physical bone on parent.530if (!bones_infos[parent].physical_bone) {531PhysicalBone3D *physical_bone = create_physical_bone(parent, bone_id, bones_infos);532if (physical_bone && physical_bone->get_child(0)) {533CollisionShape3D *collision_shape = Object::cast_to<CollisionShape3D>(physical_bone->get_child(0));534if (collision_shape) {535bones_infos.write[parent].physical_bone = physical_bone;536537ur->add_do_method(simulator, "add_child", physical_bone);538ur->add_do_method(physical_bone, "set_owner", owner);539ur->add_do_method(collision_shape, "set_owner", owner);540ur->add_do_property(physical_bone, "bone_name", skeleton->get_bone_name(parent));541542// Create joint between parent of parent.543if (parent_parent != -1) {544ur->add_do_method(physical_bone, "set_joint_type", PhysicalBone3D::JOINT_TYPE_PIN);545}546547ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), physical_bone);548ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), collision_shape);549550ur->add_do_reference(physical_bone);551ur->add_undo_method(simulator, "remove_child", physical_bone);552}553}554}555}556}557ur->add_undo_method(skeleton, "remove_child", simulator);558ur->commit_action();559}560561PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos) {562const Transform3D child_rest = skeleton->get_bone_rest(bone_child_id);563564const real_t half_height(child_rest.origin.length() * 0.5);565const real_t radius(half_height * 0.2);566567CapsuleShape3D *bone_shape_capsule = memnew(CapsuleShape3D);568bone_shape_capsule->set_height(half_height * 2);569bone_shape_capsule->set_radius(radius);570571CollisionShape3D *bone_shape = memnew(CollisionShape3D);572bone_shape->set_shape(bone_shape_capsule);573bone_shape->set_name("CollisionShape3D");574575Transform3D capsule_transform;576capsule_transform.basis.rows[0] = Vector3(1, 0, 0);577capsule_transform.basis.rows[1] = Vector3(0, 0, 1);578capsule_transform.basis.rows[2] = Vector3(0, -1, 0);579bone_shape->set_transform(capsule_transform);580581/// Get an up vector not collinear with child rest origin582Vector3 up = Vector3(0, 1, 0);583if (up.cross(child_rest.origin).is_zero_approx()) {584up = Vector3(0, 0, 1);585}586587Transform3D body_transform;588body_transform.basis = Basis::looking_at(child_rest.origin, up);589body_transform.origin = body_transform.basis.xform(Vector3(0, 0, -half_height));590591Transform3D joint_transform;592joint_transform.origin = Vector3(0, 0, half_height);593594PhysicalBone3D *physical_bone = memnew(PhysicalBone3D);595physical_bone->add_child(bone_shape);596physical_bone->set_name("Physical Bone " + skeleton->get_bone_name(bone_id));597physical_bone->set_body_offset(body_transform);598physical_bone->set_joint_offset(joint_transform);599return physical_bone;600}601602void Skeleton3DEditor::export_skeleton_profile() {603if (!skeleton->get_bone_count()) {604EditorNode::get_singleton()->show_warning(vformat(TTR("Cannot export a SkeletonProfile for a Skeleton3D node with no bones.")));605return;606}607608file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);609file_dialog->set_title(TTR("Export Skeleton Profile As..."));610611List<String> exts;612ResourceLoader::get_recognized_extensions_for_type("SkeletonProfile", &exts);613file_dialog->clear_filters();614for (const String &K : exts) {615file_dialog->add_filter("*." + K);616}617618file_dialog->popup_file_dialog();619}620621void Skeleton3DEditor::_file_selected(const String &p_file) {622// Export SkeletonProfile.623Ref<SkeletonProfile> sp(memnew(SkeletonProfile));624625// Build SkeletonProfile.626sp->set_group_size(1);627628Vector<Vector2> handle_positions;629Vector2 position_max;630Vector2 position_min;631632const int bone_count = skeleton->get_bone_count();633sp->set_bone_size(bone_count);634for (int i = 0; i < bone_count; i++) {635sp->set_bone_name(i, skeleton->get_bone_name(i));636int parent = skeleton->get_bone_parent(i);637if (parent >= 0) {638sp->set_bone_parent(i, skeleton->get_bone_name(parent));639}640sp->set_reference_pose(i, skeleton->get_bone_rest(i));641642Transform3D grest = skeleton->get_bone_global_rest(i);643handle_positions.append(Vector2(grest.origin.x, grest.origin.y));644if (i == 0) {645position_max = Vector2(grest.origin.x, grest.origin.y);646position_min = Vector2(grest.origin.x, grest.origin.y);647} else {648position_max = position_max.max(Vector2(grest.origin.x, grest.origin.y));649position_min = position_min.min(Vector2(grest.origin.x, grest.origin.y));650}651}652653// Layout handles provisionaly.654Vector2 bound = Vector2(position_max.x - position_min.x, position_max.y - position_min.y);655Vector2 center = Vector2((position_max.x + position_min.x) * 0.5, (position_max.y + position_min.y) * 0.5);656float nrm = MAX(bound.x, bound.y);657if (nrm > 0) {658for (int i = 0; i < bone_count; i++) {659handle_positions.write[i] = (handle_positions[i] - center) / nrm * 0.9;660sp->set_handle_offset(i, Vector2(0.5 + handle_positions[i].x, 0.5 - handle_positions[i].y));661}662}663664Error err = ResourceSaver::save(sp, p_file);665666if (err != OK) {667EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file: %s"), p_file));668return;669}670}671672Variant Skeleton3DEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {673TreeItem *selected = joint_tree->get_selected();674675if (!selected) {676return Variant();677}678679Ref<Texture> icon = selected->get_icon(0);680681VBoxContainer *vb = memnew(VBoxContainer);682HBoxContainer *hb = memnew(HBoxContainer);683TextureRect *tf = memnew(TextureRect);684tf->set_texture(icon);685tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);686hb->add_child(tf);687Label *label = memnew(Label(selected->get_text(0)));688label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);689hb->add_child(label);690vb->add_child(hb);691hb->set_modulate(Color(1, 1, 1, 1));692693set_drag_preview(vb);694Dictionary drag_data;695drag_data["type"] = "nodes";696drag_data["node"] = selected;697698return drag_data;699}700701bool Skeleton3DEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {702TreeItem *target = (p_point == Vector2(Math::INF, Math::INF)) ? joint_tree->get_selected() : joint_tree->get_item_at_position(p_point);703if (!target) {704return false;705}706707const String path = target->get_metadata(0);708if (!path.begins_with("bones/")) {709return false;710}711712TreeItem *selected = Object::cast_to<TreeItem>(Dictionary(p_data)["node"]);713if (target == selected) {714return false;715}716717const String path2 = target->get_metadata(0);718if (!path2.begins_with("bones/")) {719return false;720}721722return true;723}724725void Skeleton3DEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {726if (!can_drop_data_fw(p_point, p_data, p_from)) {727return;728}729730TreeItem *target = (p_point == Vector2(Math::INF, Math::INF)) ? joint_tree->get_selected() : joint_tree->get_item_at_position(p_point);731TreeItem *selected = Object::cast_to<TreeItem>(Dictionary(p_data)["node"]);732733const BoneId target_boneidx = String(target->get_metadata(0)).get_slicec('/', 1).to_int();734const BoneId selected_boneidx = String(selected->get_metadata(0)).get_slicec('/', 1).to_int();735736move_skeleton_bone(skeleton->get_path(), selected_boneidx, target_boneidx);737}738739void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx) {740Node *node = get_node_or_null(p_skeleton_path);741Skeleton3D *skeleton_node = Object::cast_to<Skeleton3D>(node);742ERR_FAIL_NULL(skeleton_node);743EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();744ur->create_action(TTR("Set Bone Parentage"));745// If the target is a child of ourselves, we move only *us* and not our children.746if (skeleton_node->is_bone_parent_of(p_target_boneidx, p_selected_boneidx)) {747const BoneId parent_idx = skeleton_node->get_bone_parent(p_selected_boneidx);748const int bone_count = skeleton_node->get_bone_count();749for (BoneId i = 0; i < bone_count; ++i) {750if (skeleton_node->get_bone_parent(i) == p_selected_boneidx) {751ur->add_undo_method(skeleton_node, "set_bone_parent", i, skeleton_node->get_bone_parent(i));752ur->add_do_method(skeleton_node, "set_bone_parent", i, parent_idx);753skeleton_node->set_bone_parent(i, parent_idx);754}755}756}757ur->add_undo_method(skeleton_node, "set_bone_parent", p_selected_boneidx, skeleton_node->get_bone_parent(p_selected_boneidx));758ur->add_do_method(skeleton_node, "set_bone_parent", p_selected_boneidx, p_target_boneidx);759760ur->add_undo_method(this, "update_joint_tree");761ur->add_do_method(this, "update_joint_tree");762763skeleton_node->set_bone_parent(p_selected_boneidx, p_target_boneidx);764765ur->commit_action();766}767768void Skeleton3DEditor::_joint_tree_selection_changed() {769TreeItem *selected = joint_tree->get_selected();770if (selected) {771const String path = selected->get_metadata(0);772if (!path.begins_with("bones/")) {773return;774}775const int b_idx = path.get_slicec('/', 1).to_int();776selected_bone = b_idx;777if (pose_editor) {778const String bone_path = "bones/" + itos(b_idx) + "/";779pose_editor->set_target(bone_path);780pose_editor->set_keyable(keyable);781}782}783784if (pose_editor && pose_editor->is_inside_tree()) {785pose_editor->set_visible(selected);786}787set_bone_options_enabled(selected);788789_update_properties();790_update_gizmo_visible();791}792793// May be not used with single select mode.794void Skeleton3DEditor::_joint_tree_rmb_select(const Vector2 &p_pos, MouseButton p_button) {795}796797void Skeleton3DEditor::_joint_tree_button_clicked(Object *p_item, int p_column, int p_id, MouseButton p_button) {798if (!skeleton) {799return;800}801802TreeItem *tree_item = Object::cast_to<TreeItem>(p_item);803if (tree_item) {804String tree_item_metadata = tree_item->get_metadata(0);805806String bone_enabled_property = tree_item_metadata + "/enabled";807String bone_parent_property = tree_item_metadata + "/parent";808String bone_name_property = tree_item_metadata + "/name";809String bone_position_property = tree_item_metadata + "/position";810String bone_rotation_property = tree_item_metadata + "/rotation";811String bone_scale_property = tree_item_metadata + "/scale";812String bone_rest_property = tree_item_metadata + "/rest";813814Variant current_enabled = skeleton->get(bone_enabled_property);815Variant current_parent = skeleton->get(bone_parent_property);816Variant current_name = skeleton->get(bone_name_property);817Variant current_position = skeleton->get(bone_position_property);818Variant current_rotation = skeleton->get(bone_rotation_property);819Variant current_scale = skeleton->get(bone_scale_property);820Variant current_rest = skeleton->get(bone_rest_property);821822EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();823ur->create_action(TTR("Revert Bone"));824825bool can_revert_enabled = EditorPropertyRevert::can_property_revert(skeleton, bone_enabled_property, ¤t_enabled);826if (can_revert_enabled) {827bool is_valid = false;828Variant new_enabled = EditorPropertyRevert::get_property_revert_value(skeleton, bone_enabled_property, &is_valid);829if (is_valid) {830ur->add_undo_method(skeleton, "set", bone_enabled_property, current_enabled);831ur->add_do_method(skeleton, "set", bone_enabled_property, new_enabled);832}833}834835bool can_revert_parent = EditorPropertyRevert::can_property_revert(skeleton, bone_parent_property, ¤t_parent);836if (can_revert_parent) {837bool is_valid = false;838Variant new_parent = EditorPropertyRevert::get_property_revert_value(skeleton, bone_parent_property, &is_valid);839if (is_valid) {840ur->add_undo_method(skeleton, "set", bone_parent_property, current_parent);841ur->add_do_method(skeleton, "set", bone_parent_property, new_parent);842}843}844bool can_revert_name = EditorPropertyRevert::can_property_revert(skeleton, bone_name_property, ¤t_name);845if (can_revert_name) {846bool is_valid = false;847Variant new_name = EditorPropertyRevert::get_property_revert_value(skeleton, bone_name_property, &is_valid);848if (is_valid) {849ur->add_undo_method(skeleton, "set", bone_name_property, current_name);850ur->add_do_method(skeleton, "set", bone_name_property, new_name);851}852}853bool can_revert_position = EditorPropertyRevert::can_property_revert(skeleton, bone_position_property, ¤t_position);854if (can_revert_position) {855bool is_valid = false;856Variant new_position = EditorPropertyRevert::get_property_revert_value(skeleton, bone_position_property, &is_valid);857if (is_valid) {858ur->add_undo_method(skeleton, "set", bone_position_property, current_position);859ur->add_do_method(skeleton, "set", bone_position_property, new_position);860}861}862bool can_revert_rotation = EditorPropertyRevert::can_property_revert(skeleton, bone_rotation_property, ¤t_rotation);863if (can_revert_rotation) {864bool is_valid = false;865Variant new_rotation = EditorPropertyRevert::get_property_revert_value(skeleton, bone_rotation_property, &is_valid);866if (is_valid) {867ur->add_undo_method(skeleton, "set", bone_rotation_property, current_rotation);868ur->add_do_method(skeleton, "set", bone_rotation_property, new_rotation);869}870}871bool can_revert_scale = EditorPropertyRevert::can_property_revert(skeleton, bone_scale_property, ¤t_scale);872if (can_revert_scale) {873bool is_valid = false;874Variant new_scale = EditorPropertyRevert::get_property_revert_value(skeleton, bone_scale_property, &is_valid);875if (is_valid) {876ur->add_undo_method(skeleton, "set", bone_scale_property, current_scale);877ur->add_do_method(skeleton, "set", bone_scale_property, new_scale);878}879}880bool can_revert_rest = EditorPropertyRevert::can_property_revert(skeleton, bone_rest_property, ¤t_rest);881if (can_revert_rest) {882bool is_valid = false;883Variant new_rest = EditorPropertyRevert::get_property_revert_value(skeleton, bone_rest_property, &is_valid);884if (is_valid) {885ur->add_undo_method(skeleton, "set", bone_rest_property, current_rest);886ur->add_do_method(skeleton, "set", bone_rest_property, new_rest);887}888}889890ur->add_undo_method(this, "update_all");891ur->add_do_method(this, "update_all");892893ur->commit_action();894}895return;896}897898void Skeleton3DEditor::_update_properties() {899if (pose_editor) {900pose_editor->_update_properties();901}902Node3DEditor::get_singleton()->update_transform_gizmo();903}904905void Skeleton3DEditor::update_joint_tree() {906joint_tree->clear();907908if (!skeleton) {909return;910}911912TreeItem *root = joint_tree->create_item();913914HashMap<int, TreeItem *> items;915916items.insert(-1, root);917918Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("Bone"));919920Vector<int> bones_to_process = skeleton->get_parentless_bones();921while (bones_to_process.size() > 0) {922int current_bone_idx = bones_to_process[0];923bones_to_process.erase(current_bone_idx);924925const int parent_idx = skeleton->get_bone_parent(current_bone_idx);926TreeItem *parent_item = items.find(parent_idx)->value;927928TreeItem *joint_item = joint_tree->create_item(parent_item);929items.insert(current_bone_idx, joint_item);930931joint_item->set_text(0, skeleton->get_bone_name(current_bone_idx));932joint_item->set_icon(0, bone_icon);933joint_item->set_selectable(0, true);934joint_item->set_metadata(0, "bones/" + itos(current_bone_idx));935936String bone_enabled_property = "bones/" + itos(current_bone_idx) + "/enabled";937String bone_parent_property = "bones/" + itos(current_bone_idx) + "/parent";938String bone_name_property = "bones/" + itos(current_bone_idx) + "/name";939String bone_position_property = "bones/" + itos(current_bone_idx) + "/position";940String bone_rotation_property = "bones/" + itos(current_bone_idx) + "/rotation";941String bone_scale_property = "bones/" + itos(current_bone_idx) + "/scale";942String bone_rest_property = "bones/" + itos(current_bone_idx) + "/rest";943944Variant current_enabled = skeleton->get(bone_enabled_property);945Variant current_parent = skeleton->get(bone_parent_property);946Variant current_name = skeleton->get(bone_name_property);947Variant current_position = skeleton->get(bone_position_property);948Variant current_rotation = skeleton->get(bone_rotation_property);949Variant current_scale = skeleton->get(bone_scale_property);950Variant current_rest = skeleton->get(bone_rest_property);951952bool can_revert_enabled = EditorPropertyRevert::can_property_revert(skeleton, bone_enabled_property, ¤t_enabled);953bool can_revert_parent = EditorPropertyRevert::can_property_revert(skeleton, bone_parent_property, ¤t_parent);954bool can_revert_name = EditorPropertyRevert::can_property_revert(skeleton, bone_name_property, ¤t_name);955bool can_revert_position = EditorPropertyRevert::can_property_revert(skeleton, bone_position_property, ¤t_position);956bool can_revert_rotation = EditorPropertyRevert::can_property_revert(skeleton, bone_rotation_property, ¤t_rotation);957bool can_revert_scale = EditorPropertyRevert::can_property_revert(skeleton, bone_scale_property, ¤t_scale);958bool can_revert_rest = EditorPropertyRevert::can_property_revert(skeleton, bone_rest_property, ¤t_rest);959960if (can_revert_enabled || can_revert_parent || can_revert_name || can_revert_position || can_revert_rotation || can_revert_scale || can_revert_rest) {961joint_item->add_button(0, get_editor_theme_icon(SNAME("ReloadSmall")), JOINT_BUTTON_REVERT, false, TTR("Revert"));962}963964// Add the bone's children to the list of bones to be processed.965Vector<int> current_bone_child_bones = skeleton->get_bone_children(current_bone_idx);966int child_bone_size = current_bone_child_bones.size();967for (int i = 0; i < child_bone_size; i++) {968bones_to_process.push_back(current_bone_child_bones[i]);969}970971if (current_bone_idx == selected_bone) {972joint_item->select(0);973}974}975}976977void Skeleton3DEditor::update_all() {978_update_properties();979update_joint_tree();980}981982void Skeleton3DEditor::create_editors() {983set_h_size_flags(SIZE_EXPAND_FILL);984set_focus_mode(FOCUS_ALL);985986Node3DEditor *ne = Node3DEditor::get_singleton();987AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();988989// Create File dialog.990file_dialog = memnew(EditorFileDialog);991file_dialog->connect("file_selected", callable_mp(this, &Skeleton3DEditor::_file_selected));992add_child(file_dialog);993994// Create Top Menu Bar.995topmenu_bar = memnew(HBoxContainer);996ne->add_control_to_menu_panel(topmenu_bar);997998// Create Skeleton Option in Top Menu Bar.999skeleton_options = memnew(MenuButton);1000skeleton_options->set_flat(false);1001skeleton_options->set_theme_type_variation("FlatMenuButton");1002topmenu_bar->add_child(skeleton_options);10031004skeleton_options->set_text(TTR("Skeleton3D"));10051006// Skeleton options.1007PopupMenu *p = skeleton_options->get_popup();1008p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/reset_all_poses", TTRC("Reset All Bone Poses")), SKELETON_OPTION_RESET_ALL_POSES);1009p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/reset_selected_poses", TTRC("Reset Selected Poses")), SKELETON_OPTION_RESET_SELECTED_POSES);1010p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/all_poses_to_rests", TTRC("Apply All Poses to Rests")), SKELETON_OPTION_ALL_POSES_TO_RESTS);1011p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/selected_poses_to_rests", TTRC("Apply Selected Poses to Rests")), SKELETON_OPTION_SELECTED_POSES_TO_RESTS);1012p->add_item(TTR("Create Physical Skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);1013p->add_item(TTR("Export Skeleton Profile"), SKELETON_OPTION_EXPORT_SKELETON_PROFILE);10141015p->connect(SceneStringName(id_pressed), callable_mp(this, &Skeleton3DEditor::_on_click_skeleton_option));1016set_bone_options_enabled(false);10171018Vector<Variant> button_binds;1019button_binds.resize(1);10201021edit_mode_button = memnew(Button);1022topmenu_bar->add_child(edit_mode_button);1023edit_mode_button->set_theme_type_variation(SceneStringName(FlatButton));1024edit_mode_button->set_toggle_mode(true);1025edit_mode_button->set_focus_mode(FOCUS_ACCESSIBILITY);1026edit_mode_button->set_tooltip_text(TTR("Edit Mode\nShow buttons on joints."));1027edit_mode_button->connect(SceneStringName(toggled), callable_mp(this, &Skeleton3DEditor::edit_mode_toggled));10281029edit_mode = false;10301031if (skeleton) {1032skeleton->add_child(handles_mesh_instance);1033handles_mesh_instance->set_skeleton_path(NodePath(""));1034}10351036// Keying buttons.1037animation_hb = memnew(HBoxContainer);1038topmenu_bar->add_child(animation_hb);1039animation_hb->add_child(memnew(VSeparator));1040animation_hb->hide();10411042key_loc_button = memnew(Button);1043key_loc_button->set_theme_type_variation(SceneStringName(FlatButton));1044key_loc_button->set_toggle_mode(true);1045key_loc_button->set_pressed(false);1046key_loc_button->set_focus_mode(FOCUS_ACCESSIBILITY);1047key_loc_button->set_tooltip_text(TTR("Translation mask for inserting keys."));1048animation_hb->add_child(key_loc_button);10491050key_rot_button = memnew(Button);1051key_rot_button->set_theme_type_variation(SceneStringName(FlatButton));1052key_rot_button->set_toggle_mode(true);1053key_rot_button->set_pressed(true);1054key_rot_button->set_focus_mode(FOCUS_ACCESSIBILITY);1055key_rot_button->set_tooltip_text(TTR("Rotation mask for inserting keys."));1056animation_hb->add_child(key_rot_button);10571058key_scale_button = memnew(Button);1059key_scale_button->set_theme_type_variation(SceneStringName(FlatButton));1060key_scale_button->set_toggle_mode(true);1061key_scale_button->set_pressed(false);1062key_scale_button->set_focus_mode(FOCUS_ACCESSIBILITY);1063key_scale_button->set_tooltip_text(TTR("Scale mask for inserting keys."));1064animation_hb->add_child(key_scale_button);10651066key_insert_button = memnew(Button);1067key_insert_button->set_theme_type_variation(SceneStringName(FlatButton));1068key_insert_button->set_focus_mode(FOCUS_ACCESSIBILITY);1069key_insert_button->connect(SceneStringName(pressed), callable_mp(this, &Skeleton3DEditor::insert_keys).bind(false));1070key_insert_button->set_tooltip_text(TTRC("Insert key (based on mask) for bones with an existing track."));1071key_insert_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_to_existing_tracks", TTRC("Insert Key (Existing Tracks)"), Key::INSERT));1072animation_hb->add_child(key_insert_button);10731074key_insert_all_button = memnew(Button);1075key_insert_all_button->set_theme_type_variation(SceneStringName(FlatButton));1076key_insert_all_button->set_focus_mode(FOCUS_ACCESSIBILITY);1077key_insert_all_button->connect(SceneStringName(pressed), callable_mp(this, &Skeleton3DEditor::insert_keys).bind(true));1078key_insert_all_button->set_tooltip_text(TTRC("Insert key (based on mask) for all bones."));1079key_insert_all_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_of_all_bones", TTRC("Insert Key (All Bones)"), KeyModifierMask::CMD_OR_CTRL + Key::INSERT));1080animation_hb->add_child(key_insert_all_button);10811082// Bone tree.1083bones_section = memnew(EditorInspectorSection);1084bones_section->setup("bones", "Bones", skeleton, Color(0.0f, 0.0, 0.0f), true);1085add_child(bones_section);1086bones_section->unfold();10871088ScrollContainer *s_con = memnew(ScrollContainer);1089s_con->set_h_size_flags(SIZE_EXPAND_FILL);1090s_con->set_custom_minimum_size(Size2(1, 350) * EDSCALE);1091bones_section->get_vbox()->add_child(s_con);10921093joint_tree = memnew(Tree);1094joint_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);1095joint_tree->set_columns(1);1096joint_tree->set_focus_mode(Control::FOCUS_NONE);1097joint_tree->set_select_mode(Tree::SELECT_SINGLE);1098joint_tree->set_hide_root(true);1099joint_tree->set_v_size_flags(SIZE_EXPAND_FILL);1100joint_tree->set_h_size_flags(SIZE_EXPAND_FILL);1101joint_tree->set_allow_rmb_select(true);1102joint_tree->set_theme_type_variation("TreeSecondary");1103SET_DRAG_FORWARDING_GCD(joint_tree, Skeleton3DEditor);1104s_con->add_child(joint_tree);11051106pose_editor = memnew(BonePropertiesEditor(skeleton));1107pose_editor->set_label(TTR("Bone Transform"));1108pose_editor->set_visible(false);1109add_child(pose_editor);11101111set_keyable(te->has_keying());1112}11131114void Skeleton3DEditor::_notification(int p_what) {1115switch (p_what) {1116case NOTIFICATION_ENTER_TREE: {1117update_joint_tree();11181119joint_tree->connect(SceneStringName(item_selected), callable_mp(this, &Skeleton3DEditor::_joint_tree_selection_changed));1120joint_tree->connect("item_mouse_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_rmb_select));1121joint_tree->connect("button_clicked", callable_mp(this, &Skeleton3DEditor::_joint_tree_button_clicked));11221123skeleton->connect(SceneStringName(pose_updated), callable_mp(this, &Skeleton3DEditor::_draw_gizmo));1124skeleton->connect(SceneStringName(pose_updated), callable_mp(this, &Skeleton3DEditor::_update_properties));1125skeleton->connect(SceneStringName(bone_enabled_changed), callable_mp(this, &Skeleton3DEditor::_bone_enabled_changed));1126skeleton->connect(SceneStringName(show_rest_only_changed), callable_mp(this, &Skeleton3DEditor::_update_gizmo_visible));11271128get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Object::CONNECT_ONE_SHOT);1129} break;1130case NOTIFICATION_READY: {1131// Will trigger NOTIFICATION_THEME_CHANGED, but won't cause any loops if called here.1132add_theme_constant_override("separation", 0);1133} break;1134case NOTIFICATION_THEME_CHANGED: {1135skeleton_options->set_button_icon(get_editor_theme_icon(SNAME("Skeleton3D")));1136edit_mode_button->set_button_icon(get_editor_theme_icon(SNAME("ToolBoneSelect")));1137key_loc_button->set_button_icon(get_editor_theme_icon(SNAME("KeyPosition")));1138key_rot_button->set_button_icon(get_editor_theme_icon(SNAME("KeyRotation")));1139key_scale_button->set_button_icon(get_editor_theme_icon(SNAME("KeyScale")));1140key_insert_button->set_button_icon(get_editor_theme_icon(SNAME("Key")));1141key_insert_all_button->set_button_icon(get_editor_theme_icon(SNAME("NewKey")));1142bones_section->set_bg_color(get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));11431144update_joint_tree();1145} break;1146case NOTIFICATION_PREDELETE: {1147if (skeleton) {1148select_bone(-1); // Requires that the joint_tree has not been deleted.1149skeleton->disconnect(SceneStringName(show_rest_only_changed), callable_mp(this, &Skeleton3DEditor::_update_gizmo_visible));1150skeleton->disconnect(SceneStringName(bone_enabled_changed), callable_mp(this, &Skeleton3DEditor::_bone_enabled_changed));1151skeleton->disconnect(SceneStringName(pose_updated), callable_mp(this, &Skeleton3DEditor::_draw_gizmo));1152skeleton->disconnect(SceneStringName(pose_updated), callable_mp(this, &Skeleton3DEditor::_update_properties));1153skeleton->set_transform_gizmo_visible(true);11541155if (handles_mesh_instance->get_parent()) {1156handles_mesh_instance->get_parent()->remove_child(handles_mesh_instance);1157}1158}1159edit_mode_toggled(false);1160} break;1161}1162}11631164void Skeleton3DEditor::_node_removed(Node *p_node) {1165if (skeleton && p_node == skeleton) {1166skeleton = nullptr;1167skeleton_options->hide();1168}11691170_update_properties();1171}11721173void Skeleton3DEditor::edit_mode_toggled(const bool pressed) {1174edit_mode = pressed;1175_update_gizmo_visible();1176}11771178Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, Skeleton3D *p_skeleton) :1179editor_plugin(e_plugin),1180skeleton(p_skeleton) {1181singleton = this;11821183// Handle.1184handle_material.instantiate();1185handle_shader.instantiate();1186handle_shader->set_code(R"(1187// Skeleton 3D gizmo handle shader.11881189shader_type spatial;1190render_mode unshaded, shadows_disabled, depth_draw_always, fog_disabled;11911192uniform sampler2D texture_albedo : source_color;1193uniform float point_size : hint_range(0, 128) = 32;11941195void vertex() {1196if (!OUTPUT_IS_SRGB) {1197COLOR.rgb = mix(pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb * (1.0 / 12.92), lessThan(COLOR.rgb, vec3(0.04045)));1198}11991200VERTEX = VERTEX;1201POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0);1202POSITION.z = mix(POSITION.z, POSITION.w, 0.999);1203POINT_SIZE = point_size;1204}12051206void fragment() {1207vec4 albedo_tex = texture(texture_albedo, POINT_COORD);1208vec3 col = albedo_tex.rgb + COLOR.rgb;1209col = vec3(min(col.r, 1.0), min(col.g, 1.0), min(col.b, 1.0));1210ALBEDO = col;12111212if (albedo_tex.a < 0.5) {1213discard;1214}12151216ALPHA = albedo_tex.a;1217}1218)");1219handle_material->set_shader(handle_shader);1220Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorBoneHandle"), EditorStringName(EditorIcons));1221handle_material->set_shader_parameter("point_size", handle->get_width());1222handle_material->set_shader_parameter("texture_albedo", handle);12231224handles_mesh_instance = memnew(MeshInstance3D);1225handles_mesh_instance->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF);1226handles_mesh.instantiate();1227handles_mesh_instance->set_mesh(handles_mesh);12281229create_editors();1230}12311232void Skeleton3DEditor::update_bone_original() {1233if (!skeleton) {1234return;1235}1236if (skeleton->get_bone_count() == 0 || selected_bone == -1) {1237return;1238}1239bone_original_position = skeleton->get_bone_pose_position(selected_bone);1240bone_original_rotation = skeleton->get_bone_pose_rotation(selected_bone);1241bone_original_scale = skeleton->get_bone_pose_scale(selected_bone);1242}12431244void Skeleton3DEditor::_hide_handles() {1245handles_mesh_instance->hide();1246}12471248void Skeleton3DEditor::_draw_gizmo() {1249if (!skeleton) {1250return;1251}12521253// If you call get_bone_global_pose() while drawing the surface, such as toggle rest mode,1254// the skeleton update will be done first and1255// the drawing surface will be interrupted once and an error will occur.1256skeleton->force_update_all_dirty_bones();12571258// Handles.1259if (edit_mode) {1260_draw_handles();1261} else {1262_hide_handles();1263}1264}12651266void Skeleton3DEditor::_draw_handles() {1267const int bone_count = skeleton->get_bone_count();12681269handles_mesh->clear_surfaces();12701271if (bone_count) {1272handles_mesh_instance->show();12731274handles_mesh->surface_begin(Mesh::PRIMITIVE_POINTS);12751276for (int i = 0; i < bone_count; i++) {1277Color c;1278if (i == selected_bone) {1279c = Color(1, 1, 0);1280} else {1281c = Color(0.1, 0.25, 0.8);1282}1283Vector3 point = skeleton->get_bone_global_pose(i).origin;1284handles_mesh->surface_set_color(c);1285handles_mesh->surface_add_vertex(point);1286}1287handles_mesh->surface_end();1288handles_mesh->surface_set_material(0, handle_material);1289} else {1290handles_mesh_instance->hide();1291}1292}12931294TreeItem *Skeleton3DEditor::_find(TreeItem *p_node, const NodePath &p_path) {1295if (!p_node) {1296return nullptr;1297}12981299NodePath np = p_node->get_metadata(0);1300if (np == p_path) {1301return p_node;1302}13031304TreeItem *children = p_node->get_first_child();1305while (children) {1306TreeItem *n = _find(children, p_path);1307if (n) {1308return n;1309}1310children = children->get_next();1311}13121313return nullptr;1314}13151316void Skeleton3DEditor::_subgizmo_selection_change() {1317if (!skeleton) {1318return;1319}13201321// Once validated by subgizmos_intersect_ray, but required if through inspector's bones tree.1322if (!edit_mode) {1323skeleton->clear_subgizmo_selection();1324return;1325}13261327int selected = -1;1328Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();1329if (se) {1330selected = se->get_selected_bone();1331}13321333if (selected >= 0) {1334Vector<Ref<Node3DGizmo>> gizmos = skeleton->get_gizmos();1335for (int i = 0; i < gizmos.size(); i++) {1336Ref<EditorNode3DGizmo> gizmo = gizmos[i];1337if (gizmo.is_null()) {1338continue;1339}1340Ref<Skeleton3DGizmoPlugin> plugin = gizmo->get_plugin();1341if (plugin.is_null()) {1342continue;1343}1344skeleton->set_subgizmo_selection(gizmo, selected, skeleton->get_bone_global_pose(selected));1345break;1346}1347} else {1348skeleton->clear_subgizmo_selection();1349}1350}13511352void Skeleton3DEditor::select_bone(int p_idx) {1353if (p_idx >= 0) {1354TreeItem *ti = _find(joint_tree->get_root(), "bones/" + itos(p_idx));1355if (ti) {1356// Make visible when it's collapsed.1357TreeItem *node = ti->get_parent();1358while (node && node != joint_tree->get_root()) {1359node->set_collapsed(false);1360node = node->get_parent();1361}1362ti->select(0);1363joint_tree->scroll_to_item(ti);1364}1365} else {1366selected_bone = -1;1367joint_tree->deselect_all();1368_joint_tree_selection_changed();1369}1370}13711372Skeleton3DEditor::~Skeleton3DEditor() {1373singleton = nullptr;13741375handles_mesh_instance->queue_free();13761377Node3DEditor *ne = Node3DEditor::get_singleton();13781379ne->remove_control_from_menu_panel(topmenu_bar);1380memdelete(topmenu_bar);1381}13821383bool EditorInspectorPluginSkeleton::can_handle(Object *p_object) {1384return Object::cast_to<Skeleton3D>(p_object) != nullptr;1385}13861387void EditorInspectorPluginSkeleton::parse_begin(Object *p_object) {1388Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_object);1389ERR_FAIL_NULL(skeleton);13901391skel_editor = memnew(Skeleton3DEditor(this, skeleton));1392add_custom_control(skel_editor);1393}13941395Skeleton3DEditorPlugin::Skeleton3DEditorPlugin() {1396skeleton_plugin = memnew(EditorInspectorPluginSkeleton);13971398EditorInspector::add_inspector_plugin(skeleton_plugin);13991400Ref<Skeleton3DGizmoPlugin> gizmo_plugin = Ref<Skeleton3DGizmoPlugin>(memnew(Skeleton3DGizmoPlugin));1401Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin);1402}14031404EditorPlugin::AfterGUIInput Skeleton3DEditorPlugin::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {1405Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();1406Node3DEditor *ne = Node3DEditor::get_singleton();1407if (se && se->is_edit_mode()) {1408const Ref<InputEventMouseButton> mb = p_event;1409if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {1410if (ne->get_tool_mode() != Node3DEditor::TOOL_MODE_SELECT) {1411if (!ne->is_gizmo_visible()) {1412return EditorPlugin::AFTER_GUI_INPUT_STOP;1413}1414}1415if (mb->is_pressed()) {1416se->update_bone_original();1417}1418}1419return EditorPlugin::AFTER_GUI_INPUT_CUSTOM;1420}1421return EditorPlugin::AFTER_GUI_INPUT_PASS;1422}14231424bool Skeleton3DEditorPlugin::handles(Object *p_object) const {1425return p_object->is_class("Skeleton3D");1426}14271428void Skeleton3DEditor::_bone_enabled_changed(const int p_bone_id) {1429_update_gizmo_visible();1430}14311432void Skeleton3DEditor::_update_gizmo_visible() {1433_subgizmo_selection_change();1434if (edit_mode) {1435if (selected_bone == -1) {1436skeleton->set_transform_gizmo_visible(false);1437} else {1438if (skeleton->is_bone_enabled(selected_bone) && !skeleton->is_show_rest_only()) {1439skeleton->set_transform_gizmo_visible(true);1440} else {1441skeleton->set_transform_gizmo_visible(false);1442}1443}1444} else {1445skeleton->set_transform_gizmo_visible(true);1446}1447_draw_gizmo();1448}14491450int Skeleton3DEditor::get_selected_bone() const {1451return selected_bone;1452}14531454Skeleton3DGizmoPlugin::SelectionMaterials Skeleton3DGizmoPlugin::selection_materials;14551456Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() {1457selection_materials.unselected_mat.instantiate();1458selection_materials.unselected_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);1459selection_materials.unselected_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);1460selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);1461selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);1462selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);14631464selection_materials.selected_mat.instantiate();1465Ref<Shader> selected_sh = Ref<Shader>(memnew(Shader));1466selected_sh->set_code(R"(1467// Skeleton 3D gizmo bones shader.14681469shader_type spatial;1470render_mode unshaded, shadows_disabled;1471void vertex() {1472if (!OUTPUT_IS_SRGB) {1473COLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );1474}1475VERTEX = VERTEX;1476POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0);1477POSITION.z = mix(POSITION.z, POSITION.w, 0.998);1478}1479void fragment() {1480ALBEDO = COLOR.rgb;1481ALPHA = COLOR.a;1482}1483)");1484selection_materials.selected_mat->set_shader(selected_sh);1485}14861487Skeleton3DGizmoPlugin::~Skeleton3DGizmoPlugin() {1488selection_materials.unselected_mat.unref();1489selection_materials.selected_mat.unref();1490}14911492bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {1493return Object::cast_to<Skeleton3D>(p_spatial) != nullptr;1494}14951496String Skeleton3DGizmoPlugin::get_gizmo_name() const {1497return "Skeleton3D";1498}14991500int Skeleton3DGizmoPlugin::get_priority() const {1501return -1;1502}15031504int Skeleton3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const {1505Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d());1506ERR_FAIL_NULL_V(skeleton, -1);15071508Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();15091510if (!se || !se->is_edit_mode()) {1511return -1;1512}15131514if (Node3DEditor::get_singleton()->get_tool_mode() != Node3DEditor::TOOL_MODE_SELECT) {1515return -1;1516}15171518// Select bone.1519real_t grab_threshold = 4 * EDSCALE;1520Vector3 ray_from = p_camera->get_global_transform().origin;1521Transform3D gt = skeleton->get_global_transform();1522int closest_idx = -1;1523real_t closest_dist = 1e10;1524const int bone_count = skeleton->get_bone_count();1525for (int i = 0; i < bone_count; i++) {1526Vector3 joint_pos_3d = gt.xform(skeleton->get_bone_global_pose(i).origin);1527Vector2 joint_pos_2d = p_camera->unproject_position(joint_pos_3d);1528real_t dist_3d = ray_from.distance_to(joint_pos_3d);1529real_t dist_2d = p_point.distance_to(joint_pos_2d);1530if (dist_2d < grab_threshold && dist_3d < closest_dist) {1531closest_dist = dist_3d;1532closest_idx = i;1533}1534}15351536if (closest_idx >= 0) {1537se->select_bone(closest_idx);1538return closest_idx;1539}15401541se->select_bone(-1);1542return -1;1543}15441545Transform3D Skeleton3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const {1546Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d());1547ERR_FAIL_NULL_V(skeleton, Transform3D());15481549return skeleton->get_bone_global_pose(p_id);1550}15511552void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) {1553Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d());1554ERR_FAIL_NULL(skeleton);15551556// Prepare for global to local.1557Transform3D original_to_local;1558int parent_idx = skeleton->get_bone_parent(p_id);1559if (parent_idx >= 0) {1560original_to_local = skeleton->get_bone_global_pose(parent_idx);1561}1562Basis to_local = original_to_local.get_basis().inverse();15631564// Prepare transform.1565Transform3D t;15661567// Basis.1568t.basis = to_local * p_transform.get_basis();15691570// Origin.1571Vector3 orig = skeleton->get_bone_pose(p_id).origin;1572Vector3 sub = p_transform.origin - skeleton->get_bone_global_pose(p_id).origin;1573t.origin = orig + to_local.xform(sub);15741575// Apply transform.1576skeleton->set_bone_pose_position(p_id, t.origin);1577skeleton->set_bone_pose_rotation(p_id, t.basis.get_rotation_quaternion());1578skeleton->set_bone_pose_scale(p_id, t.basis.get_scale());1579}15801581void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {1582Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d());1583ERR_FAIL_NULL(skeleton);15841585Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();1586Node3DEditor *ne = Node3DEditor::get_singleton();15871588EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();1589ur->create_action(TTR("Set Bone Transform"));1590if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || ne->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) {1591for (int i = 0; i < p_ids.size(); i++) {1592ur->add_do_method(skeleton, "set_bone_pose_position", p_ids[i], skeleton->get_bone_pose_position(p_ids[i]));1593ur->add_undo_method(skeleton, "set_bone_pose_position", p_ids[i], se->get_bone_original_position());1594}1595}1596if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || ne->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) {1597for (int i = 0; i < p_ids.size(); i++) {1598ur->add_do_method(skeleton, "set_bone_pose_rotation", p_ids[i], skeleton->get_bone_pose_rotation(p_ids[i]));1599ur->add_undo_method(skeleton, "set_bone_pose_rotation", p_ids[i], se->get_bone_original_rotation());1600}1601}1602if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE) {1603for (int i = 0; i < p_ids.size(); i++) {1604// If the axis is swapped by scaling, the rotation can be changed.1605ur->add_do_method(skeleton, "set_bone_pose_rotation", p_ids[i], skeleton->get_bone_pose_rotation(p_ids[i]));1606ur->add_undo_method(skeleton, "set_bone_pose_rotation", p_ids[i], se->get_bone_original_rotation());1607ur->add_do_method(skeleton, "set_bone_pose_scale", p_ids[i], skeleton->get_bone_pose_scale(p_ids[i]));1608ur->add_undo_method(skeleton, "set_bone_pose_scale", p_ids[i], se->get_bone_original_scale());1609}1610}16111612ur->add_do_method(se, "update_joint_tree");1613ur->add_undo_method(se, "update_joint_tree");16141615ur->commit_action();1616}16171618void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {1619Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d());1620p_gizmo->clear();16211622if (!skeleton->get_bone_count()) {1623return;1624}16251626int selected = -1;1627Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();1628if (se) {1629selected = se->get_selected_bone();1630}16311632Ref<ArrayMesh> m = get_bones_mesh(skeleton, selected, p_gizmo->is_selected());1633p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(skeleton->create_skin_from_rest_transforms()));1634}16351636Ref<ArrayMesh> Skeleton3DGizmoPlugin::get_bones_mesh(Skeleton3D *p_skeleton, int p_selected, bool p_is_selected) {1637Color bone_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/skeleton");1638Color selected_bone_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/selected_bone");1639real_t bone_axis_length = EDITOR_GET("editors/3d_gizmos/gizmo_settings/bone_axis_length");1640int bone_shape = EDITOR_GET("editors/3d_gizmos/gizmo_settings/bone_shape");16411642LocalVector<Color> axis_colors;1643axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)));1644axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)));1645axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_z_color"), EditorStringName(Editor)));16461647Ref<SurfaceTool> surface_tool(memnew(SurfaceTool));1648surface_tool->begin(Mesh::PRIMITIVE_LINES);16491650if (p_is_selected) {1651surface_tool->set_material(selection_materials.selected_mat);1652} else {1653selection_materials.unselected_mat->set_albedo(bone_color);1654surface_tool->set_material(selection_materials.unselected_mat);1655}16561657LocalVector<int> bones;1658LocalVector<float> weights;1659bones.resize(4);1660weights.resize(4);1661for (int i = 0; i < 4; i++) {1662bones[i] = 0;1663weights[i] = 0;1664}1665weights[0] = 1;16661667int current_bone_index = 0;1668Vector<int> bones_to_process = p_skeleton->get_parentless_bones();16691670while (bones_to_process.size() > current_bone_index) {1671int current_bone_idx = bones_to_process[current_bone_index];1672current_bone_index++;16731674Color current_bone_color = (current_bone_idx == p_selected) ? selected_bone_color : bone_color;16751676Vector<int> child_bones_vector;1677child_bones_vector = p_skeleton->get_bone_children(current_bone_idx);1678int child_bones_size = child_bones_vector.size();16791680for (int i = 0; i < child_bones_size; i++) {1681// Something wrong.1682if (child_bones_vector[i] < 0) {1683continue;1684}16851686int child_bone_idx = child_bones_vector[i];16871688Vector3 v0 = p_skeleton->get_bone_global_rest(current_bone_idx).origin;1689Vector3 v1 = p_skeleton->get_bone_global_rest(child_bone_idx).origin;1690Vector3 d = (v1 - v0).normalized();1691real_t dist = v0.distance_to(v1);16921693// Find closest axis.1694int closest = -1;1695real_t closest_d = 0.0;1696for (int j = 0; j < 3; j++) {1697real_t dp = Math::abs(p_skeleton->get_bone_global_rest(current_bone_idx).basis[j].normalized().dot(d));1698if (j == 0 || dp > closest_d) {1699closest = j;1700}1701}17021703// Draw bone.1704switch (bone_shape) {1705case 0: { // Wire shape.1706surface_tool->set_color(current_bone_color);1707bones[0] = current_bone_idx;1708surface_tool->set_bones(bones);1709surface_tool->set_weights(weights);1710surface_tool->add_vertex(v0);1711bones[0] = child_bone_idx;1712surface_tool->set_bones(bones);1713surface_tool->set_weights(weights);1714surface_tool->add_vertex(v1);1715} break;17161717case 1: { // Octahedron shape.1718Vector3 first;1719Vector3 points[6];1720int point_idx = 0;1721for (int j = 0; j < 3; j++) {1722Vector3 axis;1723if (first == Vector3()) {1724axis = d.cross(d.cross(p_skeleton->get_bone_global_rest(current_bone_idx).basis[j])).normalized();1725first = axis;1726} else {1727axis = d.cross(first).normalized();1728}17291730surface_tool->set_color(current_bone_color);1731for (int k = 0; k < 2; k++) {1732if (k == 1) {1733axis = -axis;1734}1735Vector3 point = v0 + d * dist * 0.2;1736point += axis * dist * 0.1;17371738bones[0] = current_bone_idx;1739surface_tool->set_bones(bones);1740surface_tool->set_weights(weights);1741surface_tool->add_vertex(v0);1742surface_tool->set_bones(bones);1743surface_tool->set_weights(weights);1744surface_tool->add_vertex(point);17451746surface_tool->set_bones(bones);1747surface_tool->set_weights(weights);1748surface_tool->add_vertex(point);1749bones[0] = child_bone_idx;1750surface_tool->set_bones(bones);1751surface_tool->set_weights(weights);1752surface_tool->add_vertex(v1);1753points[point_idx++] = point;1754}1755}1756surface_tool->set_color(current_bone_color);1757SWAP(points[1], points[2]);1758bones[0] = current_bone_idx;1759for (int j = 0; j < 6; j++) {1760surface_tool->set_bones(bones);1761surface_tool->set_weights(weights);1762surface_tool->add_vertex(points[j]);1763surface_tool->set_bones(bones);1764surface_tool->set_weights(weights);1765surface_tool->add_vertex(points[(j + 1) % 6]);1766}1767} break;1768}17691770// Axis as root of the bone.1771for (int j = 0; j < 3; j++) {1772bones[0] = current_bone_idx;1773surface_tool->set_color(axis_colors[j]);1774surface_tool->set_bones(bones);1775surface_tool->set_weights(weights);1776surface_tool->add_vertex(v0);1777surface_tool->set_bones(bones);1778surface_tool->set_weights(weights);1779surface_tool->add_vertex(v0 + (p_skeleton->get_bone_global_rest(current_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length);17801781if (j == closest) {1782continue;1783}1784}17851786// Axis at the end of the bone children.1787if (i == child_bones_size - 1) {1788for (int j = 0; j < 3; j++) {1789bones[0] = child_bone_idx;1790surface_tool->set_color(axis_colors[j]);1791surface_tool->set_bones(bones);1792surface_tool->set_weights(weights);1793surface_tool->add_vertex(v1);1794surface_tool->set_bones(bones);1795surface_tool->set_weights(weights);1796surface_tool->add_vertex(v1 + (p_skeleton->get_bone_global_rest(child_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length);17971798if (j == closest) {1799continue;1800}1801}1802}18031804// Add the bone's children to the list of bones to be processed.1805bones_to_process.push_back(child_bones_vector[i]);1806}1807}18081809return surface_tool->commit();1810}181118121813