Browse Source

Merge pull request #47166 from reduz/improve-scene-import

Improved 3D Scene Importer
Rémi Verschelde 4 years ago
parent
commit
3334a53ac3

+ 3 - 0
core/io/resource_importer.h

@@ -115,6 +115,9 @@ public:
 		ImportOption() {}
 		ImportOption() {}
 	};
 	};
 
 
+	virtual bool has_advanced_options() const { return false; }
+	virtual void show_advanced_options(const String &p_path) {}
+
 	virtual int get_preset_count() const { return 0; }
 	virtual int get_preset_count() const { return 0; }
 	virtual String get_preset_name(int p_idx) const { return String(); }
 	virtual String get_preset_name(int p_idx) const { return String(); }
 
 

+ 27 - 14
editor/editor_file_system.cpp

@@ -1668,7 +1668,7 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
 	return err;
 	return err;
 }
 }
 
 
-void EditorFileSystem::_reimport_file(const String &p_file) {
+void EditorFileSystem::_reimport_file(const String &p_file, const Map<StringName, Variant> *p_custom_options, const String &p_custom_importer) {
 	EditorFileSystemDirectory *fs = nullptr;
 	EditorFileSystemDirectory *fs = nullptr;
 	int cpos = -1;
 	int cpos = -1;
 	bool found = _find_file(p_file, &fs, cpos);
 	bool found = _find_file(p_file, &fs, cpos);
@@ -1677,23 +1677,32 @@ void EditorFileSystem::_reimport_file(const String &p_file) {
 	//try to obtain existing params
 	//try to obtain existing params
 
 
 	Map<StringName, Variant> params;
 	Map<StringName, Variant> params;
-	String importer_name;
+	String importer_name; //empty by default though
+
+	if (p_custom_importer != String()) {
+		importer_name = p_custom_importer;
+	}
+	if (p_custom_options != nullptr) {
+		params = *p_custom_options;
+	}
 
 
 	if (FileAccess::exists(p_file + ".import")) {
 	if (FileAccess::exists(p_file + ".import")) {
 		//use existing
 		//use existing
-		Ref<ConfigFile> cf;
-		cf.instance();
-		Error err = cf->load(p_file + ".import");
-		if (err == OK) {
-			if (cf->has_section("params")) {
-				List<String> sk;
-				cf->get_section_keys("params", &sk);
-				for (List<String>::Element *E = sk.front(); E; E = E->next()) {
-					params[E->get()] = cf->get_value("params", E->get());
+		if (p_custom_options == nullptr) {
+			Ref<ConfigFile> cf;
+			cf.instance();
+			Error err = cf->load(p_file + ".import");
+			if (err == OK) {
+				if (cf->has_section("params")) {
+					List<String> sk;
+					cf->get_section_keys("params", &sk);
+					for (List<String>::Element *E = sk.front(); E; E = E->next()) {
+						params[E->get()] = cf->get_value("params", E->get());
+					}
+				}
+				if (p_custom_importer != String() && cf->has_section("remap")) {
+					importer_name = cf->get_value("remap", "importer");
 				}
 				}
-			}
-			if (cf->has_section("remap")) {
-				importer_name = cf->get_value("remap", "importer");
 			}
 			}
 		}
 		}
 
 
@@ -1887,6 +1896,10 @@ void EditorFileSystem::_find_group_files(EditorFileSystemDirectory *efd, Map<Str
 	}
 	}
 }
 }
 
 
+void EditorFileSystem::reimport_file_with_custom_parameters(const String &p_file, const String &p_importer, const Map<StringName, Variant> &p_custom_params) {
+	_reimport_file(p_file, &p_custom_params, p_importer);
+}
+
 void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
 void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
 	{
 	{
 		// Ensure that ProjectSettings::IMPORTED_FILES_PATH exists.
 		// Ensure that ProjectSettings::IMPORTED_FILES_PATH exists.

+ 3 - 1
editor/editor_file_system.h

@@ -203,7 +203,7 @@ class EditorFileSystem : public Node {
 
 
 	void _update_extensions();
 	void _update_extensions();
 
 
-	void _reimport_file(const String &p_file);
+	void _reimport_file(const String &p_file, const Map<StringName, Variant> *p_custom_options = nullptr, const String &p_custom_importer = String());
 	Error _reimport_group(const String &p_group_file, const Vector<String> &p_files);
 	Error _reimport_group(const String &p_group_file, const Vector<String> &p_files);
 
 
 	bool _test_for_reimport(const String &p_path, bool p_only_imported_files);
 	bool _test_for_reimport(const String &p_path, bool p_only_imported_files);
@@ -257,6 +257,8 @@ public:
 
 
 	void reimport_files(const Vector<String> &p_files);
 	void reimport_files(const Vector<String> &p_files);
 
 
+	void reimport_file_with_custom_parameters(const String &p_file, const String &p_importer, const Map<StringName, Variant> &p_custom_params);
+
 	void update_script_classes();
 	void update_script_classes();
 
 
 	bool is_group_file(const String &p_path) const;
 	bool is_group_file(const String &p_path) const;

+ 4 - 0
editor/editor_node.cpp

@@ -102,6 +102,7 @@
 #include "editor/import/resource_importer_texture.h"
 #include "editor/import/resource_importer_texture.h"
 #include "editor/import/resource_importer_texture_atlas.h"
 #include "editor/import/resource_importer_texture_atlas.h"
 #include "editor/import/resource_importer_wav.h"
 #include "editor/import/resource_importer_wav.h"
+#include "editor/import/scene_import_settings.h"
 #include "editor/import/scene_importer_mesh_node_3d.h"
 #include "editor/import/scene_importer_mesh_node_3d.h"
 #include "editor/import_dock.h"
 #include "editor/import_dock.h"
 #include "editor/multi_node_edit.h"
 #include "editor/multi_node_edit.h"
@@ -6179,6 +6180,9 @@ EditorNode::EditorNode() {
 	project_settings = memnew(ProjectSettingsEditor(&editor_data));
 	project_settings = memnew(ProjectSettingsEditor(&editor_data));
 	gui_base->add_child(project_settings);
 	gui_base->add_child(project_settings);
 
 
+	scene_import_settings = memnew(SceneImportSettings);
+	gui_base->add_child(scene_import_settings);
+
 	export_template_manager = memnew(ExportTemplateManager);
 	export_template_manager = memnew(ExportTemplateManager);
 	gui_base->add_child(export_template_manager);
 	gui_base->add_child(export_template_manager);
 
 

+ 2 - 0
editor/editor_node.h

@@ -88,6 +88,7 @@ class Button;
 class VSplitContainer;
 class VSplitContainer;
 class Window;
 class Window;
 class SubViewport;
 class SubViewport;
+class SceneImportSettings;
 
 
 class EditorNode : public Node {
 class EditorNode : public Node {
 	GDCLASS(EditorNode, Node);
 	GDCLASS(EditorNode, Node);
@@ -410,6 +411,7 @@ private:
 	EditorResourcePreview *resource_preview;
 	EditorResourcePreview *resource_preview;
 	EditorFolding editor_folding;
 	EditorFolding editor_folding;
 
 
+	SceneImportSettings *scene_import_settings;
 	struct BottomPanelItem {
 	struct BottomPanelItem {
 		String name;
 		String name;
 		Control *control = nullptr;
 		Control *control = nullptr;

+ 19 - 1
editor/filesystem_dock.cpp

@@ -945,7 +945,25 @@ void FileSystemDock::_select_file(const String &p_path, bool p_select_in_favorit
 		}
 		}
 	} else if (fpath != "Favorites") {
 	} else if (fpath != "Favorites") {
 		if (ResourceLoader::get_resource_type(fpath) == "PackedScene") {
 		if (ResourceLoader::get_resource_type(fpath) == "PackedScene") {
-			editor->open_request(fpath);
+			bool is_imported = false;
+
+			{
+				List<String> importer_exts;
+				ResourceImporterScene::get_singleton()->get_recognized_extensions(&importer_exts);
+				String extension = fpath.get_extension();
+				for (List<String>::Element *E = importer_exts.front(); E; E = E->next()) {
+					if (extension.nocasecmp_to(E->get())) {
+						is_imported = true;
+						break;
+					}
+				}
+			}
+
+			if (is_imported) {
+				ResourceImporterScene::get_singleton()->show_advanced_options(fpath);
+			} else {
+				editor->open_request(fpath);
+			}
 		} else {
 		} else {
 			editor->load_resource(fpath);
 			editor->load_resource(fpath);
 		}
 		}

+ 45 - 19
editor/import/editor_import_collada.cpp

@@ -79,6 +79,9 @@ struct ColladaImport {
 	Vector<int> valid_animated_properties;
 	Vector<int> valid_animated_properties;
 	Map<String, bool> bones_with_animation;
 	Map<String, bool> bones_with_animation;
 
 
+	Set<String> mesh_unique_names;
+	Set<String> material_unique_names;
+
 	Error _populate_skeleton(Skeleton3D *p_skeleton, Collada::Node *p_node, int &r_bone, int p_parent);
 	Error _populate_skeleton(Skeleton3D *p_skeleton, Collada::Node *p_node, int &r_bone, int p_parent);
 	Error _create_scene_skeletons(Collada::Node *p_node);
 	Error _create_scene_skeletons(Collada::Node *p_node);
 	Error _create_scene(Collada::Node *p_node, Node3D *p_parent);
 	Error _create_scene(Collada::Node *p_node, Node3D *p_parent);
@@ -326,12 +329,25 @@ Error ColladaImport::_create_material(const String &p_target) {
 
 
 	Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
 	Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
 
 
+	String base_name;
 	if (src_mat.name != "") {
 	if (src_mat.name != "") {
-		material->set_name(src_mat.name);
+		base_name = src_mat.name;
 	} else if (effect.name != "") {
 	} else if (effect.name != "") {
-		material->set_name(effect.name);
+		base_name = effect.name;
+	} else {
+		base_name = "Material";
 	}
 	}
 
 
+	String name = base_name;
+	int counter = 2;
+	while (material_unique_names.has(name)) {
+		name = base_name + itos(counter++);
+	}
+
+	material_unique_names.insert(name);
+
+	material->set_name(name);
+
 	// DIFFUSE
 	// DIFFUSE
 
 
 	if (effect.diffuse.texture != "") {
 	if (effect.diffuse.texture != "") {
@@ -1128,7 +1144,22 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
 					ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid), ERR_INVALID_DATA);
 					ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid), ERR_INVALID_DATA);
 					mesh = Ref<EditorSceneImporterMesh>(memnew(EditorSceneImporterMesh));
 					mesh = Ref<EditorSceneImporterMesh>(memnew(EditorSceneImporterMesh));
 					const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
 					const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
-					mesh->set_name(meshdata.name);
+					String name = meshdata.name;
+					if (name == "") {
+						name = "Mesh";
+					}
+					int counter = 2;
+					while (mesh_unique_names.has(name)) {
+						name = meshdata.name;
+						if (name == "") {
+							name = "Mesh";
+						}
+						name += itos(counter++);
+					}
+
+					mesh_unique_names.insert(name);
+
+					mesh->set_name(name);
 					Error err = _create_mesh_surfaces(morphs.size() == 0, mesh, ng2->material_map, meshdata, apply_xform, bone_remap, skin, morph, morphs, p_use_compression, use_mesh_builtin_materials);
 					Error err = _create_mesh_surfaces(morphs.size() == 0, mesh, ng2->material_map, meshdata, apply_xform, bone_remap, skin, morph, morphs, p_use_compression, use_mesh_builtin_materials);
 					ERR_FAIL_COND_V_MSG(err, err, "Cannot create mesh surface.");
 					ERR_FAIL_COND_V_MSG(err, err, "Cannot create mesh surface.");
 
 
@@ -1645,16 +1676,23 @@ void EditorSceneImporterCollada::get_extensions(List<String> *r_extensions) cons
 }
 }
 
 
 Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
 Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
+	if (r_err) {
+		*r_err = OK;
+	}
 	ColladaImport state;
 	ColladaImport state;
 	uint32_t flags = Collada::IMPORT_FLAG_SCENE;
 	uint32_t flags = Collada::IMPORT_FLAG_SCENE;
 	if (p_flags & IMPORT_ANIMATION) {
 	if (p_flags & IMPORT_ANIMATION) {
 		flags |= Collada::IMPORT_FLAG_ANIMATION;
 		flags |= Collada::IMPORT_FLAG_ANIMATION;
 	}
 	}
 
 
-	state.use_mesh_builtin_materials = !(p_flags & IMPORT_MATERIALS_IN_INSTANCES);
+	state.use_mesh_builtin_materials = true;
 	state.bake_fps = p_bake_fps;
 	state.bake_fps = p_bake_fps;
 
 
-	Error err = state.load(p_path, flags, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS, p_flags & EditorSceneImporter::IMPORT_USE_COMPRESSION);
+	Error err = state.load(p_path, flags, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS, 0);
+
+	if (r_err) {
+		*r_err = err;
+	}
 
 
 	ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Cannot load scene from file '" + p_path + "'.");
 	ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Cannot load scene from file '" + p_path + "'.");
 
 
@@ -1674,7 +1712,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
 	}
 	}
 
 
 	if (p_flags & IMPORT_ANIMATION) {
 	if (p_flags & IMPORT_ANIMATION) {
-		state.create_animations(p_flags & IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS, p_flags & EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS);
+		state.create_animations(true, true);
 		AnimationPlayer *ap = memnew(AnimationPlayer);
 		AnimationPlayer *ap = memnew(AnimationPlayer);
 		for (int i = 0; i < state.animations.size(); i++) {
 		for (int i = 0; i < state.animations.size(); i++) {
 			String name;
 			String name;
@@ -1684,12 +1722,6 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
 				name = state.animations[i]->get_name();
 				name = state.animations[i]->get_name();
 			}
 			}
 
 
-			if (p_flags & IMPORT_ANIMATION_DETECT_LOOP) {
-				if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
-					state.animations.write[i]->set_loop(true);
-				}
-			}
-
 			ap->add_animation(name, state.animations[i]);
 			ap->add_animation(name, state.animations[i]);
 		}
 		}
 		state.scene->add_child(ap);
 		state.scene->add_child(ap);
@@ -1707,7 +1739,7 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path
 	Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
 	Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
 	ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'.");
 	ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'.");
 
 
-	state.create_animations(p_flags & EditorSceneImporter::IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS, p_flags & EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS);
+	state.create_animations(true, true);
 	if (state.scene) {
 	if (state.scene) {
 		memdelete(state.scene);
 		memdelete(state.scene);
 	}
 	}
@@ -1716,12 +1748,6 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path
 		return Ref<Animation>();
 		return Ref<Animation>();
 	}
 	}
 	Ref<Animation> anim = state.animations[0];
 	Ref<Animation> anim = state.animations[0];
-	String base = p_path.get_basename().to_lower();
-	if (p_flags & IMPORT_ANIMATION_DETECT_LOOP) {
-		if (base.begins_with("loop") || base.ends_with("loop") || base.begins_with("cycle") || base.ends_with("cycle")) {
-			anim->set_loop(true);
-		}
-	}
 
 
 	return anim;
 	return anim;
 }
 }

