diff --git a/doc/classes/ResourceImporterScene.xml b/doc/classes/ResourceImporterScene.xml index f14eb6d1e8f..5ae47a2dfbe 100644 --- a/doc/classes/ResourceImporterScene.xml +++ b/doc/classes/ResourceImporterScene.xml @@ -33,6 +33,21 @@ Path to an import script, which can run code after the import process has completed for custom processing. See [url=$DOCS_URL/tutorials/assets_pipeline/importing_3d_scenes/import_configuration.html#using-import-scripts-for-automation]Using import scripts for automation[/url] for more information. + + Material extraction mode. + - [code]0 (Keep Internal)[/code], materials are not extracted. + - [code]1 (Extract Once)[/code], materials are extracted once and reused on subsequent import. + - [code]2 (Extract and Overwrite)[/code], materials are extracted and overwritten on every import. + + + Extracted material file format. + - [code]0 (Text)[/code], text file format ([code]*.tres[/code]). + - [code]1 (Binary)[/code], binary file format ([code]*.res[/code]). + - [code]2 (Material)[/code], binary file format ([code]*.material[/code]). + + + Path extracted materials are saved to. If empty, source scene path is used. + If [code]true[/code], enables the generation of shadow meshes on import. This optimizes shadow rendering without reducing quality by welding vertices together when possible. This in turn reduces the memory bandwidth required to render shadows. Shadow mesh generation currently doesn't support using a lower detail level than the source mesh (but shadow rendering will make use of LODs when relevant). diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index 45bcd1930e7..56b67db3028 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -248,6 +248,8 @@ void EditorScenePostImportPlugin::_bind_methods() { ///////////////////////////////////////////////////////// +const String ResourceImporterScene::material_extension[3] = { ".tres", ".res", ".material" }; + String ResourceImporterScene::get_importer_name() const { // For compatibility with 4.2 and earlier we need to keep the "scene" and "animation_library" names. // However this is arbitrary so for new import types we can use any string. @@ -1366,15 +1368,28 @@ Node *ResourceImporterScene::_post_fix_animations(Node *p_node, Node *p_root, co return p_node; } -Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap, Vector>> &collision_map, Pair &r_occluder_arrays, HashSet> &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) { +Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap, Vector>> &collision_map, Pair &r_occluder_arrays, HashSet> &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 &p_options) { // children first for (int i = 0; i < p_node->get_child_count(); i++) { - Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_occluder_arrays, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps, p_applied_root_scale); + Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_occluder_arrays, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps, p_applied_root_scale, p_source_file, p_options); if (!r) { i--; //was erased } } + int extract_mat = 0; + if (p_options.has("materials/extract")) { + extract_mat = p_options["materials/extract"]; + } + + String spath = p_source_file.get_base_dir(); + if (p_options.has("materials/extract_path")) { + String extpath = p_options["materials/extract_path"]; + if (!extpath.is_empty()) { + spath = extpath; + } + } + bool isroot = p_node == p_root; String import_id = p_node->get_meta("import_id", "PATH:" + p_root->get_path_to(p_node)); @@ -1520,7 +1535,6 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< Ref mat = m->get_surface_material(i); if (mat.is_valid()) { String mat_id = mat->get_meta("import_id", mat->get_name()); - if (!mat_id.is_empty() && p_material_data.has(mat_id)) { Dictionary matdata = p_material_data[mat_id]; { @@ -1537,7 +1551,27 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< for (int j = 0; j < post_importer_plugins.size(); j++) { post_importer_plugins.write[j]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL, p_root, p_node, mat, matdata); } + } + if (!mat_id.is_empty() && extract_mat != 0) { + String ext = material_extension[p_options.has("materials/extract_format") ? (int)p_options["materials/extract_format"] : 0]; + String path = spath.path_join(mat_id.validate_filename() + ext); + String uid_path = ResourceUID::path_to_uid(path); + Dictionary matdata = p_material_data[mat_id]; + matdata["use_external/enabled"] = true; + matdata["use_external/path"] = uid_path; + matdata["use_external/fallback_path"] = path; + if (!FileAccess::exists(path) || extract_mat == 2 /*overwrite*/) { + ResourceSaver::save(mat, path); + } + + Ref external_mat = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE); + if (external_mat.is_valid()) { + m->set_surface_material(i, external_mat); + } + } + if (!mat_id.is_empty() && p_material_data.has(mat_id)) { + Dictionary matdata = p_material_data[mat_id]; if (matdata.has("use_external/enabled") && bool(matdata["use_external/enabled"]) && matdata.has("use_external/path")) { String path = matdata["use_external/path"]; Ref external_mat = ResourceLoader::load(path); @@ -2434,6 +2468,9 @@ void ResourceImporterScene::get_import_options(const String &p_path, Listpush_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import_rest_as_RESET"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), "")); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/extract", PROPERTY_HINT_ENUM, "Keep Internal,Extract Once,Extract and Overwrite"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/extract_format", PROPERTY_HINT_ENUM, "Text (*.tres),Binary (*.res),Material (*.material)"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "materials/extract_path", PROPERTY_HINT_DIR, ""), "")); r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Dictionary())); @@ -3106,7 +3143,7 @@ Error ResourceImporterScene::import(ResourceUID::ID p_source_id, const String &p } bool remove_immutable_tracks = p_options.has("animation/remove_immutable_tracks") ? (bool)p_options["animation/remove_immutable_tracks"] : true; _pre_fix_animations(scene, scene, node_data, animation_data, fps); - _post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps, apply_root ? root_scale : 1.0); + _post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps, apply_root ? root_scale : 1.0, p_source_file, p_options); _post_fix_animations(scene, scene, node_data, animation_data, fps, remove_immutable_tracks); String root_type = p_options["nodes/root_type"]; diff --git a/editor/import/3d/resource_importer_scene.h b/editor/import/3d/resource_importer_scene.h index 775c69c816e..8bb3fd4045b 100644 --- a/editor/import/3d/resource_importer_scene.h +++ b/editor/import/3d/resource_importer_scene.h @@ -235,6 +235,7 @@ class ResourceImporterScene : public ResourceImporter { String _scene_import_type = "PackedScene"; public: + static const String material_extension[3]; static ResourceImporterScene *get_scene_singleton() { return scene_singleton; } static ResourceImporterScene *get_animation_singleton() { return animation_singleton; } @@ -285,7 +286,7 @@ public: void _pre_fix_global(Node *p_scene, const HashMap &p_options) const; Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap, Vector>> &r_collision_map, Pair *r_occluder_arrays, List> &r_node_renames, const HashMap &p_options); Node *_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps); - Node *_post_fix_node(Node *p_node, Node *p_root, HashMap, Vector>> &collision_map, Pair &r_occluder_arrays, HashSet> &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); + Node *_post_fix_node(Node *p_node, Node *p_root, HashMap, Vector>> &collision_map, Pair &r_occluder_arrays, HashSet> &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 &p_options); Node *_post_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps, bool p_remove_immutable_tracks); Ref _save_animation_to_file(Ref anim, bool p_save_to_file, const String &p_save_to_path, bool p_keep_custom_tracks); diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp index 6125450861f..45e6853c65d 100644 --- a/editor/import/3d/scene_import_settings.cpp +++ b/editor/import/3d/scene_import_settings.cpp @@ -201,6 +201,23 @@ class SceneImportSettingsData : public Object { } }; +bool SceneImportSettingsDialog::_get_current(const StringName &p_name, Variant &r_ret) const { + if (scene_import_settings_data->_get(p_name, r_ret)) { + return true; + } + if (defaults.has(p_name)) { + r_ret = defaults[p_name]; + return true; + } + return false; +} + +void SceneImportSettingsDialog::_set_default(const StringName &p_name, const Variant &p_value) { + defaults[p_name] = p_value; + scene_import_settings_data->defaults[p_name] = p_value; + scene_import_settings_data->_set(p_name, p_value); +} + void SceneImportSettingsDialog::_fill_material(Tree *p_tree, const Ref &p_material, TreeItem *p_parent) { String import_id; bool has_import_id = false; @@ -233,6 +250,24 @@ void SceneImportSettingsDialog::_fill_material(Tree *p_tree, const Ref MaterialData &material_data = material_map[import_id]; ERR_FAIL_COND(p_material != material_data.material); + Variant value; + if (_get_current("materials/extract", value) && (int)value != 0) { + String spath = base_path.get_base_dir(); + if (_get_current("materials/extract_path", value)) { + String extpath = value; + if (!extpath.is_empty()) { + spath = extpath; + } + } + + String ext = ResourceImporterScene::material_extension[_get_current("materials/extract_format", value) ? (int)value : 0]; + String path = spath.path_join(import_id.validate_filename() + ext); + String uid_path = ResourceUID::path_to_uid(path); + material_data.settings["use_external/enabled"] = true; + material_data.settings["use_external/path"] = uid_path; + material_data.settings["use_external/fallback_path"] = path; + } + Ref icon = get_editor_theme_icon(SNAME("StandardMaterial3D")); TreeItem *item = p_tree->create_item(p_parent); @@ -1004,6 +1039,30 @@ void SceneImportSettingsDialog::_inspector_property_edited(const String &p_name) animation_loop_mode = Animation::LoopMode::LOOP_NONE; } } + if ((p_name == "use_external/enabled") || (p_name == "use_external/path") || (p_name == "use_external/fallback_path")) { + MaterialData &material_data = material_map[selected_id]; + String spath = base_path.get_base_dir(); + Variant value; + if (_get_current("materials/extract_path", value)) { + String extpath = value; + if (!extpath.is_empty()) { + spath = extpath; + } + } + String opath = material_data.settings.has("use_external/path") ? (String)material_data.settings["use_external/path"] : String(); + if (opath.begins_with("uid://")) { + opath = ResourceUID::uid_to_path(opath); + } + String ext = ResourceImporterScene::material_extension[_get_current("materials/extract_format", value) ? (int)value : 0]; + String npath = spath.path_join(selected_id.validate_filename() + ext); + + if (!material_data.settings.has("use_external/enabled") || (bool)material_data.settings["use_external/enabled"] == false || opath != npath) { + if (_get_current("materials/extract", value) && (int)value != 0) { + print_line("Material settings changed, automatic material extraction disabled."); + } + _set_default("materials/extract", 0); + } + } } void SceneImportSettingsDialog::_reset_bone_transforms() { diff --git a/editor/import/3d/scene_import_settings.h b/editor/import/3d/scene_import_settings.h index f46e1e777f3..b54f83b791a 100644 --- a/editor/import/3d/scene_import_settings.h +++ b/editor/import/3d/scene_import_settings.h @@ -164,6 +164,8 @@ class SceneImportSettingsDialog : public ConfirmationDialog { }; HashMap node_map; + bool _get_current(const StringName &p_name, Variant &r_ret) const; + void _set_default(const StringName &p_name, const Variant &p_value); void _fill_material(Tree *p_tree, const Ref &p_material, TreeItem *p_parent); void _fill_mesh(Tree *p_tree, const Ref &p_mesh, TreeItem *p_parent); void _fill_animation(Tree *p_tree, const Ref &p_anim, const String &p_name, TreeItem *p_parent);