diff --git a/core/ustring.cpp b/core/ustring.cpp index c3676e9c68c..6190a375e0c 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -4111,6 +4111,18 @@ String String::property_name_encode() const { return *this; } +// Changes made to the set of invalid characters must also be reflected in the String documentation. +const String String::invalid_node_name_characters = ". : @ / \""; + +String String::validate_node_name() const { + Vector chars = String::invalid_node_name_characters.split(" "); + String name = this->replace(chars[0], ""); + for (int i = 1; i < chars.size(); i++) { + name = name.replace(chars[i], ""); + } + return name; +} + String String::get_basename() const { int pos = find_last("."); diff --git a/core/ustring.h b/core/ustring.h index 94b62f09f73..ea7d898c428 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -340,6 +340,10 @@ public: String property_name_encode() const; + // node functions + static const String invalid_node_name_characters; + String validate_node_name() const; + bool is_valid_identifier() const; bool is_valid_integer() const; bool is_valid_float() const; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index ad20b970d83..4cdaf13565a 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -300,6 +300,7 @@ struct _VariantCall { VCALL_LOCALMEM0R(String, json_escape); VCALL_LOCALMEM0R(String, percent_encode); VCALL_LOCALMEM0R(String, percent_decode); + VCALL_LOCALMEM0R(String, validate_node_name); VCALL_LOCALMEM0R(String, is_valid_identifier); VCALL_LOCALMEM0R(String, is_valid_integer); VCALL_LOCALMEM0R(String, is_valid_float); @@ -1646,6 +1647,7 @@ void register_variant_methods() { ADDFUNC0R(STRING, STRING, String, json_escape, varray()); ADDFUNC0R(STRING, STRING, String, percent_encode, varray()); ADDFUNC0R(STRING, STRING, String, percent_decode, varray()); + ADDFUNC0R(STRING, STRING, String, validate_node_name, varray()); ADDFUNC0R(STRING, BOOL, String, is_valid_identifier, varray()); ADDFUNC0R(STRING, BOOL, String, is_valid_integer, varray()); ADDFUNC0R(STRING, BOOL, String, is_valid_float, varray()); diff --git a/doc/classes/String.xml b/doc/classes/String.xml index f54cac71bf5..941d0e697be 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -959,6 +959,13 @@ Removes a given string from the end if it ends with it or leaves the string unchanged. + + + + + Removes any characters from the string that are prohibited in [Node] names ([code].[/code] [code]:[/code] [code]@[/code] [code]/[/code] [code]"[/code]). + + diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 5d33cf4a6fa..50bf635a2f9 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -40,6 +40,7 @@ #include "scene/3d/camera.h" #include "scene/3d/mesh_instance.h" #include "scene/animation/animation_player.h" +#include "scene/main/node.h" #include "scene/resources/surface_tool.h" uint32_t EditorSceneImporterGLTF::get_import_flags() const { @@ -155,15 +156,9 @@ static Transform _arr_to_xform(const Array &p_array) { return xform; } -String EditorSceneImporterGLTF::_sanitize_scene_name(const String &name) { - RegEx regex("([^a-zA-Z0-9_ -]+)"); - String p_name = regex.sub(name, "", true); - return p_name; -} - String EditorSceneImporterGLTF::_gen_unique_name(GLTFState &state, const String &p_name) { - const String s_name = _sanitize_scene_name(p_name); + const String s_name = p_name.validate_node_name(); String name; int index = 1; @@ -171,7 +166,7 @@ String EditorSceneImporterGLTF::_gen_unique_name(GLTFState &state, const String name = s_name; if (index > 1) { - name += " " + itos(index); + name += itos(index); } if (!state.unique_names.has(name)) { break; @@ -184,6 +179,40 @@ String EditorSceneImporterGLTF::_gen_unique_name(GLTFState &state, const String return name; } +String EditorSceneImporterGLTF::_sanitize_animation_name(const String &p_name) { + // Animations disallow the normal node invalid characters as well as "," and "[" + // (See animation/animation_player.cpp::add_animation) + + // TODO: Consider adding invalid_characters or a _validate_animation_name to animation_player to mirror Node. + String name = p_name.validate_node_name(); + name = name.replace(",", ""); + name = name.replace("[", ""); + return name; +} + +String EditorSceneImporterGLTF::_gen_unique_animation_name(GLTFState &state, const String &p_name) { + + const String s_name = _sanitize_animation_name(p_name); + + String name; + int index = 1; + while (true) { + name = s_name; + + if (index > 1) { + name += itos(index); + } + if (!state.unique_animation_names.has(name)) { + break; + } + index++; + } + + state.unique_animation_names.insert(name); + + return name; +} + String EditorSceneImporterGLTF::_sanitize_bone_name(const String &name) { String p_name = name.camelcase_to_underscore(true); @@ -2473,7 +2502,7 @@ Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) { animation.loop = true; } - animation.name = _sanitize_scene_name(name); + animation.name = _gen_unique_animation_name(state, name); } for (int j = 0; j < channels.size(); j++) { diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h index af195454035..96e041aea8f 100644 --- a/editor/import/editor_scene_importer_gltf.h +++ b/editor/import/editor_scene_importer_gltf.h @@ -342,6 +342,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Vector lights; Set unique_names; + Set unique_animation_names; Vector skeletons; Vector animations; @@ -358,9 +359,11 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { } }; - String _sanitize_scene_name(const String &name); String _gen_unique_name(GLTFState &state, const String &p_name); + String _sanitize_animation_name(const String &p_name); + String _gen_unique_animation_name(GLTFState &state, const String &p_name); + String _sanitize_bone_name(const String &name); String _gen_unique_bone_name(GLTFState &state, const GLTFSkeletonIndex skel_i, const String &p_name); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 47f4d034f28..73518e7ae13 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -785,10 +785,10 @@ void SceneTreeEditor::_renamed() { return; } - String new_name = which->get_text(0); - if (!Node::_validate_node_name(new_name)) { - - error->set_text(TTR("Invalid node name, the following characters are not allowed:") + "\n" + Node::invalid_character); + String raw_new_name = which->get_text(0); + String new_name = raw_new_name.validate_node_name(); + if (new_name != raw_new_name) { + error->set_text(TTR("Invalid node name, the following characters are not allowed:") + "\n" + String::invalid_node_name_characters); error->popup_centered_minsize(); if (new_name.empty()) { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index f503c6d5c7b..98c6bca318e 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -927,23 +927,9 @@ void Node::_set_name_nocheck(const StringName &p_name) { data.name = p_name; } -String Node::invalid_character = ". : @ / \""; - -bool Node::_validate_node_name(String &p_name) { - String name = p_name; - Vector chars = Node::invalid_character.split(" "); - for (int i = 0; i < chars.size(); i++) { - name = name.replace(chars[i], ""); - } - bool is_valid = name == p_name; - p_name = name; - return is_valid; -} - void Node::set_name(const String &p_name) { - String name = p_name; - _validate_node_name(name); + String name = p_name.validate_node_name(); ERR_FAIL_COND(name == ""); data.name = name; diff --git a/scene/main/node.h b/scene/main/node.h index d63e72e62a4..27b128c387d 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -188,12 +188,6 @@ private: void _set_tree(SceneTree *p_tree); -#ifdef TOOLS_ENABLED - friend class SceneTreeEditor; -#endif - static String invalid_character; - static bool _validate_node_name(String &p_name); - protected: void _block() { data.blocked++; } void _unblock() { data.blocked--; }