+ 1 - 1
editor/import/resource_importer_obj.cpp

@@ -427,7 +427,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
 Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
 Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
 	List<Ref<Mesh>> meshes;
 	List<Ref<Mesh>> meshes;
 
 
-	Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, p_flags & IMPORT_USE_COMPRESSION, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps);
+	Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, 0, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps);
 
 
 	if (err != OK) {
 	if (err != OK) {
 		if (r_err) {
 		if (r_err) {

File diff suppressed because it is too large
+ 440 - 457
editor/import/resource_importer_scene.cpp


+ 50 - 39
editor/import/resource_importer_scene.h

@@ -39,7 +39,9 @@
 #include "scene/resources/skin.h"
 #include "scene/resources/skin.h"
 
 
 class Material;
 class Material;
+class AnimationPlayer;
 
 
+class EditorSceneImporterMesh;
 class EditorSceneImporter : public Reference {
 class EditorSceneImporter : public Reference {
 	GDCLASS(EditorSceneImporter, Reference);
 	GDCLASS(EditorSceneImporter, Reference);
 
 
@@ -53,15 +55,9 @@ public:
 	enum ImportFlags {
 	enum ImportFlags {
 		IMPORT_SCENE = 1,
 		IMPORT_SCENE = 1,
 		IMPORT_ANIMATION = 2,
 		IMPORT_ANIMATION = 2,
-		IMPORT_ANIMATION_DETECT_LOOP = 4,
-		IMPORT_ANIMATION_OPTIMIZE = 8,
-		IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS = 16,
-		IMPORT_ANIMATION_KEEP_VALUE_TRACKS = 32,
-		IMPORT_GENERATE_TANGENT_ARRAYS = 256,
-		IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 512,
-		IMPORT_MATERIALS_IN_INSTANCES = 1024,
-		IMPORT_USE_COMPRESSION = 2048,
-		IMPORT_USE_NAMED_SKIN_BINDS = 4096,
+		IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 4,
+		IMPORT_GENERATE_TANGENT_ARRAYS = 8,
+		IMPORT_USE_NAMED_SKIN_BINDS = 16,
 
 
 	};
 	};
 
 
@@ -76,17 +72,15 @@ public:
 class EditorScenePostImport : public Reference {
 class EditorScenePostImport : public Reference {
 	GDCLASS(EditorScenePostImport, Reference);
 	GDCLASS(EditorScenePostImport, Reference);
 
 
-	String source_folder;
 	String source_file;
 	String source_file;
 
 
 protected:
 protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
-	String get_source_folder() const;
 	String get_source_file() const;
 	String get_source_file() const;
 	virtual Node *post_import(Node *p_scene);
 	virtual Node *post_import(Node *p_scene);
-	virtual void init(const String &p_source_folder, const String &p_source_file);
+	virtual void init(const String &p_source_file);
 	EditorScenePostImport();
 	EditorScenePostImport();
 };
 };
 
 
@@ -97,31 +91,35 @@ class ResourceImporterScene : public ResourceImporter {
 
 
 	static ResourceImporterScene *singleton;
 	static ResourceImporterScene *singleton;
 
 
-	enum Presets {
-		PRESET_SEPARATE_MATERIALS,
-		PRESET_SEPARATE_MESHES,
-		PRESET_SEPARATE_ANIMATIONS,
-
-		PRESET_SINGLE_SCENE,
+	enum LightBakeMode {
+		LIGHT_BAKE_DISABLED,
+		LIGHT_BAKE_DYNAMIC,
+		LIGHT_BAKE_STATIC,
+		LIGHT_BAKE_STATIC_LIGHTMAPS
+	};
 
 
-		PRESET_SEPARATE_MESHES_AND_MATERIALS,
-		PRESET_SEPARATE_MESHES_AND_ANIMATIONS,
-		PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS,
-		PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS,
+	enum MeshPhysicsMode {
+		MESH_PHYSICS_DISABLED,
+		MESH_PHYSICS_MESH_AND_STATIC_COLLIDER,
+		MESH_PHYSICS_RIGID_BODY_AND_MESH,
+		MESH_PHYSICS_STATIC_COLLIDER_ONLY,
+		MESH_PHYSICS_AREA_ONLY,
+	};
 
 
-		PRESET_MULTIPLE_SCENES,
-		PRESET_MULTIPLE_SCENES_AND_MATERIALS,
-		PRESET_MAX
+	enum NavMeshMode {
+		NAVMESH_DISABLED,
+		NAVMESH_MESH_AND_NAVMESH,
+		NAVMESH_NAVMESH_ONLY,
 	};
 	};
 
 
-	enum LightBakeMode {
-		LIGHT_BAKE_DISABLED,
-		LIGHT_BAKE_ENABLE,
-		LIGHT_BAKE_LIGHTMAPS
+	enum MeshOverride {
+		MESH_OVERRIDE_DEFAULT,
+		MESH_OVERRIDE_ENABLE,
+		MESH_OVERRIDE_DISABLE,
 	};
 	};
 
 
 	void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner);
 	void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner);
-	void _generate_meshes(Node *p_node, bool p_generate_lods, bool p_create_shadow_meshes);
+	void _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<uint8_t> &r_dst_lightmap_cache);
 
 
 public:
 public:
 	static ResourceImporterScene *get_singleton() { return singleton; }
 	static ResourceImporterScene *get_singleton() { return singleton; }
@@ -141,26 +139,39 @@ public:
 	virtual int get_preset_count() const override;
 	virtual int get_preset_count() const override;
 	virtual String get_preset_name(int p_idx) const override;
 	virtual String get_preset_name(int p_idx) const override;
 
 
+	enum InternalImportCategory {
+		INTERNAL_IMPORT_CATEGORY_NODE,
+		INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE,
+		INTERNAL_IMPORT_CATEGORY_MESH,
+		INTERNAL_IMPORT_CATEGORY_MATERIAL,
+		INTERNAL_IMPORT_CATEGORY_ANIMATION,
+		INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE,
+		INTERNAL_IMPORT_CATEGORY_MAX
+	};
+
+	void get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const;
+	bool get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const;
+
 	virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
 	virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
 	virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
 	virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
 	virtual int get_import_order() const override { return 100; } //after everything
 	virtual int get_import_order() const override { return 100; } //after everything
 
 
-	void _find_meshes(Node *p_node, Map<Ref<ArrayMesh>, Transform> &meshes);
+	Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map);
+	Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map, Set<Ref<EditorSceneImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
 
 
-	void _make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_animations_as_text, bool p_keep_animations, bool p_make_materials, bool p_materials_as_text, bool p_keep_materials, bool p_make_meshes, bool p_meshes_as_text, Map<Ref<Animation>, Ref<Animation>> &p_animations, Map<Ref<Material>, Ref<Material>> &p_materials, Map<Ref<ArrayMesh>, Ref<ArrayMesh>> &p_meshes);
-
-	Node *_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape3D>>> &collision_map, LightBakeMode p_light_bake_mode);
-
-	void _create_clips(Node *scene, const Array &p_clips, bool p_bake_all);
-	void _filter_anim_tracks(Ref<Animation> anim, Set<String> &keep);
-	void _filter_tracks(Node *scene, const String &p_text);
-	void _optimize_animations(Node *scene, float p_max_lin_error, float p_max_ang_error, float p_max_angle);
+	Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks);
+	void _create_clips(AnimationPlayer *anim, const Array &p_clips, bool p_bake_all);
+	void _optimize_animations(AnimationPlayer *anim, float p_max_lin_error, float p_max_ang_error, float p_max_angle);
 
 
+	Node *pre_import(const String &p_source_file);
 	virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
 	virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
 
 
 	Node *import_scene_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
 	Node *import_scene_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
 	Ref<Animation> import_animation_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
 	Ref<Animation> import_animation_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
 
 
+	virtual bool has_advanced_options() const override;
+	virtual void show_advanced_options(const String &p_path) override;
+
 	ResourceImporterScene();
 	ResourceImporterScene();
 };
 };
 
 

+ 1199 - 0
editor/import/scene_import_settings.cpp

