Skip to content

Commit 033e55f

Browse files
committed
Merge pull request #103418 from aaronfranke/adv-imp-attach-script
Allow attaching scripts to nodes in the Advanced Import Settings dialog
2 parents 1bbfe63 + a3daba2 commit 033e55f

File tree

4 files changed

+62
-22
lines changed

4 files changed

+62
-22
lines changed

doc/classes/ResourceImporterScene.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@
8080
<member name="nodes/root_scale" type="float" setter="" getter="" default="1.0">
8181
The uniform scale to use for the scene root. The default value of [code]1.0[/code] will not perform any rescaling. See [member nodes/apply_root_scale] for details of how this scale is applied.
8282
</member>
83+
<member name="nodes/root_script" type="Script" setter="" getter="" default="null">
84+
If set to a valid script, attaches the script to the root node of the imported scene. If the type of the root node is not compatible with the script, the root node will be replaced with a type that is compatible with the script. This setting can also be used on other non-mesh nodes in the scene to attach scripts to them.
85+
</member>
8386
<member name="nodes/root_type" type="String" setter="" getter="" default="&quot;&quot;">
8487
Override for the root node type. If empty, the root node will use what the scene specifies, or [Node3D] if the scene does not specify a root type. Using a node type that inherits from [Node3D] is recommended. Otherwise, you'll lose the ability to position the node directly in the 3D editor.
8588
</member>

editor/import/3d/resource_importer_scene.cpp

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,6 +1368,51 @@ Node *ResourceImporterScene::_post_fix_animations(Node *p_node, Node *p_root, co
13681368
return p_node;
13691369
}
13701370

1371+
Node *ResourceImporterScene::_replace_node_with_type_and_script(Node *p_node, String p_node_type, Ref<Script> p_script) {
1372+
p_node_type = p_node_type.get_slicec(' ', 0); // Full root_type is "ClassName (filename.gd)" for a script global class.
1373+
if (p_script.is_valid()) {
1374+
// Ensure the node type supports the script, or pick one that does.
1375+
String script_base_type = p_script->get_instance_base_type();
1376+
if (ClassDB::is_parent_class(script_base_type, "Node")) {
1377+
if (p_node_type.is_empty() || !ClassDB::is_parent_class(p_node_type, script_base_type)) {
1378+
p_node_type = script_base_type;
1379+
}
1380+
}
1381+
}
1382+
if (!p_node_type.is_empty() && ScriptServer::is_global_class(p_node_type)) {
1383+
// If the user specified a script class, we need to get the base node type.
1384+
if (p_script.is_null()) {
1385+
p_script = ResourceLoader::load(ScriptServer::get_global_class_path(p_node_type));
1386+
}
1387+
p_node_type = ScriptServer::get_global_class_base(p_node_type);
1388+
}
1389+
if (!p_node_type.is_empty() && p_node->get_class_name() != p_node_type) {
1390+
// If the user specified a Godot node type that does not match
1391+
// what the scene import gave us, replace the root node.
1392+
Node *new_base_node = Object::cast_to<Node>(ClassDB::instantiate(p_node_type));
1393+
if (new_base_node) {
1394+
List<PropertyInfo> old_properties;
1395+
p_node->get_property_list(&old_properties);
1396+
for (const PropertyInfo &prop : old_properties) {
1397+
if (!(prop.usage & PROPERTY_USAGE_STORAGE)) {
1398+
continue;
1399+
}
1400+
new_base_node->set(prop.name, p_node->get(prop.name));
1401+
}
1402+
new_base_node->set_name(p_node->get_name());
1403+
_copy_meta(p_node, new_base_node);
1404+
p_node->replace_by(new_base_node);
1405+
p_node->set_owner(nullptr);
1406+
memdelete(p_node);
1407+
p_node = new_base_node;
1408+
}
1409+
}
1410+
if (p_script.is_valid()) {
1411+
p_node->set_script(Variant(p_script));
1412+
}
1413+
return p_node;
1414+
}
1415+
13711416
Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale, const String &p_source_file, const HashMap<StringName, Variant> &p_options) {
13721417
// children first
13731418
for (int i = 0; i < p_node->get_child_count(); i++) {
@@ -1842,6 +1887,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
18421887
}
18431888
}
18441889

