1
0
mirror of https://github.com/godotengine/godot.git synced 2026-01-05 19:31:35 +00:00

Merge pull request #103418 from aaronfranke/adv-imp-attach-script

Allow attaching scripts to nodes in the Advanced Import Settings dialog
This commit is contained in:
Rémi Verschelde
2025-06-11 11:19:59 +02:00
4 changed files with 62 additions and 22 deletions

View File

@@ -1368,6 +1368,51 @@ Node *ResourceImporterScene::_post_fix_animations(Node *p_node, Node *p_root, co
return p_node;
}
Node *ResourceImporterScene::_replace_node_with_type_and_script(Node *p_node, String p_node_type, Ref<Script> p_script) {
p_node_type = p_node_type.get_slicec(' ', 0); // Full root_type is "ClassName (filename.gd)" for a script global class.
if (p_script.is_valid()) {
// Ensure the node type supports the script, or pick one that does.
String script_base_type = p_script->get_instance_base_type();
if (ClassDB::is_parent_class(script_base_type, "Node")) {
if (p_node_type.is_empty() || !ClassDB::is_parent_class(p_node_type, script_base_type)) {
p_node_type = script_base_type;
}
}
}
if (!p_node_type.is_empty() && ScriptServer::is_global_class(p_node_type)) {
// If the user specified a script class, we need to get the base node type.
if (p_script.is_null()) {
p_script = ResourceLoader::load(ScriptServer::get_global_class_path(p_node_type));
}
p_node_type = ScriptServer::get_global_class_base(p_node_type);
}
if (!p_node_type.is_empty() && p_node->get_class_name() != p_node_type) {
// If the user specified a Godot node type that does not match
// what the scene import gave us, replace the root node.
Node *new_base_node = Object::cast_to<Node>(ClassDB::instantiate(p_node_type));
if (new_base_node) {
List<PropertyInfo> old_properties;
p_node->get_property_list(&old_properties);
for (const PropertyInfo &prop : old_properties) {
if (!(prop.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
new_base_node->set(prop.name, p_node->get(prop.name));
}
new_base_node->set_name(p_node->get_name());
_copy_meta(p_node, new_base_node);
p_node->replace_by(new_base_node);
p_node->set_owner(nullptr);
memdelete(p_node);
p_node = new_base_node;
}
}
if (p_script.is_valid()) {
p_node->set_script(Variant(p_script));
}
return p_node;
}
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) {
// children first
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<
}
}
String node_type = node_settings.get("node/node_type", "");
Ref<Script> node_script = node_settings.get("node/script", Ref<Script>());
p_node = _replace_node_with_type_and_script(p_node, node_type, node_script);
return p_node;
}
@@ -2057,9 +2106,12 @@ void ResourceImporterScene::_compress_animations(AnimationPlayer *anim, int p_pa
void ResourceImporterScene::get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const {
switch (p_category) {
case INTERNAL_IMPORT_CATEGORY_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "node/node_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
} break;
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));
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::BOOL, "generate/physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
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
}
} break;
case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));
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::BOOL, "optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
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
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
} break;
case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));
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::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));
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
void ResourceImporterScene::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_name"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "nodes/root_script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));
List<String> script_extensions;
ResourceLoader::get_recognized_extensions_for_type("Script", &script_extensions);
@@ -3147,28 +3202,8 @@ Error ResourceImporterScene::import(ResourceUID::ID p_source_id, const String &p
_post_fix_animations(scene, scene, node_data, animation_data, fps, remove_immutable_tracks);
String root_type = p_options["nodes/root_type"];
if (!root_type.is_empty()) {
root_type = root_type.split(" ")[0]; // Full root_type is "ClassName (filename.gd)" for a script global class.
Ref<Script> root_script = nullptr;
if (ScriptServer::is_global_class(root_type)) {
root_script = ResourceLoader::load(ScriptServer::get_global_class_path(root_type));
root_type = ScriptServer::get_global_class_base(root_type);
}
if (scene->get_class_name() != root_type) {
// If the user specified a Godot node type that does not match
// what the scene import gave us, replace the root node.
Node *base_node = Object::cast_to<Node>(ClassDB::instantiate(root_type));
if (base_node) {
scene->replace_by(base_node);
scene->set_owner(nullptr);
memdelete(scene);
scene = base_node;
}
}
if (root_script.is_valid()) {
scene->set_script(Variant(root_script));
}
}
Ref<Script> root_script = p_options["nodes/root_script"];
scene = _replace_node_with_type_and_script(scene, root_type, root_script);
String root_name = p_options["nodes/root_name"];
if (!root_name.is_empty() && root_name != "Scene Root") {

View File

@@ -216,6 +216,7 @@ class ResourceImporterScene : public ResourceImporter {
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);
void _add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes);
void _copy_meta(Object *p_src_object, Object *p_dst_object);
Node *_replace_node_with_type_and_script(Node *p_node, String p_node_type, Ref<Script> p_script);
enum AnimationImportTracks {
ANIMATION_IMPORT_TRACKS_IF_PRESENT,