@@ -0,0 +1,1199 @@
+/*************************************************************************/
+/*  scene_import_settings.cpp                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "scene_import_settings.h"
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "editor/import/scene_importer_mesh_node_3d.h"
+#include "scene/resources/surface_tool.h"
+
+class SceneImportSettingsData : public Object {
+	GDCLASS(SceneImportSettingsData, Object)
+	friend class SceneImportSettings;
+	Map<StringName, Variant> *settings = nullptr;
+	Map<StringName, Variant> current;
+	Map<StringName, Variant> defaults;
+	List<ResourceImporter::ImportOption> options;
+
+	ResourceImporterScene::InternalImportCategory category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX;
+
+	bool _set(const StringName &p_name, const Variant &p_value) {
+		if (settings) {
+			if (defaults.has(p_name) && defaults[p_name] == p_value) {
+				settings->erase(p_name);
+			} else {
+				(*settings)[p_name] = p_value;
+			}
+
+			current[p_name] = p_value;
+			return true;
+		}
+		return false;
+	}
+	bool _get(const StringName &p_name, Variant &r_ret) const {
+		if (settings) {
+			if (settings->has(p_name)) {
+				r_ret = (*settings)[p_name];
+				return true;
+			}
+		}
+		if (defaults.has(p_name)) {
+			r_ret = defaults[p_name];
+			return true;
+		}
+		return false;
+	}
+	void _get_property_list(List<PropertyInfo> *p_list) const {
+		for (const List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) {
+			if (ResourceImporterScene::get_singleton()->get_internal_option_visibility(category, E->get().option.name, current)) {
+				p_list->push_back(E->get().option);
+			}
+		}
+	}
+};
+
+void SceneImportSettings::_fill_material(Tree *p_tree, const Ref<Material> &p_material, TreeItem *p_parent) {
+	String import_id;
+	bool has_import_id = false;
+
+	if (p_material->has_meta("import_id")) {
+		import_id = p_material->get_meta("import_id");
+		has_import_id = true;
+	} else if (p_material->get_name() != "") {
+		import_id = p_material->get_name();
+		has_import_id = true;
+	} else {
+		import_id = "@MATERIAL:" + itos(material_set.size());
+	}
+
+	if (!material_map.has(import_id)) {
+		MaterialData md;
+		md.has_import_id = has_import_id;
+		md.material = p_material;
+
+		_load_default_subresource_settings(md.settings, "materials", import_id, ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MATERIAL);
+
+		material_map[import_id] = md;
+	}
+
+	MaterialData &material_data = material_map[import_id];
+
+	Ref<Texture2D> icon = get_theme_icon("StandardMaterial3D", "EditorIcons");
+
+	TreeItem *item = p_tree->create_item(p_parent);
+	item->set_text(0, p_material->get_name());
+	item->set_icon(0, icon);
+
+	bool created = false;
+	if (!material_set.has(p_material)) {
+		material_set.insert(p_material);
+		created = true;
+	}
+
+	item->set_meta("type", "Material");
+	item->set_meta("import_id", import_id);
+	item->set_tooltip(0, vformat(TTR("Import ID: %s"), import_id));
+	item->set_selectable(0, true);
+
+	if (p_tree == scene_tree) {
+		material_data.scene_node = item;
+	} else if (p_tree == mesh_tree) {
+		material_data.mesh_node = item;
+	} else {
+		material_data.material_node = item;
+	}
+
+	if (created) {
+		_fill_material(material_tree, p_material, material_tree->get_root());
+	}
+}
+
+void SceneImportSettings::_fill_mesh(Tree *p_tree, const Ref<Mesh> &p_mesh, TreeItem *p_parent) {
+	String import_id;
+
+	bool has_import_id = false;
+	if (p_mesh->has_meta("import_id")) {
+		import_id = p_mesh->get_meta("import_id");
+		has_import_id = true;
+	} else if (p_mesh->get_name() != String()) {
+		import_id = p_mesh->get_name();
+		has_import_id = true;
+	} else {
+		import_id = "@MESH:" + itos(mesh_set.size());
+	}
+
+	if (!mesh_map.has(import_id)) {
+		MeshData md;
+		md.has_import_id = has_import_id;
+		md.mesh = p_mesh;
+
+		_load_default_subresource_settings(md.settings, "meshes", import_id, ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH);
+
+		mesh_map[import_id] = md;
+	}
+
+	MeshData &mesh_data = mesh_map[import_id];
+
+	Ref<Texture2D> icon = get_theme_icon("Mesh", "EditorIcons");
+
+	TreeItem *item = p_tree->create_item(p_parent);
+	item->set_text(0, p_mesh->get_name());
+	item->set_icon(0, icon);
+
+	bool created = false;
+	if (!mesh_set.has(p_mesh)) {
+		mesh_set.insert(p_mesh);
+		created = true;
+	}
+
+	item->set_meta("type", "Mesh");
+	item->set_meta("import_id", import_id);
+	item->set_tooltip(0, vformat(TTR("Import ID: %s"), import_id));
+
+	item->set_selectable(0, true);
+
+	if (p_tree == scene_tree) {
+		mesh_data.scene_node = item;
+	} else {
+		mesh_data.mesh_node = item;
+	}
+
+	item->set_collapsed(true);
+
+	for (int i = 0; i < p_mesh->get_surface_count(); i++) {
+		Ref<Material> mat = p_mesh->surface_get_material(i);
+		if (mat.is_valid()) {
+			_fill_material(p_tree, mat, item);
+		}
+	}
+
+	if (created) {
+		_fill_mesh(mesh_tree, p_mesh, mesh_tree->get_root());
+	}
+}
+
+void SceneImportSettings::_fill_animation(Tree *p_tree, const Ref<Animation> &p_anim, const String &p_name, TreeItem *p_parent) {
+	if (!animation_map.has(p_name)) {
+		AnimationData ad;
+		ad.animation = p_anim;
+
+		_load_default_subresource_settings(ad.settings, "animations", p_name, ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION);
+
+		animation_map[p_name] = ad;
+	}
+
+	AnimationData &animation_data = animation_map[p_name];
+
+	Ref<Texture2D> icon = get_theme_icon("Animation", "EditorIcons");
+
+	TreeItem *item = p_tree->create_item(p_parent);
+	item->set_text(0, p_name);
+	item->set_icon(0, icon);
+
+	item->set_meta("type", "Animation");
+	item->set_meta("import_id", p_name);
+
+	item->set_selectable(0, true);
+
+	animation_data.scene_node = item;
+}
+
+void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
+	String import_id;
+
+	if (p_node->has_meta("import_id")) {
+		import_id = p_node->get_meta("import_id");
+	} else {
+		import_id = "PATH:" + String(scene->get_path_to(p_node));
+		p_node->set_meta("import_id", import_id);
+	}
+
+	EditorSceneImporterMeshNode3D *src_mesh_node = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
+
+	if (src_mesh_node) {
+		MeshInstance3D *mesh_node = memnew(MeshInstance3D);
+		mesh_node->set_name(src_mesh_node->get_name());
+		mesh_node->set_transform(src_mesh_node->get_transform());
+		mesh_node->set_skin(src_mesh_node->get_skin());
+		mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());
+		if (src_mesh_node->get_mesh().is_valid()) {
+			Ref<EditorSceneImporterMesh> editor_mesh = src_mesh_node->get_mesh();
+			mesh_node->set_mesh(editor_mesh->get_mesh());
+		}
+
+		p_node->replace_by(mesh_node);
+		memdelete(p_node);
+		p_node = mesh_node;
+	}
+
+	String type = p_node->get_class();
+
+	if (!has_theme_icon(type, "EditorIcons")) {
+		type = "Node3D";
+	}
+
+	Ref<Texture2D> icon = get_theme_icon(type, "EditorIcons");
+
+	TreeItem *item = scene_tree->create_item(p_parent_item);
+	item->set_text(0, p_node->get_name());
+
+	if (p_node == scene) {
+		icon = get_theme_icon("PackedScene", "EditorIcons");
+		item->set_text(0, "Scene");
+	}
+
+	item->set_icon(0, icon);
+
+	item->set_meta("type", "Node");
+	item->set_meta("class", type);
+	item->set_meta("import_id", import_id);
+	item->set_tooltip(0, vformat(TTR("Type: %s\nImport ID: %s"), type, import_id));
+
+	item->set_selectable(0, true);
+
+	if (!node_map.has(import_id)) {
+		NodeData nd;
+
+		if (p_node != scene) {
+			ResourceImporterScene::InternalImportCategory category;
+			if (src_mesh_node) {
+				category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE;
+			} else if (Object::cast_to<AnimationPlayer>(p_node)) {
+				category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
+			} else {
+				category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
+			}
+
+			_load_default_subresource_settings(nd.settings, "nodes", import_id, category);
+		}
+
+		node_map[import_id] = nd;
+	}
+	NodeData &node_data = node_map[import_id];
+
+	node_data.node = p_node;
+	node_data.scene_node = item;
+
+	AnimationPlayer *anim_node = Object::cast_to<AnimationPlayer>(p_node);
+	if (anim_node) {
+		List<StringName> animations;
+		anim_node->get_animation_list(&animations);
+		for (List<StringName>::Element *E = animations.front(); E; E = E->next()) {
+			_fill_animation(scene_tree, anim_node->get_animation(E->get()), E->get(), item);
+		}
+	}
+
+	for (int i = 0; i < p_node->get_child_count(); i++) {
+		_fill_scene(p_node->get_child(i), item);
+	}
+	MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(p_node);
+	if (mesh_node && mesh_node->get_mesh().is_valid()) {
+		_fill_mesh(scene_tree, mesh_node->get_mesh(), item);
+
+		Transform accum_xform;
+		Node3D *base = mesh_node;
+		while (base) {
+			accum_xform = base->get_transform() * accum_xform;
+			base = Object::cast_to<Node3D>(base->get_parent());
+		}
+
+		AABB aabb = accum_xform.xform(mesh_node->get_mesh()->get_aabb());
+		if (first_aabb) {
+			contents_aabb = aabb;
+			first_aabb = false;
+		} else {
+			contents_aabb.merge_with(aabb);
+		}
+	}
+}
+
+void SceneImportSettings::_update_scene() {
+	scene_tree->clear();
+	material_tree->clear();
+	mesh_tree->clear();
+
+	//hiden roots
+	material_tree->create_item();
+	mesh_tree->create_item();
+
+	_fill_scene(scene, nullptr);
+}
+
+void SceneImportSettings::_update_camera() {
+	AABB camera_aabb;
+
+	float rot_x = cam_rot_x;
+	float rot_y = cam_rot_y;
+	float zoom = cam_zoom;
+
+	if (selected_type == "Node" || selected_type == "") {
+		camera_aabb = contents_aabb;
+	} else {
+		if (mesh_preview->get_mesh().is_valid()) {
+			camera_aabb = mesh_preview->get_transform().xform(mesh_preview->get_mesh()->get_aabb());
+		} else {
+			camera_aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
+		}
+		if (selected_type == "Mesh" && mesh_map.has(selected_id)) {
+			const MeshData &md = mesh_map[selected_id];
+			rot_x = md.cam_rot_x;
+			rot_y = md.cam_rot_y;
+			zoom = md.cam_zoom;
+		} else if (selected_type == "Material" && material_map.has(selected_id)) {
+			const MaterialData &md = material_map[selected_id];
+			rot_x = md.cam_rot_x;
+			rot_y = md.cam_rot_y;
+			zoom = md.cam_zoom;
+		}
+	}
+
+	Vector3 center = camera_aabb.position + camera_aabb.size * 0.5;
+	float camera_size = camera_aabb.get_longest_axis_size();
+
+	camera->set_orthogonal(camera_size * zoom, 0.0001, camera_size * 2);
+
+	Transform xf;
+	xf.basis = Basis(Vector3(0, 1, 0), rot_y) * Basis(Vector3(1, 0, 0), rot_x);
+	xf.origin = center;
+	xf.translate(0, 0, camera_size);
+
+	camera->set_transform(xf);
+}
+
+void SceneImportSettings::_load_default_subresource_settings(Map<StringName, Variant> &settings, const String &p_type, const String &p_import_id, ResourceImporterScene::InternalImportCategory p_category) {
+	if (base_subresource_settings.has(p_type)) {
+		Dictionary d = base_subresource_settings[p_type];
+		if (d.has(p_import_id)) {
+			d = d[p_import_id];
+			List<ResourceImporterScene::ImportOption> options;
+			ResourceImporterScene::get_singleton()->get_internal_import_options(p_category, &options);
+			for (List<ResourceImporterScene::ImportOption>::Element *E = options.front(); E; E = E->next()) {
+				String key = E->get().option.name;
+				if (d.has(key)) {
+					settings[key] = d[key];
+				}
+			}
+		}
+	}
+}
+
+void SceneImportSettings::open_settings(const String &p_path) {
+	if (scene) {
+		memdelete(scene);
+		scene = nullptr;
+	}
+	scene = ResourceImporterScene::get_singleton()->pre_import(p_path);
+	if (scene == nullptr) {
+		EditorNode::get_singleton()->show_warning(TTR("Error opening scene"));
+		return;
+	}
+
+	base_path = p_path;
+
+	material_set.clear();
+	mesh_set.clear();
+	material_map.clear();
+	mesh_map.clear();
+	node_map.clear();
+	defaults.clear();
+
+	selected_id = "";
+	selected_type = "";
+
+	cam_rot_x = -Math_PI / 4;
+	cam_rot_y = -Math_PI / 4;
+	cam_zoom = 1;
+
+	{
+		base_subresource_settings.clear();
+
+		Ref<ConfigFile> config;
+		config.instance();
+		Error err = config->load(p_path + ".import");
+		if (err == OK) {
+			List<String> keys;
+			config->get_section_keys("params", &keys);
+			for (List<String>::Element *E = keys.front(); E; E = E->next()) {
+				Variant value = config->get_value("params", E->get());
+				if (E->get() == "_subresources") {
+					base_subresource_settings = value;
+				} else {
+					defaults[E->get()] = value;
+				}
+			}
+		}
+	}
+
+	first_aabb = true;
+
+	_update_scene();
+
+	base_viewport->add_child(scene);
+
+	if (first_aabb) {
+		contents_aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
+		first_aabb = false;
+	}
+
+	popup_centered_ratio();
+	_update_camera();
+
+	set_title(vformat(TTR("Advanced Import Settings for '%s'"), base_path.get_file()));
+}
+
+SceneImportSettings *SceneImportSettings::singleton = nullptr;
+
+SceneImportSettings *SceneImportSettings::get_singleton() {
+	return singleton;
+}
+
+void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
+	selecting = true;
+
+	if (p_type == "Node") {
+		node_selected->hide(); //always hide just in case
+		mesh_preview->hide();
+		if (Object::cast_to<Node3D>(scene)) {
+			Object::cast_to<Node3D>(scene)->show();
+		}
+		//NodeData &nd=node_map[p_id];
+		material_tree->deselect_all();
+		mesh_tree->deselect_all();
+		NodeData &nd = node_map[p_id];
+
+		MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(nd.node);
+		if (mi) {
+			Ref<Mesh> base_mesh = mi->get_mesh();
+			if (base_mesh.is_valid()) {
+				AABB aabb = base_mesh->get_aabb();
+				Transform aabb_xf;
+				aabb_xf.basis.scale(aabb.size);
+				aabb_xf.origin = aabb.position;
+
+				aabb_xf = mi->get_global_transform() * aabb_xf;
+				node_selected->set_transform(aabb_xf);
+				node_selected->show();
+			}
+		}
+
+		if (nd.node == scene) {
+			scene_import_settings_data->settings = &defaults;
+			scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX;
+		} else {
+			scene_import_settings_data->settings = &nd.settings;
+			if (mi) {
+				scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE;
+			} else if (Object::cast_to<AnimationPlayer>(nd.node)) {
+				scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
+			} else {
+				scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
+			}
+		}
+	} else if (p_type == "Animation") {
+		node_selected->hide(); //always hide just in case
+		mesh_preview->hide();
+		if (Object::cast_to<Node3D>(scene)) {
+			Object::cast_to<Node3D>(scene)->show();
+		}
+		//NodeData &nd=node_map[p_id];
+		material_tree->deselect_all();
+		mesh_tree->deselect_all();
+		AnimationData &ad = animation_map[p_id];
+
+		scene_import_settings_data->settings = &ad.settings;
+		scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION;
+	} else if (p_type == "Mesh") {
+		node_selected->hide();
+		if (Object::cast_to<Node3D>(scene)) {
+			Object::cast_to<Node3D>(scene)->hide();
+		}
+
+		MeshData &md = mesh_map[p_id];
+		if (p_from != mesh_tree) {
+			md.mesh_node->uncollapse_tree();
+			md.mesh_node->select(0);
+			mesh_tree->ensure_cursor_is_visible();
+		}
+		if (p_from != scene_tree) {
+			md.scene_node->uncollapse_tree();
+			md.scene_node->select(0);
+			scene_tree->ensure_cursor_is_visible();
+		}
+
+		mesh_preview->set_mesh(md.mesh);
+		mesh_preview->show();
+
+		material_tree->deselect_all();
+
+		scene_import_settings_data->settings = &md.settings;
+		scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH;
+	} else if (p_type == "Material") {
+		node_selected->hide();
+		if (Object::cast_to<Node3D>(scene)) {
+			Object::cast_to<Node3D>(scene)->hide();
+		}
+
+		mesh_preview->show();
+
+		MaterialData &md = material_map[p_id];
+
+		material_preview->set_material(md.material);
+		mesh_preview->set_mesh(material_preview);
+
+		if (p_from != mesh_tree) {
+			md.mesh_node->uncollapse_tree();
+			md.mesh_node->select(0);
+			mesh_tree->ensure_cursor_is_visible();
+		}
+		if (p_from != scene_tree) {
+			md.scene_node->uncollapse_tree();
+			md.scene_node->select(0);
+			scene_tree->ensure_cursor_is_visible();
+		}
+		if (p_from != material_tree) {
+			md.material_node->uncollapse_tree();
+			md.material_node->select(0);
+			material_tree->ensure_cursor_is_visible();
+		}
+
+		scene_import_settings_data->settings = &md.settings;
+		scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MATERIAL;
+	}
+
+	selected_type = p_type;
+	selected_id = p_id;
+
+	selecting = false;
+
+	_update_camera();
+
+	List<ResourceImporter::ImportOption> options;
+
+	if (scene_import_settings_data->category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) {
+		ResourceImporterScene::get_singleton()->get_import_options(&options);
+	} else {
+		ResourceImporterScene::get_singleton()->get_internal_import_options(scene_import_settings_data->category, &options);
+	}
+
+	scene_import_settings_data->defaults.clear();
+	scene_import_settings_data->current.clear();
+
+	for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) {
+		scene_import_settings_data->defaults[E->get().option.name] = E->get().default_value;
+		//needed for visibility toggling (fails if something is missing)
+		if (scene_import_settings_data->settings->has(E->get().option.name)) {
+			scene_import_settings_data->current[E->get().option.name] = (*scene_import_settings_data->settings)[E->get().option.name];
+		} else {
+			scene_import_settings_data->current[E->get().option.name] = E->get().default_value;
+		}
+	}
+	scene_import_settings_data->options = options;
+	inspector->edit(scene_import_settings_data);
+	scene_import_settings_data->notify_property_list_changed();
+}
+
+void SceneImportSettings::_material_tree_selected() {
+	if (selecting) {
+		return;
+	}
+	TreeItem *item = material_tree->get_selected();
+	String type = item->get_meta("type");
+	String import_id = item->get_meta("import_id");
+
+	_select(material_tree, type, import_id);
+}
+void SceneImportSettings::_mesh_tree_selected() {
+	if (selecting) {
+		return;
+	}
+
+	TreeItem *item = mesh_tree->get_selected();
+	String type = item->get_meta("type");
+	String import_id = item->get_meta("import_id");
+
+	_select(mesh_tree, type, import_id);
+}
+void SceneImportSettings::_scene_tree_selected() {
+	if (selecting) {
+		return;
+	}
+	TreeItem *item = scene_tree->get_selected();
+	String type = item->get_meta("type");
+	String import_id = item->get_meta("import_id");
+
+	_select(scene_tree, type, import_id);
+}
+
+void SceneImportSettings::_viewport_input(const Ref<InputEvent> &p_input) {
+	float *rot_x = &cam_rot_x;
+	float *rot_y = &cam_rot_y;
+	float *zoom = &cam_zoom;
+
+	if (selected_type == "Mesh" && mesh_map.has(selected_id)) {
+		MeshData &md = mesh_map[selected_id];
+		rot_x = &md.cam_rot_x;
+		rot_y = &md.cam_rot_y;
+		zoom = &md.cam_zoom;
+	} else if (selected_type == "Material" && material_map.has(selected_id)) {
+		MaterialData &md = material_map[selected_id];
+		rot_x = &md.cam_rot_x;
+		rot_y = &md.cam_rot_y;
+		zoom = &md.cam_zoom;
+	}
+	Ref<InputEventMouseMotion> mm = p_input;
+	if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
+		(*rot_x) -= mm->get_relative().y * 0.01 * EDSCALE;
+		(*rot_y) -= mm->get_relative().x * 0.01 * EDSCALE;
+		(*rot_x) = CLAMP((*rot_x), -Math_PI / 2, Math_PI / 2);
+		_update_camera();
+	}
+	Ref<InputEventMouseButton> mb = p_input;
+	if (mb.is_valid() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+		(*zoom) *= 1.1;
+		if ((*zoom) > 10.0) {
+			(*zoom) = 10.0;
+		}
+		_update_camera();
+	}
+	if (mb.is_valid() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+		(*zoom) /= 1.1;
+		if ((*zoom) < 0.1) {
+			(*zoom) = 0.1;
+		}
+		_update_camera();
+	}
+}
+
+void SceneImportSettings::_re_import() {
+	Map<StringName, Variant> main_settings;
+
+	main_settings = defaults;
+	main_settings.erase("_subresources");
+	Dictionary nodes;
+	Dictionary materials;
+	Dictionary meshes;
+	Dictionary animations;
+
+	Dictionary subresources;
+
+	for (Map<String, NodeData>::Element *E = node_map.front(); E; E = E->next()) {
+		if (E->get().settings.size()) {
+			Dictionary d;
+			for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
+				d[String(F->key())] = F->get();
+			}
+			nodes[E->key()] = d;
+		}
+	}
+	if (nodes.size()) {
+		subresources["nodes"] = nodes;
+	}
+
+	for (Map<String, MaterialData>::Element *E = material_map.front(); E; E = E->next()) {
+		if (E->get().settings.size()) {
+			Dictionary d;
+			for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
+				d[String(F->key())] = F->get();
+			}
+			materials[E->key()] = d;
+		}
+	}
+	if (materials.size()) {
+		subresources["materials"] = materials;
+	}
+
+	for (Map<String, MeshData>::Element *E = mesh_map.front(); E; E = E->next()) {
+		if (E->get().settings.size()) {
+			Dictionary d;
+			for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
+				d[String(F->key())] = F->get();
+			}
+			meshes[E->key()] = d;
+		}
+	}
+	if (meshes.size()) {
+		subresources["meshes"] = meshes;
+	}
+
+	for (Map<String, AnimationData>::Element *E = animation_map.front(); E; E = E->next()) {
+		if (E->get().settings.size()) {
+			Dictionary d;
+			for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
+				d[String(F->key())] = F->get();
+			}
+			animations[E->key()] = d;
+		}
+	}
+	if (animations.size()) {
+		subresources["animations"] = animations;
+	}
+
+	if (subresources.size()) {
+		main_settings["_subresources"] = subresources;
+	}
+
+	EditorFileSystem::get_singleton()->reimport_file_with_custom_parameters(base_path, "scene", main_settings);
+}
+
+void SceneImportSettings::_notification(int p_what) {
+	if (p_what == NOTIFICATION_READY) {
+		connect("confirmed", callable_mp(this, &SceneImportSettings::_re_import));
+	}
+}
+
+void SceneImportSettings::_menu_callback(int p_id) {
+	switch (p_id) {
+		case ACTION_EXTRACT_MATERIALS: {
+			save_path->set_text(TTR("Select folder to extract material resources"));
+			external_extension_type->select(0);
+		} break;
+		case ACTION_CHOOSE_MESH_SAVE_PATHS: {
+			save_path->set_text(TTR("Select folder where mesh resources will save on import"));
+			external_extension_type->select(1);
+		} break;
+		case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: {
+			save_path->set_text(TTR("Select folder where animations will save on import"));
+			external_extension_type->select(1);
+		} break;
+	}
+
+	save_path->set_current_dir(base_path.get_base_dir());
+	current_action = p_id;
+	save_path->popup_centered_ratio();
+}
+
+void SceneImportSettings::_save_path_changed(const String &p_path) {
+	save_path_item->set_text(1, p_path);
+
+	if (FileAccess::exists(p_path)) {
+		save_path_item->set_text(2, "Warning: File exists");
+		save_path_item->set_tooltip(2, TTR("Existing file with the same name will be replaced."));
+		save_path_item->set_icon(2, get_theme_icon("StatusWarning", "EditorIcons"));
+
+	} else {
+		save_path_item->set_text(2, "Will create new File");
+		save_path_item->set_icon(2, get_theme_icon("StatusSuccess", "EditorIcons"));
+	}
+}
+
+void SceneImportSettings::_browse_save_callback(Object *p_item, int p_column, int p_id) {
+	TreeItem *item = Object::cast_to<TreeItem>(p_item);
+
+	String path = item->get_text(1);
+
+	item_save_path->set_current_file(path);
+	save_path_item = item;
+
+	item_save_path->popup_centered_ratio();
+}
+
+void SceneImportSettings::_save_dir_callback(const String &p_path) {
+	external_path_tree->clear();
+	TreeItem *root = external_path_tree->create_item();
+	save_path_items.clear();
+
+	switch (current_action) {
+		case ACTION_EXTRACT_MATERIALS: {
+			for (Map<String, MaterialData>::Element *E = material_map.front(); E; E = E->next()) {
+				MaterialData &md = material_map[E->key()];
+
+				TreeItem *item = external_path_tree->create_item(root);
+
+				String name = md.material_node->get_text(0);
+
+				item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+				item->set_icon(0, get_theme_icon("StandardMaterial3D", "EditorIcons"));
+				item->set_text(0, name);
+
+				if (md.has_import_id) {
+					if (md.settings.has("use_external/enabled") && bool(md.settings["use_external/enabled"])) {
+						item->set_text(2, "Already External");
+						item->set_tooltip(2, TTR("This material already references an external file, no action will be taken.\nDisable the external property for it to be extracted again."));
+					} else {
+						item->set_metadata(0, E->key());
+						item->set_editable(0, true);
+						item->set_checked(0, true);
+						String path = p_path.plus_file(name);
+						if (external_extension_type->get_selected() == 0) {
+							path += ".tres";
+						} else {
+							path += ".res";
+						}
+
+						item->set_text(1, path);
+						if (FileAccess::exists(path)) {
+							item->set_text(2, "Warning: File exists");
+							item->set_tooltip(2, TTR("Existing file with the same name will be replaced."));
+							item->set_icon(2, get_theme_icon("StatusWarning", "EditorIcons"));
+
+						} else {
+							item->set_text(2, "Will create new File");
+							item->set_icon(2, get_theme_icon("StatusSuccess", "EditorIcons"));
+						}
+
+						item->add_button(1, get_theme_icon("Folder", "EditorIcons"));
+					}
+
+				} else {
+					item->set_text(2, "No import ID");
+					item->set_tooltip(2, TTR("Material has no name nor any other way to identify on re-import.\nPlease name it or ensure it is exported with an unique ID."));
+					item->set_icon(2, get_theme_icon("StatusError", "EditorIcons"));
+				}
+
+				save_path_items.push_back(item);
+			}
+
+			external_paths->set_title(TTR("Extract Materials to Resource Files"));
+			external_paths->get_ok_button()->set_text(TTR("Extract"));
+		} break;
+		case ACTION_CHOOSE_MESH_SAVE_PATHS: {
+			for (Map<String, MeshData>::Element *E = mesh_map.front(); E; E = E->next()) {
+				MeshData &md = mesh_map[E->key()];
+
+				TreeItem *item = external_path_tree->create_item(root);
+
+				String name = md.mesh_node->get_text(0);
+
+				item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+				item->set_icon(0, get_theme_icon("Mesh", "EditorIcons"));
+				item->set_text(0, name);
+
+				if (md.has_import_id) {
+					if (md.settings.has("save_to_file/enabled") && bool(md.settings["save_to_file/enabled"])) {
+						item->set_text(2, "Already Saving");
+						item->set_tooltip(2, TTR("This mesh already saves to an external resource, no action will be taken."));
+					} else {
+						item->set_metadata(0, E->key());
+						item->set_editable(0, true);
+						item->set_checked(0, true);
+						String path = p_path.plus_file(name);
+						if (external_extension_type->get_selected() == 0) {
+							path += ".tres";
+						} else {
+							path += ".res";
+						}
+
+						item->set_text(1, path);
+						if (FileAccess::exists(path)) {
+							item->set_text(2, "Warning: File exists");
+							item->set_tooltip(2, TTR("Existing file with the same name will be replaced on import."));
+							item->set_icon(2, get_theme_icon("StatusWarning", "EditorIcons"));
+
+						} else {
+							item->set_text(2, "Will save to new File");
+							item->set_icon(2, get_theme_icon("StatusSuccess", "EditorIcons"));
+						}
+
+						item->add_button(1, get_theme_icon("Folder", "EditorIcons"));
+					}
+
+				} else {
+					item->set_text(2, "No import ID");
+					item->set_tooltip(2, TTR("Mesh has no name nor any other way to identify on re-import.\nPlease name it or ensure it is exported with an unique ID."));
+					item->set_icon(2, get_theme_icon("StatusError", "EditorIcons"));
+				}
+
+				save_path_items.push_back(item);
+			}
+
+			external_paths->set_title(TTR("Set paths to save meshes as resource files on Reimport"));
+			external_paths->get_ok_button()->set_text(TTR("Set Paths"));
+		} break;
+		case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: {
+			for (Map<String, AnimationData>::Element *E = animation_map.front(); E; E = E->next()) {
+				AnimationData &ad = animation_map[E->key()];
+
+				TreeItem *item = external_path_tree->create_item(root);
+
+				String name = ad.scene_node->get_text(0);
+
+				item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+				item->set_icon(0, get_theme_icon("Animation", "EditorIcons"));
+				item->set_text(0, name);
+
+				if (ad.settings.has("save_to_file/enabled") && bool(ad.settings["save_to_file/enabled"])) {
+					item->set_text(2, "Already Saving");
+					item->set_tooltip(2, TTR("This animation already saves to an external resource, no action will be taken."));
+				} else {
+					item->set_metadata(0, E->key());
+					item->set_editable(0, true);
+					item->set_checked(0, true);
+					String path = p_path.plus_file(name);
+					if (external_extension_type->get_selected() == 0) {
+						path += ".tres";
+					} else {
+						path += ".res";
+					}
+
+					item->set_text(1, path);
+					if (FileAccess::exists(path)) {
+						item->set_text(2, "Warning: File exists");
+						item->set_tooltip(2, TTR("Existing file with the same name will be replaced on import."));
+						item->set_icon(2, get_theme_icon("StatusWarning", "EditorIcons"));
+
+					} else {
+						item->set_text(2, "Will save to new File");
+						item->set_icon(2, get_theme_icon("StatusSuccess", "EditorIcons"));
+					}
+
+					item->add_button(1, get_theme_icon("Folder", "EditorIcons"));
+				}
+
+				save_path_items.push_back(item);
+			}
+
+			external_paths->set_title(TTR("Set paths to save animations as resource files on Reimport"));
+			external_paths->get_ok_button()->set_text(TTR("Set Paths"));
+
+		} break;
+	}
+
+	external_paths->popup_centered_ratio();
+}
+
+void SceneImportSettings::_save_dir_confirm() {
+	for (int i = 0; i < save_path_items.size(); i++) {
+		TreeItem *item = save_path_items[i];
+		if (!item->is_checked(0)) {
+			continue; //ignore
+		}
+		String path = item->get_text(1);
+		if (!path.is_resource_file()) {
+			continue;
+		}
+
+		String id = item->get_metadata(0);
+
+		switch (current_action) {
+			case ACTION_EXTRACT_MATERIALS: {
+				ERR_CONTINUE(!material_map.has(id));
+				MaterialData &md = material_map[id];
+
+				Error err = ResourceSaver::save(path, md.material);
+				if (err != OK) {
+					EditorNode::get_singleton()->add_io_error(TTR("Can't make material external to file, write error:") + "\n\t" + path);
+					continue;
+				}
+
+				md.settings["use_external/enabled"] = true;
+				md.settings["use_external/path"] = path;
+
+			} break;
+			case ACTION_CHOOSE_MESH_SAVE_PATHS: {
+				ERR_CONTINUE(!mesh_map.has(id));
+				MeshData &md = mesh_map[id];
+
+				md.settings["save_to_file/enabled"] = true;
+				md.settings["save_to_file/path"] = path;
+			} break;
+			case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: {
+				ERR_CONTINUE(!animation_map.has(id));
+				AnimationData &ad = animation_map[id];
+
+				ad.settings["save_to_file/enabled"] = true;
+				ad.settings["save_to_file/path"] = path;
+
+			} break;
+		}
+	}
+
+	if (current_action == ACTION_EXTRACT_MATERIALS) {
+		//as this happens right now, the scene needs to be saved and reimported.
+		_re_import();
+		open_settings(base_path);
+	} else {
+		scene_import_settings_data->notify_property_list_changed();
+	}
+}
+
+SceneImportSettings::SceneImportSettings() {
+	singleton = this;
+
+	VBoxContainer *main_vb = memnew(VBoxContainer);
+	add_child(main_vb);
+	HBoxContainer *menu_hb = memnew(HBoxContainer);
+	main_vb->add_child(menu_hb);
+
+	action_menu = memnew(MenuButton);
+	action_menu->set_text(TTR("Actions..."));
+	menu_hb->add_child(action_menu);
+
+	action_menu->get_popup()->add_item(TTR("Extract Materials"), ACTION_EXTRACT_MATERIALS);
+	action_menu->get_popup()->add_separator();
+	action_menu->get_popup()->add_item(TTR("Set Animation Save Paths"), ACTION_CHOOSE_ANIMATION_SAVE_PATHS);
+	action_menu->get_popup()->add_item(TTR("Set Mesh Save Paths"), ACTION_CHOOSE_MESH_SAVE_PATHS);
+
+	action_menu->get_popup()->connect("id_pressed", callable_mp(this, &SceneImportSettings::_menu_callback));
+
+	tree_split = memnew(HSplitContainer);
+	main_vb->add_child(tree_split);
+	tree_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+	data_mode = memnew(TabContainer);
+	tree_split->add_child(data_mode);
+	data_mode->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
+
+	property_split = memnew(HSplitContainer);
+	tree_split->add_child(property_split);
+	property_split->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+	scene_tree = memnew(Tree);
+	scene_tree->set_name(TTR("Scene"));
+	data_mode->add_child(scene_tree);
+	scene_tree->connect("cell_selected", callable_mp(this, &SceneImportSettings::_scene_tree_selected));
+
+	mesh_tree = memnew(Tree);
+	mesh_tree->set_name(TTR("Meshes"));
+	data_mode->add_child(mesh_tree);
+	mesh_tree->set_hide_root(true);
+	mesh_tree->connect("cell_selected", callable_mp(this, &SceneImportSettings::_mesh_tree_selected));
+
+	material_tree = memnew(Tree);
+	material_tree->set_name(TTR("Materials"));
+	data_mode->add_child(material_tree);
+	material_tree->connect("cell_selected", callable_mp(this, &SceneImportSettings::_material_tree_selected));
+
+	material_tree->set_hide_root(true);
+
+	SubViewportContainer *vp_container = memnew(SubViewportContainer);
+	vp_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	vp_container->set_custom_minimum_size(Size2(10, 10));
+	vp_container->set_stretch(true);
+	vp_container->connect("gui_input", callable_mp(this, &SceneImportSettings::_viewport_input));
+	property_split->add_child(vp_container);
+
+	base_viewport = memnew(SubViewport);
+	vp_container->add_child(base_viewport);
+
+	base_viewport->set_use_own_world_3d(true);
+
+	camera = memnew(Camera3D);
+	base_viewport->add_child(camera);
+	camera->make_current();
+
+	light = memnew(DirectionalLight3D);
+	light->set_transform(Transform().looking_at(Vector3(-1, -2, -0.6), Vector3(0, 1, 0)));
+	base_viewport->add_child(light);
+	light->set_shadow(true);
+
+	{
+		Ref<StandardMaterial3D> selection_mat;
+		selection_mat.instance();
+		selection_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+		selection_mat->set_albedo(Color(1, 0.8, 1.0));
+
+		Ref<SurfaceTool> st;
+		st.instance();
+		st->begin(Mesh::PRIMITIVE_LINES);
+
+		AABB base_aabb;
+		base_aabb.size = Vector3(1, 1, 1);
+
+		for (int i = 0; i < 12; i++) {
+			Vector3 a, b;
+			base_aabb.get_edge(i, a, b);
+
+			st->add_vertex(a);
+			st->add_vertex(a.lerp(b, 0.2));
+			st->add_vertex(b);
+			st->add_vertex(b.lerp(a, 0.2));
+		}
+
+		selection_mesh.instance();
+		st->commit(selection_mesh);
+		selection_mesh->surface_set_material(0, selection_mat);
+
+		node_selected = memnew(MeshInstance3D);
+		node_selected->set_mesh(selection_mesh);
+		base_viewport->add_child(node_selected);
+		node_selected->hide();
+	}
+
+	{
+		mesh_preview = memnew(MeshInstance3D);
+		base_viewport->add_child(mesh_preview);
+		mesh_preview->hide();
+
+		material_preview.instance();
+	}
+
+	inspector = memnew(EditorInspector);
+	inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
+
+	property_split->add_child(inspector);
+
+	scene_import_settings_data = memnew(SceneImportSettingsData);
+
+	get_ok_button()->set_text(TTR("Reimport"));
+	get_cancel_button()->set_text(TTR("Close"));
+
+	external_paths = memnew(ConfirmationDialog);
+	add_child(external_paths);
+	external_path_tree = memnew(Tree);
+	external_paths->add_child(external_path_tree);
+	external_path_tree->connect("button_pressed", callable_mp(this, &SceneImportSettings::_browse_save_callback));
+	external_paths->connect("confirmed", callable_mp(this, &SceneImportSettings::_save_dir_confirm));
+	external_path_tree->set_columns(3);
+	external_path_tree->set_column_titles_visible(true);
+	external_path_tree->set_column_expand(0, true);
+	external_path_tree->set_column_min_width(0, 100 * EDSCALE);
+	external_path_tree->set_column_title(0, TTR("Resource"));
+	external_path_tree->set_column_expand(1, true);
+	external_path_tree->set_column_min_width(1, 100 * EDSCALE);
+	external_path_tree->set_column_title(1, TTR("Path"));
+	external_path_tree->set_column_expand(2, false);
+	external_path_tree->set_column_min_width(2, 200 * EDSCALE);
+	external_path_tree->set_column_title(2, TTR("Status"));
+	save_path = memnew(EditorFileDialog);
+	save_path->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
+	HBoxContainer *extension_hb = memnew(HBoxContainer);
+	save_path->get_vbox()->add_child(extension_hb);
+	extension_hb->add_spacer();
+	extension_hb->add_child(memnew(Label(TTR("Save Extension: "))));
+	external_extension_type = memnew(OptionButton);
+	extension_hb->add_child(external_extension_type);
+	external_extension_type->add_item(TTR("Text: *.tres"));
+	external_extension_type->add_item(TTR("Binary: *.res"));
+	external_path_tree->set_hide_root(true);
+	add_child(save_path);
+
+	item_save_path = memnew(EditorFileDialog);
+	item_save_path->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
+	item_save_path->add_filter("*.tres;Text Resource");
+	item_save_path->add_filter("*.res;Binary Resource");
+	add_child(item_save_path);
+	item_save_path->connect("file_selected", callable_mp(this, &SceneImportSettings::_save_path_changed));
+
+	save_path->connect("dir_selected", callable_mp(this, &SceneImportSettings::_save_dir_callback));
+}
+
+SceneImportSettings::~SceneImportSettings() {
+	memdelete(scene_import_settings_data);
+}

+ 199 - 0
editor/import/scene_import_settings.h

@@ -0,0 +1,199 @@
+/*************************************************************************/
+/*  scene_import_settings.h                                              */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef SCENEIMPORTSETTINGS_H
+#define SCENEIMPORTSETTINGS_H
+
+#include "editor/editor_file_dialog.h"
+#include "editor/editor_inspector.h"
+#include "editor/import/resource_importer_scene.h"
+#include "scene/3d/camera_3d.h"
+#include "scene/3d/light_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/item_list.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/subviewport_container.h"
+#include "scene/gui/tab_container.h"
+#include "scene/gui/tree.h"
+#include "scene/resources/primitive_meshes.h"
+
+class SceneImportSettingsData;
+
+class SceneImportSettings : public ConfirmationDialog {
+	GDCLASS(SceneImportSettings, ConfirmationDialog)
+
+	static SceneImportSettings *singleton;
+
+	enum Actions {
+		ACTION_EXTRACT_MATERIALS,
+		ACTION_CHOOSE_MESH_SAVE_PATHS,
+		ACTION_CHOOSE_ANIMATION_SAVE_PATHS,
+	};
+
+	Node *scene = nullptr;
+
+	HSplitContainer *tree_split;
+	HSplitContainer *property_split;
+	TabContainer *data_mode;
+	Tree *scene_tree;
+	Tree *mesh_tree;
+	Tree *material_tree;
+
+	EditorInspector *inspector;
+
+	SubViewport *base_viewport;
+
+	Camera3D *camera;
+	bool first_aabb = false;
+	AABB contents_aabb;
+
+	DirectionalLight3D *light;
+	Ref<ArrayMesh> selection_mesh;
+	MeshInstance3D *node_selected;
+
+	MeshInstance3D *mesh_preview;
+	Ref<SphereMesh> material_preview;
+
+	float cam_rot_x;
+	float cam_rot_y;
+	float cam_zoom;
+
+	void _update_scene();
+
+	struct MaterialData {
+		bool has_import_id;
+		Ref<Material> material;
+		TreeItem *scene_node;
+		TreeItem *mesh_node;
+		TreeItem *material_node;
+
+		float cam_rot_x = -Math_PI / 4;
+		float cam_rot_y = -Math_PI / 4;
+		float cam_zoom = 1;
+
+		Map<StringName, Variant> settings;
+	};
+	Map<String, MaterialData> material_map;
+
+	struct MeshData {
+		bool has_import_id;
+		Ref<Mesh> mesh;
+		TreeItem *scene_node;
+		TreeItem *mesh_node;
+
+		float cam_rot_x = -Math_PI / 4;
+		float cam_rot_y = -Math_PI / 4;
+		float cam_zoom = 1;
+		Map<StringName, Variant> settings;
+	};
+	Map<String, MeshData> mesh_map;
+
+	struct AnimationData {
+		Ref<Animation> animation;
+		TreeItem *scene_node;
+		Map<StringName, Variant> settings;
+	};
+	Map<String, AnimationData> animation_map;
+
+	struct NodeData {
+		Node *node;
+		TreeItem *scene_node;
+		Map<StringName, Variant> settings;
+	};
+	Map<String, NodeData> node_map;
+
+	void _fill_material(Tree *p_tree, const Ref<Material> &p_material, TreeItem *p_parent);
+	void _fill_mesh(Tree *p_tree, const Ref<Mesh> &p_mesh, TreeItem *p_parent);
+	void _fill_animation(Tree *p_tree, const Ref<Animation> &p_anim, const String &p_name, TreeItem *p_parent);
+	void _fill_scene(Node *p_node, TreeItem *p_parent_item);
+
+	Set<Ref<Mesh>> mesh_set;
+	Set<Ref<Material>> material_set;
+
+	String selected_type;
+	String selected_id;
+
+	bool selecting = false;
+
+	void _update_camera();
+	void _select(Tree *p_from, String p_type, String p_id);
+	void _material_tree_selected();
+	void _mesh_tree_selected();
+	void _scene_tree_selected();
+
+	void _viewport_input(const Ref<InputEvent> &p_input);
+
+	Map<StringName, Variant> defaults;
+
+	SceneImportSettingsData *scene_import_settings_data;
+
+	void _re_import();
+
+	String base_path;
+
+	MenuButton *action_menu;
+
+	ConfirmationDialog *external_paths;
+	Tree *external_path_tree;
+	EditorFileDialog *save_path;
+	OptionButton *external_extension_type;
+
+	EditorFileDialog *item_save_path;
+
+	void _menu_callback(int p_id);
+	void _save_dir_callback(const String &p_path);
+
+	int current_action;
+
+	Vector<TreeItem *> save_path_items;
+
+	TreeItem *save_path_item = nullptr;
+	void _save_path_changed(const String &p_path);
+	void _browse_save_callback(Object *p_item, int p_column, int p_id);
+	void _save_dir_confirm();
+
+	Dictionary base_subresource_settings;
+
+	void _load_default_subresource_settings(Map<StringName, Variant> &settings, const String &p_type, const String &p_import_id, ResourceImporterScene::InternalImportCategory p_category);
+
+protected:
+	void _notification(int p_what);
+
+public:
+	void open_settings(const String &p_path);
+	static SceneImportSettings *get_singleton();
+	SceneImportSettings();
+	~SceneImportSettings();
+};
+
+#endif // SCENEIMPORTSETTINGS_H