1890+
String node_type = node_settings.get("node/node_type", "");
1891+
Ref<Script> node_script = node_settings.get("node/script", Ref<Script>());
1892+
p_node = _replace_node_with_type_and_script(p_node, node_type, node_script);
1893+
18451894
return p_node;
18461895
}
18471896

@@ -2057,9 +2106,12 @@ void ResourceImporterScene::_compress_animations(AnimationPlayer *anim, int p_pa
20572106
void ResourceImporterScene::get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const {
20582107
switch (p_category) {
20592108
case INTERNAL_IMPORT_CATEGORY_NODE: {
2109+
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "node/node_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));
2110+
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));
20602111
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
20612112
} break;
20622113
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
2114+
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));
20632115
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
20642116
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate/physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
20652117
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));
@@ -2140,6 +2192,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
21402192
}
21412193
} break;
21422194
case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
2195+
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));
21432196
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
21442197
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
21452198
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_velocity_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));
@@ -2152,6 +2205,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
21522205
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
21532206
} break;
21542207
case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {
2208+
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));
21552209
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
21562210
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "rest_pose/load_pose", PROPERTY_HINT_ENUM, "Default Pose,Use AnimationPlayer,Load External Animation", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
21572211
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "rest_pose/external_animation_library", PROPERTY_HINT_RESOURCE_TYPE, "Animation,AnimationLibrary", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Variant()));
@@ -2436,6 +2490,7 @@ bool ResourceImporterScene::get_internal_option_update_view_required(InternalImp
24362490
void ResourceImporterScene::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
24372491
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));
24382492
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_name"), ""));
2493+
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "nodes/root_script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));
24392494

24402495
List<String> script_extensions;
24412496
ResourceLoader::get_recognized_extensions_for_type("Script", &script_extensions);
@@ -3147,28 +3202,8 @@ Error ResourceImporterScene::import(ResourceUID::ID p_source_id, const String &p
31473202
_post_fix_animations(scene, scene, node_data, animation_data, fps, remove_immutable_tracks);
31483203

31493204
String root_type = p_options["nodes/root_type"];
3150-
if (!root_type.is_empty()) {
3151-
root_type = root_type.split(" ")[0]; // Full root_type is "ClassName (filename.gd)" for a script global class.
3152-
Ref<Script> root_script = nullptr;
3153-
if (ScriptServer::is_global_class(root_type)) {
3154-
root_script = ResourceLoader::load(ScriptServer::get_global_class_path(root_type));
3155-
root_type = ScriptServer::get_global_class_base(root_type);
3156-
}
3157-
if (scene->get_class_name() != root_type) {
3158-
// If the user specified a Godot node type that does not match
3159-
// what the scene import gave us, replace the root node.
3160-
Node *base_node = Object::cast_to<Node>(ClassDB::instantiate(root_type));
3161-
if (base_node) {
3162-
scene->replace_by(base_node);
3163-
scene->set_owner(nullptr);
3164-
memdelete(scene);
3165-
scene = base_node;
3166-
}
3167-
}
3168-
if (root_script.is_valid()) {
3169-
scene->set_script(Variant(root_script));
3170-
}
3171-
}
3205+
Ref<Script> root_script = p_options["nodes/root_script"];
3206+
scene = _replace_node_with_type_and_script(scene, root_type, root_script);
31723207

31733208
String root_name = p_options["nodes/root_name"];
31743209
if (!root_name.is_empty() && root_name != "Scene Root") {

editor/import/3d/resource_importer_scene.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ class ResourceImporterScene : public ResourceImporter {
216216
Node *_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches);
217217
void _add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes);
218218
void _copy_meta(Object *p_src_object, Object *p_dst_object);
219+
Node *_replace_node_with_type_and_script(Node *p_node, String p_node_type, Ref<Script> p_script);
219220

220221
enum AnimationImportTracks {
221222
ANIMATION_IMPORT_TRACKS_IF_PRESENT,

modules/gltf/tests/test_gltf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ static Node *gltf_import(const String &p_file) {
7878
HashMap<StringName, Variant> options(21);
7979
options["nodes/root_type"] = "";
8080
options["nodes/root_name"] = "";
81+
options["nodes/root_script"] = Variant();
8182
options["nodes/apply_root_scale"] = true;
8283
options["nodes/root_scale"] = 1.0;
8384
options["meshes/ensure_tangents"] = true;

0 commit comments

Comments
 (0)