+ 353 - 2
editor/import/scene_importer_mesh.cpp

@@ -136,6 +136,11 @@ Ref<Material> EditorSceneImporterMesh::get_surface_material(int p_surface) const
 	return surfaces[p_surface].material;
 	return surfaces[p_surface].material;
 }
 }
 
 
+void EditorSceneImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_material) {
+	ERR_FAIL_INDEX(p_surface, surfaces.size());
+	surfaces.write[p_surface].material = p_material;
+}
+
 void EditorSceneImporterMesh::generate_lods() {
 void EditorSceneImporterMesh::generate_lods() {
 	if (!SurfaceTool::simplify_func) {
 	if (!SurfaceTool::simplify_func) {
 		return;
 		return;
@@ -219,11 +224,20 @@ bool EditorSceneImporterMesh::has_mesh() const {
 	return mesh.is_valid();
 	return mesh.is_valid();
 }
 }
 
 
-Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh() {
+Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh(const Ref<Mesh> &p_base) {
 	ERR_FAIL_COND_V(surfaces.size() == 0, Ref<ArrayMesh>());
 	ERR_FAIL_COND_V(surfaces.size() == 0, Ref<ArrayMesh>());
 
 
 	if (mesh.is_null()) {
 	if (mesh.is_null()) {
-		mesh.instance();
+		if (p_base.is_valid()) {
+			mesh = p_base;
+		}
+		if (mesh.is_null()) {
+			mesh.instance();
+		}
+		mesh->set_name(get_name());
+		if (has_meta("import_id")) {
+			mesh->set_meta("import_id", get_meta("import_id"));
+		}
 		for (int i = 0; i < blend_shapes.size(); i++) {
 		for (int i = 0; i < blend_shapes.size(); i++) {
 			mesh->add_blend_shape(blend_shapes[i]);
 			mesh->add_blend_shape(blend_shapes[i]);
 		}
 		}
@@ -251,6 +265,8 @@ Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh() {
 			}
 			}
 		}
 		}
 
 
+		mesh->set_lightmap_size_hint(lightmap_size_hint);
+
 		if (shadow_mesh.is_valid()) {
 		if (shadow_mesh.is_valid()) {
 			Ref<ArrayMesh> shadow = shadow_mesh->get_mesh();
 			Ref<ArrayMesh> shadow = shadow_mesh->get_mesh();
 			mesh->set_shadow_mesh(shadow);
 			mesh->set_shadow_mesh(shadow);
@@ -436,6 +452,338 @@ Dictionary EditorSceneImporterMesh::_get_data() const {
 	return data;
 	return data;
 }
 }
 
 
+Vector<Face3> EditorSceneImporterMesh::get_faces() const {
+	Vector<Face3> faces;
+	for (int i = 0; i < surfaces.size(); i++) {
+		if (surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) {
+			Vector<Vector3> vertices = surfaces[i].arrays[Mesh::ARRAY_VERTEX];
+			Vector<int> indices = surfaces[i].arrays[Mesh::ARRAY_INDEX];
+			if (indices.size()) {
+				for (int j = 0; j < indices.size(); j += 3) {
+					Face3 f;
+					f.vertex[0] = vertices[indices[j + 0]];
+					f.vertex[1] = vertices[indices[j + 1]];
+					f.vertex[2] = vertices[indices[j + 2]];
+					faces.push_back(f);
+				}
+			} else {
+				for (int j = 0; j < vertices.size(); j += 3) {
+					Face3 f;
+					f.vertex[0] = vertices[j + 0];
+					f.vertex[1] = vertices[j + 1];
+					f.vertex[2] = vertices[j + 2];
+					faces.push_back(f);
+				}
+			}
+		}
+	}
+
+	return faces;
+}
+
+Vector<Ref<Shape3D>> EditorSceneImporterMesh::convex_decompose() const {
+	ERR_FAIL_COND_V(!Mesh::convex_composition_function, Vector<Ref<Shape3D>>());
+
+	const Vector<Face3> faces = get_faces();
+
+	Vector<Vector<Face3>> decomposed = Mesh::convex_composition_function(faces);
+
+	Vector<Ref<Shape3D>> ret;
+
+	for (int i = 0; i < decomposed.size(); i++) {
+		Set<Vector3> points;
+		for (int j = 0; j < decomposed[i].size(); j++) {
+			points.insert(decomposed[i][j].vertex[0]);
+			points.insert(decomposed[i][j].vertex[1]);
+			points.insert(decomposed[i][j].vertex[2]);
+		}
+
+		Vector<Vector3> convex_points;
+		convex_points.resize(points.size());
+		{
+			Vector3 *w = convex_points.ptrw();
+			int idx = 0;
+			for (Set<Vector3>::Element *E = points.front(); E; E = E->next()) {
+				w[idx++] = E->get();
+			}
+		}
+
+		Ref<ConvexPolygonShape3D> shape;
+		shape.instance();
+		shape->set_points(convex_points);
+		ret.push_back(shape);
+	}
+
+	return ret;
+}
+
+Ref<Shape3D> EditorSceneImporterMesh::create_trimesh_shape() const {
+	Vector<Face3> faces = get_faces();
+	if (faces.size() == 0) {
+		return Ref<Shape3D>();
+	}
+
+	Vector<Vector3> face_points;
+	face_points.resize(faces.size() * 3);
+
+	for (int i = 0; i < face_points.size(); i += 3) {
+		Face3 f = faces.get(i / 3);
+		face_points.set(i, f.vertex[0]);
+		face_points.set(i + 1, f.vertex[1]);
+		face_points.set(i + 2, f.vertex[2]);
+	}
+
+	Ref<ConcavePolygonShape3D> shape = memnew(ConcavePolygonShape3D);
+	shape->set_faces(face_points);
+	return shape;
+}
+
+Ref<NavigationMesh> EditorSceneImporterMesh::create_navigation_mesh() {
+	Vector<Face3> faces = get_faces();
+	if (faces.size() == 0) {
+		return Ref<NavigationMesh>();
+	}
+
+	Map<Vector3, int> unique_vertices;
+	LocalVector<int> face_indices;
+
+	for (int i = 0; i < faces.size(); i++) {
+		for (int j = 0; j < 3; j++) {
+			Vector3 v = faces[i].vertex[j];
+			int idx;
+			if (unique_vertices.has(v)) {
+				idx = unique_vertices[v];
+			} else {
+				idx = unique_vertices.size();
+				unique_vertices[v] = idx;
+			}
+			face_indices.push_back(idx);
+		}
+	}
+
+	Vector<Vector3> vertices;
+	vertices.resize(unique_vertices.size());
+	for (Map<Vector3, int>::Element *E = unique_vertices.front(); E; E = E->next()) {
+		vertices.write[E->get()] = E->key();
+	}
+
+	Ref<NavigationMesh> nm;
+	nm.instance();
+	nm->set_vertices(vertices);
+
+	Vector<int> v3;
+	v3.resize(3);
+	for (uint32_t i = 0; i < face_indices.size(); i += 3) {
+		v3.write[0] = face_indices[i + 0];
+		v3.write[1] = face_indices[i + 1];
+		v3.write[2] = face_indices[i + 2];
+		nm->add_polygon(v3);
+	}
+
+	return nm;
+}
+
+extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache);
+
+struct EditorSceneImporterMeshLightmapSurface {
+	Ref<Material> material;
+	LocalVector<SurfaceTool::Vertex> vertices;
+	Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX;
+	uint32_t format = 0;
+	String name;
+};
+
+Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size) {
+	ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
+	ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes.");
+
+	Vector<float> vertices;
+	Vector<float> normals;
+	Vector<int> indices;
+	Vector<float> uv;
+	Vector<Pair<int, int>> uv_indices;
+
+	Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces;
+
+	// Keep only the scale
+	Transform transform = p_base_transform;
+	transform.origin = Vector3();
+	transform.looking_at(Vector3(1, 0, 0), Vector3(0, 1, 0));
+
+	Basis normal_basis = transform.basis.inverse().transposed();
+
+	for (int i = 0; i < get_surface_count(); i++) {
+		EditorSceneImporterMeshLightmapSurface s;
+		s.primitive = get_surface_primitive_type(i);
+
+		ERR_FAIL_COND_V_MSG(s.primitive != Mesh::PRIMITIVE_TRIANGLES, ERR_UNAVAILABLE, "Only triangles are supported for lightmap unwrap.");
+		Array arrays = get_surface_arrays(i);
+		s.material = get_surface_material(i);
+		s.name = get_surface_name(i);
+
+		SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format);
+
+		Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX];
+		int vc = rvertices.size();
+		const Vector3 *r = rvertices.ptr();
+
+		Vector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL];
+
+		ERR_FAIL_COND_V_MSG(rnormals.size() == 0, ERR_UNAVAILABLE, "Normals are required for lightmap unwrap.");
+
+		const Vector3 *rn = rnormals.ptr();
+
+		int vertex_ofs = vertices.size() / 3;
+
+		vertices.resize((vertex_ofs + vc) * 3);
+		normals.resize((vertex_ofs + vc) * 3);
+		uv_indices.resize(vertex_ofs + vc);
+
+		for (int j = 0; j < vc; j++) {
+			Vector3 v = transform.xform(r[j]);
+			Vector3 n = normal_basis.xform(rn[j]).normalized();
+
+			vertices.write[(j + vertex_ofs) * 3 + 0] = v.x;
+			vertices.write[(j + vertex_ofs) * 3 + 1] = v.y;
+			vertices.write[(j + vertex_ofs) * 3 + 2] = v.z;
+			normals.write[(j + vertex_ofs) * 3 + 0] = n.x;
+			normals.write[(j + vertex_ofs) * 3 + 1] = n.y;
+			normals.write[(j + vertex_ofs) * 3 + 2] = n.z;
+			uv_indices.write[j + vertex_ofs] = Pair<int, int>(i, j);
+		}
+
+		Vector<int> rindices = arrays[Mesh::ARRAY_INDEX];
+		int ic = rindices.size();
+
+		if (ic == 0) {
+			for (int j = 0; j < vc / 3; j++) {
+				if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate()) {
+					continue;
+				}
+
+				indices.push_back(vertex_ofs + j * 3 + 0);
+				indices.push_back(vertex_ofs + j * 3 + 1);
+				indices.push_back(vertex_ofs + j * 3 + 2);
+			}
+
+		} else {
+			const int *ri = rindices.ptr();
+
+			for (int j = 0; j < ic / 3; j++) {
+				if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate()) {
+					continue;
+				}
+				indices.push_back(vertex_ofs + ri[j * 3 + 0]);
+				indices.push_back(vertex_ofs + ri[j * 3 + 1]);
+				indices.push_back(vertex_ofs + ri[j * 3 + 2]);
+			}
+		}
+
+		lightmap_surfaces.push_back(s);
+	}
+
+	//unwrap
+
+	float *gen_uvs;
+	int *gen_vertices;
+	int *gen_indices;
+	int gen_vertex_count;
+	int gen_index_count;
+	int size_x;
+	int size_y;
+
+	bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y, r_cache_data, r_cache_size, r_used_cache);
+
+	if (!ok) {
+		return ERR_CANT_CREATE;
+	}
+
+	//remove surfaces
+	clear();
+
+	//create surfacetools for each surface..
+	Vector<Ref<SurfaceTool>> surfaces_tools;
+
+	for (int i = 0; i < lightmap_surfaces.size(); i++) {
+		Ref<SurfaceTool> st;
+		st.instance();
+		st->begin(Mesh::PRIMITIVE_TRIANGLES);
+		st->set_material(lightmap_surfaces[i].material);
+		st->set_meta("name", lightmap_surfaces[i].name);
+		surfaces_tools.push_back(st); //stay there
+	}
+
+	print_verbose("Mesh: Gen indices: " + itos(gen_index_count));
+	//go through all indices
+	for (int i = 0; i < gen_index_count; i += 3) {
+		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_indices.size(), ERR_BUG);
+		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_indices.size(), ERR_BUG);
+		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_indices.size(), ERR_BUG);
+
+		ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);
+
+		int surface = uv_indices[gen_vertices[gen_indices[i + 0]]].first;
+
+		for (int j = 0; j < 3; j++) {
+			SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second];
+
+			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_COLOR) {
+				surfaces_tools.write[surface]->set_color(v.color);
+			}
+			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TEX_UV) {
+				surfaces_tools.write[surface]->set_uv(v.uv);
+			}
+			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_NORMAL) {
+				surfaces_tools.write[surface]->set_normal(v.normal);
+			}
+			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TANGENT) {
+				Plane t;
+				t.normal = v.tangent;
+				t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
+				surfaces_tools.write[surface]->set_tangent(t);
+			}
+			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_BONES) {
+				surfaces_tools.write[surface]->set_bones(v.bones);
+			}
+			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) {
+				surfaces_tools.write[surface]->set_weights(v.weights);
+			}
+
+			Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
+			surfaces_tools.write[surface]->set_uv2(uv2);
+
+			surfaces_tools.write[surface]->add_vertex(v.vertex);
+		}
+	}
+
+	//generate surfaces
+
+	for (int i = 0; i < surfaces_tools.size(); i++) {
+		surfaces_tools.write[i]->index();
+		Array arrays = surfaces_tools.write[i]->commit_to_arrays();
+		add_surface(surfaces_tools.write[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools.write[i]->get_material(), surfaces_tools.write[i]->get_meta("name"));
+	}
+
+	set_lightmap_size_hint(Size2(size_x, size_y));
+
+	if (!r_used_cache) {
+		//free stuff
+		::free(gen_vertices);
+		::free(gen_indices);
+		::free(gen_uvs);
+	}
+
+	return OK;
+}
+
+void EditorSceneImporterMesh::set_lightmap_size_hint(const Size2i &p_size) {
+	lightmap_size_hint = p_size;
+}
+
+Size2i EditorSceneImporterMesh::get_lightmap_size_hint() const {
+	return lightmap_size_hint;
+}
+
 void EditorSceneImporterMesh::_bind_methods() {
 void EditorSceneImporterMesh::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &EditorSceneImporterMesh::add_blend_shape);
 	ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &EditorSceneImporterMesh::add_blend_shape);
 	ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &EditorSceneImporterMesh::get_blend_shape_count);
 	ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &EditorSceneImporterMesh::get_blend_shape_count);
@@ -462,5 +810,8 @@ void EditorSceneImporterMesh::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_set_data", "data"), &EditorSceneImporterMesh::_set_data);
 	ClassDB::bind_method(D_METHOD("_set_data", "data"), &EditorSceneImporterMesh::_set_data);
 	ClassDB::bind_method(D_METHOD("_get_data"), &EditorSceneImporterMesh::_get_data);
 	ClassDB::bind_method(D_METHOD("_get_data"), &EditorSceneImporterMesh::_get_data);
 
 
+	ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &EditorSceneImporterMesh::set_lightmap_size_hint);
+	ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &EditorSceneImporterMesh::get_lightmap_size_hint);
+
 	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data");
 	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data");
 }
 }

+ 17 - 1
editor/import/scene_importer_mesh.h

@@ -32,7 +32,10 @@
 #define EDITOR_SCENE_IMPORTER_MESH_H
 #define EDITOR_SCENE_IMPORTER_MESH_H
 
 
 #include "core/io/resource.h"
 #include "core/io/resource.h"
+#include "scene/resources/concave_polygon_shape_3d.h"
+#include "scene/resources/convex_polygon_shape_3d.h"
 #include "scene/resources/mesh.h"
 #include "scene/resources/mesh.h"
+#include "scene/resources/navigation_mesh.h"
 // The following classes are used by importers instead of ArrayMesh and MeshInstance3D
 // The following classes are used by importers instead of ArrayMesh and MeshInstance3D
 // so the data is not registered (hence, quality loss), importing happens faster and
 // so the data is not registered (hence, quality loss), importing happens faster and
 // its easier to modify before saving
 // its easier to modify before saving
@@ -63,6 +66,8 @@ class EditorSceneImporterMesh : public Resource {
 
 
 	Ref<EditorSceneImporterMesh> shadow_mesh;
 	Ref<EditorSceneImporterMesh> shadow_mesh;
 
 
+	Size2i lightmap_size_hint;
+
 protected:
 protected:
 	void _set_data(const Dictionary &p_data);
 	void _set_data(const Dictionary &p_data);
 	Dictionary _get_data() const;
 	Dictionary _get_data() const;
@@ -89,13 +94,24 @@ public:
 	float get_surface_lod_size(int p_surface, int p_lod) const;
 	float get_surface_lod_size(int p_surface, int p_lod) const;
 	Ref<Material> get_surface_material(int p_surface) const;
 	Ref<Material> get_surface_material(int p_surface) const;
 
 
+	void set_surface_material(int p_surface, const Ref<Material> &p_material);
+
 	void generate_lods();
 	void generate_lods();
 
 
 	void create_shadow_mesh();
 	void create_shadow_mesh();
 	Ref<EditorSceneImporterMesh> get_shadow_mesh() const;
 	Ref<EditorSceneImporterMesh> get_shadow_mesh() const;
 
 
+	Vector<Face3> get_faces() const;
+	Vector<Ref<Shape3D>> convex_decompose() const;
+	Ref<Shape3D> create_trimesh_shape() const;
+	Ref<NavigationMesh> create_navigation_mesh();
+	Error lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size);
+
+	void set_lightmap_size_hint(const Size2i &p_size);
+	Size2i get_lightmap_size_hint() const;
+
 	bool has_mesh() const;
 	bool has_mesh() const;
-	Ref<ArrayMesh> get_mesh();
+	Ref<ArrayMesh> get_mesh(const Ref<Mesh> &p_base = Ref<Mesh>());
 	void clear();
 	void clear();
 };
 };
 #endif // EDITOR_SCENE_IMPORTER_MESH_H
 #endif // EDITOR_SCENE_IMPORTER_MESH_H

+ 38 - 0
editor/import_dock.cpp

@@ -156,6 +156,14 @@ void ImportDock::_update_options(const Ref<ConfigFile> &p_config) {
 
 
 	params->update();
 	params->update();
 	_update_preset_menu();
 	_update_preset_menu();
+
+	if (params->paths.size() == 1 && params->importer->has_advanced_options()) {
+		advanced->show();
+		advanced_spacer->show();
+	} else {
+		advanced->hide();
+		advanced_spacer->hide();
+	}
 }
 }
 
 
 void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) {
 void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) {
@@ -258,6 +266,14 @@ void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) {
 	preset->set_disabled(false);
 	preset->set_disabled(false);
 
 
 	imported->set_text(vformat(TTR("%d Files"), p_paths.size()));
 	imported->set_text(vformat(TTR("%d Files"), p_paths.size()));
+
+	if (params->paths.size() == 1 && params->importer->has_advanced_options()) {
+		advanced->show();
+		advanced_spacer->show();
+	} else {
+		advanced->hide();
+		advanced_spacer->hide();
+	}
 }
 }
 
 
 void ImportDock::_update_preset_menu() {
 void ImportDock::_update_preset_menu() {
@@ -422,6 +438,11 @@ void ImportDock::_reimport_and_restart() {
 	EditorNode::get_singleton()->restart_editor();
 	EditorNode::get_singleton()->restart_editor();
 }
 }
 
 
+void ImportDock::_advanced_options() {
+	if (params->paths.size() == 1 && params->importer.is_valid()) {
+		params->importer->show_advanced_options(params->paths[0]);
+	}
+}
 void ImportDock::_reimport() {
 void ImportDock::_reimport() {
 	for (int i = 0; i < params->paths.size(); i++) {
 	for (int i = 0; i < params->paths.size(); i++) {
 		Ref<ConfigFile> config;
 		Ref<ConfigFile> config;
@@ -531,10 +552,27 @@ ImportDock::ImportDock() {
 	import->set_text(TTR("Reimport"));
 	import->set_text(TTR("Reimport"));
 	import->set_disabled(true);
 	import->set_disabled(true);
 	import->connect("pressed", callable_mp(this, &ImportDock::_reimport_attempt));
 	import->connect("pressed", callable_mp(this, &ImportDock::_reimport_attempt));
+	if (!DisplayServer::get_singleton()->get_swap_cancel_ok()) {
+		advanced_spacer = hb->add_spacer();
+		advanced = memnew(Button);
+		advanced->set_text(TTR("Advanced..."));
+		hb->add_child(advanced);
+	}
 	hb->add_spacer();
 	hb->add_spacer();
 	hb->add_child(import);
 	hb->add_child(import);
 	hb->add_spacer();
 	hb->add_spacer();
 
 
+	if (DisplayServer::get_singleton()->get_swap_cancel_ok()) {
+		advanced = memnew(Button);
+		advanced->set_text(TTR("Advanced..."));
+		hb->add_child(advanced);
+		advanced_spacer = hb->add_spacer();
+	}
+
+	advanced->hide();
+	advanced_spacer->hide();
+	advanced->connect("pressed", callable_mp(this, &ImportDock::_advanced_options));
+
 	reimport_confirm = memnew(ConfirmationDialog);
 	reimport_confirm = memnew(ConfirmationDialog);
 	reimport_confirm->get_ok_button()->set_text(TTR("Save Scenes, Re-Import, and Restart"));
 	reimport_confirm->get_ok_button()->set_text(TTR("Save Scenes, Re-Import, and Restart"));
 	add_child(reimport_confirm);
 	add_child(reimport_confirm);

+ 4 - 0
editor/import_dock.h

@@ -57,6 +57,9 @@ class ImportDock : public VBoxContainer {
 	Label *label_warning;
 	Label *label_warning;
 	Button *import;
 	Button *import;
 
 
+	Control *advanced_spacer;
+	Button *advanced;
+
 	ImportDockParameters *params;
 	ImportDockParameters *params;
 
 
 	void _preset_selected(int p_idx);
 	void _preset_selected(int p_idx);
@@ -69,6 +72,7 @@ class ImportDock : public VBoxContainer {
 	void _reimport_and_restart();
 	void _reimport_and_restart();
 	void _reimport();
 	void _reimport();
 
 
+	void _advanced_options();
 	enum {
 	enum {
 		ITEM_SET_AS_DEFAULT = 100,
 		ITEM_SET_AS_DEFAULT = 100,
 		ITEM_LOAD_DEFAULT,
 		ITEM_LOAD_DEFAULT,

+ 1 - 1
modules/fbx/editor_scene_importer_fbx.cpp

@@ -628,7 +628,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
 						mesh_data_precached->mesh_node = fbx_node;
 						mesh_data_precached->mesh_node = fbx_node;
 
 
 						// mesh node, mesh id
 						// mesh node, mesh id
-						mesh_node = mesh_data_precached->create_fbx_mesh(state, mesh_geometry, fbx_node->fbx_model, (p_flags & IMPORT_USE_COMPRESSION) != 0);
+						mesh_node = mesh_data_precached->create_fbx_mesh(state, mesh_geometry, fbx_node->fbx_model, 0);
 						if (!state.MeshNodes.has(mesh_id)) {
 						if (!state.MeshNodes.has(mesh_id)) {
 							state.MeshNodes.insert(mesh_id, fbx_node);
 							state.MeshNodes.insert(mesh_id, fbx_node);
 						}
 						}

+ 3 - 1
modules/gltf/editor_scene_importer_gltf.cpp

@@ -99,7 +99,9 @@ Node *PackedSceneGLTF::import_scene(const String &p_path, uint32_t p_flags,
 	Ref<GLTFDocument> gltf_document;
 	Ref<GLTFDocument> gltf_document;
 	gltf_document.instance();
 	gltf_document.instance();
 	Error err = gltf_document->parse(r_state, p_path);
 	Error err = gltf_document->parse(r_state, p_path);
-	*r_err = err;
+	if (r_err) {
+		*r_err = err;
+	}
 	ERR_FAIL_COND_V(err != Error::OK, nullptr);
 	ERR_FAIL_COND_V(err != Error::OK, nullptr);
 
 
 	Node3D *root = memnew(Node3D);
 	Node3D *root = memnew(Node3D);

+ 3 - 1
scene/gui/box_container.cpp

@@ -313,7 +313,7 @@ BoxContainer::AlignMode BoxContainer::get_alignment() const {
 	return align;
 	return align;
 }
 }
 
 
-void BoxContainer::add_spacer(bool p_begin) {
+Control *BoxContainer::add_spacer(bool p_begin) {
 	Control *c = memnew(Control);
 	Control *c = memnew(Control);
 	c->set_mouse_filter(MOUSE_FILTER_PASS); //allow spacer to pass mouse events
 	c->set_mouse_filter(MOUSE_FILTER_PASS); //allow spacer to pass mouse events
 
 
@@ -327,6 +327,8 @@ void BoxContainer::add_spacer(bool p_begin) {
 	if (p_begin) {
 	if (p_begin) {
 		move_child(c, 0);
 		move_child(c, 0);
 	}
 	}
+
+	return c;
 }
 }
 
 
 BoxContainer::BoxContainer(bool p_vertical) {
 BoxContainer::BoxContainer(bool p_vertical) {

+ 1 - 1
scene/gui/box_container.h

@@ -55,7 +55,7 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
-	void add_spacer(bool p_begin = false);
+	Control *add_spacer(bool p_begin = false);
 
 
 	void set_alignment(AlignMode p_align);
 	void set_alignment(AlignMode p_align);
 	AlignMode get_alignment() const;
 	AlignMode get_alignment() const;

+ 10 - 0
scene/gui/tree.cpp

@@ -410,6 +410,14 @@ bool TreeItem::is_collapsed() {
 	return collapsed;
 	return collapsed;
 }
 }
 
 
+void TreeItem::uncollapse_tree() {
+	TreeItem *t = this;
+	while (t) {
+		t->set_collapsed(false);
+		t = t->parent;
+	}
+}
+
 void TreeItem::set_custom_minimum_height(int p_height) {
 void TreeItem::set_custom_minimum_height(int p_height) {
 	custom_min_height = p_height;
 	custom_min_height = p_height;
 	_changed_notify();
 	_changed_notify();
@@ -842,6 +850,8 @@ void TreeItem::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_collapsed", "enable"), &TreeItem::set_collapsed);
 	ClassDB::bind_method(D_METHOD("set_collapsed", "enable"), &TreeItem::set_collapsed);
 	ClassDB::bind_method(D_METHOD("is_collapsed"), &TreeItem::is_collapsed);
 	ClassDB::bind_method(D_METHOD("is_collapsed"), &TreeItem::is_collapsed);
 
 
+	ClassDB::bind_method(D_METHOD("uncollapse_tree"), &TreeItem::uncollapse_tree);
+
 	ClassDB::bind_method(D_METHOD("set_custom_minimum_height", "height"), &TreeItem::set_custom_minimum_height);
 	ClassDB::bind_method(D_METHOD("set_custom_minimum_height", "height"), &TreeItem::set_custom_minimum_height);
 	ClassDB::bind_method(D_METHOD("get_custom_minimum_height"), &TreeItem::get_custom_minimum_height);
 	ClassDB::bind_method(D_METHOD("get_custom_minimum_height"), &TreeItem::get_custom_minimum_height);
 
 

+ 2 - 0
scene/gui/tree.h

@@ -229,6 +229,8 @@ public:
 	void set_collapsed(bool p_collapsed);
 	void set_collapsed(bool p_collapsed);
 	bool is_collapsed();
 	bool is_collapsed();
 
 
+	void uncollapse_tree();
+
 	void set_custom_minimum_height(int p_height);
 	void set_custom_minimum_height(int p_height);
 	int get_custom_minimum_height() const;
 	int get_custom_minimum_height() const;
 
 

+ 2 - 1
scene/main/window.cpp

@@ -893,12 +893,13 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) {
 	}
 	}
 
 
 	if (exclusive_child != nullptr) {
 	if (exclusive_child != nullptr) {
+		/*
 		Window *focus_target = exclusive_child;
 		Window *focus_target = exclusive_child;
 		focus_target->grab_focus();
 		focus_target->grab_focus();
 		while (focus_target->exclusive_child != nullptr) {
 		while (focus_target->exclusive_child != nullptr) {
 			focus_target = focus_target->exclusive_child;
 			focus_target = focus_target->exclusive_child;
 			focus_target->grab_focus();
 			focus_target->grab_focus();
-		}
+		}*/
 
 
 		if (!is_embedding_subwindows()) { //not embedding, no need for event
 		if (!is_embedding_subwindows()) { //not embedding, no need for event
 			return;
 			return;

+ 9 - 0
scene/resources/surface_tool.cpp

@@ -1059,6 +1059,10 @@ void SurfaceTool::set_material(const Ref<Material> &p_material) {
 	material = p_material;
 	material = p_material;
 }
 }
 
 
+Ref<Material> SurfaceTool::get_material() const {
+	return material;
+}
+
 void SurfaceTool::clear() {
 void SurfaceTool::clear() {
 	begun = false;
 	begun = false;
 	primitive = Mesh::PRIMITIVE_LINES;
 	primitive = Mesh::PRIMITIVE_LINES;
@@ -1088,6 +1092,10 @@ void SurfaceTool::set_custom_format(int p_index, CustomFormat p_format) {
 	ERR_FAIL_COND(begun);
 	ERR_FAIL_COND(begun);
 	last_custom_format[p_index] = p_format;
 	last_custom_format[p_index] = p_format;
 }
 }
+
+Mesh::PrimitiveType SurfaceTool::get_primitive() const {
+	return primitive;
+}
 SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_index) const {
 SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_index) const {
 	ERR_FAIL_INDEX_V(p_index, RS::ARRAY_CUSTOM_COUNT, CUSTOM_MAX);
 	ERR_FAIL_INDEX_V(p_index, RS::ARRAY_CUSTOM_COUNT, CUSTOM_MAX);
 	return last_custom_format[p_index];
 	return last_custom_format[p_index];
@@ -1174,6 +1182,7 @@ void SurfaceTool::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("generate_lod", "nd_threshold", "target_index_count"), &SurfaceTool::generate_lod, DEFVAL(3));
 	ClassDB::bind_method(D_METHOD("generate_lod", "nd_threshold", "target_index_count"), &SurfaceTool::generate_lod, DEFVAL(3));
 
 
 	ClassDB::bind_method(D_METHOD("set_material", "material"), &SurfaceTool::set_material);
 	ClassDB::bind_method(D_METHOD("set_material", "material"), &SurfaceTool::set_material);
+	ClassDB::bind_method(D_METHOD("get_primitive"), &SurfaceTool::get_primitive);
 
 
 	ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear);
 	ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear);
 
 

+ 3 - 0
scene/resources/surface_tool.h

@@ -143,6 +143,8 @@ public:
 	void set_custom_format(int p_index, CustomFormat p_format);
 	void set_custom_format(int p_index, CustomFormat p_format);
 	CustomFormat get_custom_format(int p_index) const;
 	CustomFormat get_custom_format(int p_index) const;
 
 
+	Mesh::PrimitiveType get_primitive() const;
+
 	void begin(Mesh::PrimitiveType p_primitive);
 	void begin(Mesh::PrimitiveType p_primitive);
 
 
 	void set_color(Color p_color);
 	void set_color(Color p_color);
@@ -171,6 +173,7 @@ public:
 	Vector<int> generate_lod(float p_threshold, int p_target_index_count = 3);
 	Vector<int> generate_lod(float p_threshold, int p_target_index_count = 3);
 
 
 	void set_material(const Ref<Material> &p_material);
 	void set_material(const Ref<Material> &p_material);
+	Ref<Material> get_material() const;
 
 
 	void clear();
 	void clear();
 
 

Some files were not shown because too many files changed in this diff