Path: blob/master/editor/import/3d/resource_importer_scene.cpp
21025 views
/**************************************************************************/1/* resource_importer_scene.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "resource_importer_scene.h"3132#include "core/error/error_macros.h"33#include "core/io/dir_access.h"34#include "core/io/resource_saver.h"35#include "core/object/script_language.h"36#include "editor/editor_interface.h"37#include "editor/editor_node.h"38#include "editor/import/3d/scene_import_settings.h"39#include "editor/settings/editor_settings.h"40#include "scene/3d/importer_mesh_instance_3d.h"41#include "scene/3d/mesh_instance_3d.h"42#include "scene/3d/navigation/navigation_region_3d.h"43#include "scene/3d/occluder_instance_3d.h"44#include "scene/3d/physics/area_3d.h"45#include "scene/3d/physics/collision_shape_3d.h"46#include "scene/3d/physics/static_body_3d.h"47#include "scene/3d/physics/vehicle_body_3d.h"48#include "scene/animation/animation_player.h"49#include "scene/resources/3d/box_shape_3d.h"50#include "scene/resources/3d/importer_mesh.h"51#include "scene/resources/3d/separation_ray_shape_3d.h"52#include "scene/resources/3d/sphere_shape_3d.h"53#include "scene/resources/3d/world_boundary_shape_3d.h"54#include "scene/resources/animation.h"55#include "scene/resources/bone_map.h"56#include "scene/resources/packed_scene.h"57#include "scene/resources/resource_format_text.h"5859void EditorSceneFormatImporter::get_extensions(List<String> *r_extensions) const {60Vector<String> arr;61GDVIRTUAL_CALL(_get_extensions, arr);62for (int i = 0; i < arr.size(); i++) {63r_extensions->push_back(arr[i]);64}65}6667Node *EditorSceneFormatImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {68Dictionary options_dict;69for (const KeyValue<StringName, Variant> &elem : p_options) {70options_dict[elem.key] = elem.value;71}72Object *ret = nullptr;73GDVIRTUAL_CALL(_import_scene, p_path, p_flags, options_dict, ret);74return Object::cast_to<Node>(ret);75}7677void EditorSceneFormatImporter::add_import_option(const String &p_name, const Variant &p_default_value) {78ERR_FAIL_NULL_MSG(current_option_list, "add_import_option() can only be called from get_import_options().");79add_import_option_advanced(p_default_value.get_type(), p_name, p_default_value);80}8182void EditorSceneFormatImporter::add_import_option_advanced(Variant::Type p_type, const String &p_name, const Variant &p_default_value, PropertyHint p_hint, const String &p_hint_string, int p_usage_flags) {83ERR_FAIL_NULL_MSG(current_option_list, "add_import_option_advanced() can only be called from get_import_options().");84current_option_list->push_back(ResourceImporter::ImportOption(PropertyInfo(p_type, p_name, p_hint, p_hint_string, p_usage_flags), p_default_value));85}8687void EditorSceneFormatImporter::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) {88current_option_list = r_options;89GDVIRTUAL_CALL(_get_import_options, p_path);90current_option_list = nullptr;91}9293Variant EditorSceneFormatImporter::get_option_visibility(const String &p_path, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) {94Variant ret;95// For compatibility with the old API, pass the import type as a boolean.96GDVIRTUAL_CALL(_get_option_visibility, p_path, p_scene_import_type == "AnimationLibrary", p_option, ret);97return ret;98}99100void EditorSceneFormatImporter::_bind_methods() {101ClassDB::bind_method(D_METHOD("add_import_option", "name", "value"), &EditorSceneFormatImporter::add_import_option);102ClassDB::bind_method(D_METHOD("add_import_option_advanced", "type", "name", "default_value", "hint", "hint_string", "usage_flags"), &EditorSceneFormatImporter::add_import_option_advanced, DEFVAL(PROPERTY_HINT_NONE), DEFVAL(""), DEFVAL(PROPERTY_USAGE_DEFAULT));103104GDVIRTUAL_BIND(_get_extensions);105GDVIRTUAL_BIND(_import_scene, "path", "flags", "options");106GDVIRTUAL_BIND(_get_import_options, "path");107GDVIRTUAL_BIND(_get_option_visibility, "path", "for_animation", "option");108109BIND_CONSTANT(IMPORT_SCENE);110BIND_CONSTANT(IMPORT_ANIMATION);111BIND_CONSTANT(IMPORT_FAIL_ON_MISSING_DEPENDENCIES);112BIND_CONSTANT(IMPORT_GENERATE_TANGENT_ARRAYS);113BIND_CONSTANT(IMPORT_USE_NAMED_SKIN_BINDS);114BIND_CONSTANT(IMPORT_DISCARD_MESHES_AND_MATERIALS);115BIND_CONSTANT(IMPORT_FORCE_DISABLE_MESH_COMPRESSION);116}117118/////////////////////////////////119void EditorScenePostImport::_bind_methods() {120GDVIRTUAL_BIND(_post_import, "scene")121ClassDB::bind_method(D_METHOD("get_source_file"), &EditorScenePostImport::get_source_file);122}123124Node *EditorScenePostImport::post_import(Node *p_scene) {125Object *ret;126if (GDVIRTUAL_CALL(_post_import, p_scene, ret)) {127return Object::cast_to<Node>(ret);128}129130return p_scene;131}132133String EditorScenePostImport::get_source_file() const {134return source_file;135}136137void EditorScenePostImport::init(const String &p_source_file) {138source_file = p_source_file;139}140141///////////////////////////////////////////////////////142143Variant EditorScenePostImportPlugin::get_option_value(const StringName &p_name) const {144ERR_FAIL_COND_V_MSG(current_options == nullptr && current_options_dict == nullptr, Variant(), "get_option_value called from a function where option values are not available.");145ERR_FAIL_COND_V_MSG(current_options && !current_options->has(p_name), Variant(), "get_option_value called with unexisting option argument: " + String(p_name));146ERR_FAIL_COND_V_MSG(current_options_dict && !current_options_dict->has(p_name), Variant(), "get_option_value called with unexisting option argument: " + String(p_name));147if (current_options && current_options->has(p_name)) {148return (*current_options)[p_name];149}150if (current_options_dict && current_options_dict->has(p_name)) {151return (*current_options_dict)[p_name];152}153return Variant();154}155void EditorScenePostImportPlugin::add_import_option(const String &p_name, const Variant &p_default_value) {156ERR_FAIL_NULL_MSG(current_option_list, "add_import_option() can only be called from get_import_options().");157add_import_option_advanced(p_default_value.get_type(), p_name, p_default_value);158}159void EditorScenePostImportPlugin::add_import_option_advanced(Variant::Type p_type, const String &p_name, const Variant &p_default_value, PropertyHint p_hint, const String &p_hint_string, int p_usage_flags) {160ERR_FAIL_NULL_MSG(current_option_list, "add_import_option_advanced() can only be called from get_import_options().");161current_option_list->push_back(ResourceImporter::ImportOption(PropertyInfo(p_type, p_name, p_hint, p_hint_string, p_usage_flags), p_default_value));162}163164void EditorScenePostImportPlugin::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {165current_option_list = r_options;166GDVIRTUAL_CALL(_get_internal_import_options, p_category);167current_option_list = nullptr;168}169170Variant EditorScenePostImportPlugin::get_internal_option_visibility(InternalImportCategory p_category, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) const {171current_options = &p_options;172Variant ret;173// For compatibility with the old API, pass the import type as a boolean.174GDVIRTUAL_CALL(_get_internal_option_visibility, p_category, p_scene_import_type == "AnimationLibrary", p_option, ret);175current_options = nullptr;176return ret;177}178179Variant EditorScenePostImportPlugin::get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const {180current_options = &p_options;181Variant ret;182GDVIRTUAL_CALL(_get_internal_option_update_view_required, p_category, p_option, ret);183current_options = nullptr;184return ret;185}186187void EditorScenePostImportPlugin::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) {188current_options_dict = &p_options;189GDVIRTUAL_CALL(_internal_process, p_category, p_base_scene, p_node, p_resource);190current_options_dict = nullptr;191}192193void EditorScenePostImportPlugin::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) {194current_option_list = r_options;195GDVIRTUAL_CALL(_get_import_options, p_path);196current_option_list = nullptr;197}198Variant EditorScenePostImportPlugin::get_option_visibility(const String &p_path, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) const {199current_options = &p_options;200Variant ret;201GDVIRTUAL_CALL(_get_option_visibility, p_path, p_scene_import_type == "AnimationLibrary", p_option, ret);202current_options = nullptr;203return ret;204}205206void EditorScenePostImportPlugin::pre_process(Node *p_scene, const HashMap<StringName, Variant> &p_options) {207current_options = &p_options;208GDVIRTUAL_CALL(_pre_process, p_scene);209current_options = nullptr;210}211void EditorScenePostImportPlugin::post_process(Node *p_scene, const HashMap<StringName, Variant> &p_options) {212current_options = &p_options;213GDVIRTUAL_CALL(_post_process, p_scene);214current_options = nullptr;215}216217void EditorScenePostImportPlugin::_bind_methods() {218ClassDB::bind_method(D_METHOD("get_option_value", "name"), &EditorScenePostImportPlugin::get_option_value);219220ClassDB::bind_method(D_METHOD("add_import_option", "name", "value"), &EditorScenePostImportPlugin::add_import_option);221ClassDB::bind_method(D_METHOD("add_import_option_advanced", "type", "name", "default_value", "hint", "hint_string", "usage_flags"), &EditorScenePostImportPlugin::add_import_option_advanced, DEFVAL(PROPERTY_HINT_NONE), DEFVAL(""), DEFVAL(PROPERTY_USAGE_DEFAULT));222223GDVIRTUAL_BIND(_get_internal_import_options, "category");224GDVIRTUAL_BIND(_get_internal_option_visibility, "category", "for_animation", "option");225GDVIRTUAL_BIND(_get_internal_option_update_view_required, "category", "option");226GDVIRTUAL_BIND(_internal_process, "category", "base_node", "node", "resource");227GDVIRTUAL_BIND(_get_import_options, "path");228GDVIRTUAL_BIND(_get_option_visibility, "path", "for_animation", "option");229GDVIRTUAL_BIND(_pre_process, "scene");230GDVIRTUAL_BIND(_post_process, "scene");231232BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_NODE);233BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE);234BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MESH);235BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MATERIAL);236BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION);237BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE);238BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE);239BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MAX);240}241242/////////////////////////////////////////////////////////243244const String ResourceImporterScene::material_extension[3] = { ".tres", ".res", ".material" };245246String ResourceImporterScene::get_importer_name() const {247// For compatibility with 4.2 and earlier we need to keep the "scene" and "animation_library" names.248// However this is arbitrary so for new import types we can use any string.249if (_scene_import_type == "PackedScene") {250return "scene";251} else if (_scene_import_type == "AnimationLibrary") {252return "animation_library";253}254return _scene_import_type;255}256257String ResourceImporterScene::get_visible_name() const {258// This is displayed on the UI. Friendly names here are nice but not vital, so fall back to the type.259if (_scene_import_type == "PackedScene") {260return "Scene";261}262return _scene_import_type.capitalize();263}264265void ResourceImporterScene::get_recognized_extensions(List<String> *p_extensions) const {266get_scene_importer_extensions(p_extensions);267}268269String ResourceImporterScene::get_save_extension() const {270if (_scene_import_type == "PackedScene") {271return "scn";272}273return "res";274}275276String ResourceImporterScene::get_resource_type() const {277return _scene_import_type;278}279280int ResourceImporterScene::get_format_version() const {281return 1;282}283284bool ResourceImporterScene::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {285if (_scene_import_type == "PackedScene") {286if (p_option.begins_with("animation/")) {287if (p_option != "animation/import" && !bool(p_options["animation/import"])) {288return false;289}290}291} else if (_scene_import_type == "AnimationLibrary") {292if (p_option == "animation/import") { // Option ignored, animation always imported.293return false;294}295if (p_option == "nodes/root_type" || p_option == "nodes/root_name" || p_option.begins_with("meshes/") || p_option.begins_with("skins/")) {296return false; // Nothing to do here for animations.297}298}299300if (p_option == "nodes/use_node_type_suffixes" && p_options.has("nodes/use_name_suffixes")) {301return p_options["nodes/use_name_suffixes"];302}303if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) != 2) {304// Only display the lightmap texel size import option when using the Static Lightmaps light baking mode.305return false;306}307308for (int i = 0; i < post_importer_plugins.size(); i++) {309Variant ret = post_importer_plugins.write[i]->get_option_visibility(p_path, _scene_import_type, p_option, p_options);310if (ret.get_type() == Variant::BOOL) {311if (!ret) {312return false;313}314}315}316317for (Ref<EditorSceneFormatImporter> importer : scene_importers) {318Variant ret = importer->get_option_visibility(p_path, _scene_import_type, p_option, p_options);319if (ret.get_type() == Variant::BOOL) {320if (!ret) {321return false;322}323}324}325326return true;327}328329int ResourceImporterScene::get_preset_count() const {330return 0;331}332333String ResourceImporterScene::get_preset_name(int p_idx) const {334return String();335}336337void ResourceImporterScene::_pre_fix_global(Node *p_scene, const HashMap<StringName, Variant> &p_options) const {338if (p_options.has("animation/import_rest_as_RESET") && (bool)p_options["animation/import_rest_as_RESET"]) {339TypedArray<Node> anim_players = p_scene->find_children("*", "AnimationPlayer");340if (anim_players.is_empty()) {341AnimationPlayer *anim_player = memnew(AnimationPlayer);342anim_player->set_name("AnimationPlayer");343p_scene->add_child(anim_player);344anim_player->set_owner(p_scene);345anim_players.append(anim_player);346}347Ref<Animation> reset_anim;348for (int i = 0; i < anim_players.size(); i++) {349AnimationPlayer *player = cast_to<AnimationPlayer>(anim_players[i]);350if (player->has_animation(SceneStringName(RESET))) {351reset_anim = player->get_animation(SceneStringName(RESET));352break;353}354}355if (reset_anim.is_null()) {356AnimationPlayer *anim_player = cast_to<AnimationPlayer>(anim_players[0]);357reset_anim.instantiate();358Ref<AnimationLibrary> anim_library;359if (anim_player->has_animation_library(StringName())) {360anim_library = anim_player->get_animation_library(StringName());361} else {362anim_library.instantiate();363anim_player->add_animation_library(StringName(), anim_library);364}365anim_library->add_animation(SceneStringName(RESET), reset_anim);366}367TypedArray<Node> skeletons = p_scene->find_children("*", "Skeleton3D");368for (int i = 0; i < skeletons.size(); i++) {369Skeleton3D *skeleton = cast_to<Skeleton3D>(skeletons[i]);370NodePath skeleton_path = p_scene->get_path_to(skeleton);371372HashSet<NodePath> existing_pos_tracks;373HashSet<NodePath> existing_rot_tracks;374for (int trk_i = 0; trk_i < reset_anim->get_track_count(); trk_i++) {375NodePath np = reset_anim->track_get_path(trk_i);376if (reset_anim->track_get_type(trk_i) == Animation::TYPE_POSITION_3D) {377existing_pos_tracks.insert(np);378}379if (reset_anim->track_get_type(trk_i) == Animation::TYPE_ROTATION_3D) {380existing_rot_tracks.insert(np);381}382}383for (int bone_i = 0; bone_i < skeleton->get_bone_count(); bone_i++) {384NodePath bone_path(skeleton_path.get_names(), Vector<StringName>{ skeleton->get_bone_name(bone_i) }, false);385if (!existing_pos_tracks.has(bone_path)) {386int pos_t = reset_anim->add_track(Animation::TYPE_POSITION_3D);387reset_anim->track_set_path(pos_t, bone_path);388reset_anim->position_track_insert_key(pos_t, 0.0, skeleton->get_bone_rest(bone_i).origin);389reset_anim->track_set_imported(pos_t, true);390}391if (!existing_rot_tracks.has(bone_path)) {392int rot_t = reset_anim->add_track(Animation::TYPE_ROTATION_3D);393reset_anim->track_set_path(rot_t, bone_path);394reset_anim->rotation_track_insert_key(rot_t, 0.0, skeleton->get_bone_rest(bone_i).basis.get_rotation_quaternion());395reset_anim->track_set_imported(rot_t, true);396}397}398}399}400}401402static bool _teststr(const String &p_what, const String &p_str) {403String what = p_what;404405// Remove trailing spaces and numbers, some apps like blender add ".number" to duplicates406// (dot is replaced with _ as invalid character) so also compensate for this.407while (what.length() && (is_digit(what[what.length() - 1]) || what[what.length() - 1] <= 32 || what[what.length() - 1] == '_')) {408what = what.substr(0, what.length() - 1);409}410411if (what.containsn("$" + p_str)) { // Blender and other stuff.412return true;413}414if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters415return true;416}417if (what.to_lower().ends_with("_" + p_str)) { //collada only supports "_" and "-" besides letters418return true;419}420return false;421}422423static String _fixstr(const String &p_what, const String &p_str) {424String what = p_what;425426// Remove trailing spaces and numbers, some apps like blender add ".number" to duplicates427// (dot is replaced with _ as invalid character) so also compensate for this.428while (what.length() && (is_digit(what[what.length() - 1]) || what[what.length() - 1] <= 32 || what[what.length() - 1] == '_')) {429what = what.substr(0, what.length() - 1);430}431432String end = p_what.substr(what.length());433434if (what.containsn("$" + p_str)) { // Blender and other stuff.435return what.replace("$" + p_str, "") + end;436}437if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters438return what.substr(0, what.length() - (p_str.length() + 1)) + end;439}440if (what.to_lower().ends_with("_" + p_str)) { //collada only supports "_" and "-" besides letters441return what.substr(0, what.length() - (p_str.length() + 1)) + end;442}443return what;444}445446static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r_shape_list, bool p_convex) {447ERR_FAIL_COND_MSG(mesh.is_null(), "Cannot generate shape list with null mesh value.");448if (!p_convex) {449Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape();450r_shape_list.push_back(shape);451} else {452Vector<Ref<Shape3D>> cd;453cd.push_back(mesh->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false));454if (cd.size()) {455for (int i = 0; i < cd.size(); i++) {456r_shape_list.push_back(cd[i]);457}458}459}460}461462struct ScalableNodeCollection {463HashSet<Node3D *> node_3ds;464HashSet<Ref<ImporterMesh>> importer_meshes;465HashSet<Ref<Skin>> skins;466HashSet<Ref<Animation>> animations;467};468469void _rescale_importer_mesh(Vector3 p_scale, Ref<ImporterMesh> p_mesh, bool is_shadow = false) {470// MESH and SKIN data divide, to compensate for object position multiplying.471472const int surf_count = p_mesh->get_surface_count();473const int blendshape_count = p_mesh->get_blend_shape_count();474struct LocalSurfData {475Mesh::PrimitiveType prim = {};476Array arr;477Array bsarr;478Dictionary lods;479String name;480Ref<Material> mat;481uint64_t fmt_compress_flags = 0;482};483484Vector<LocalSurfData> surf_data_by_mesh;485486Vector<String> blendshape_names;487for (int bsidx = 0; bsidx < blendshape_count; bsidx++) {488blendshape_names.append(p_mesh->get_blend_shape_name(bsidx));489}490491for (int surf_idx = 0; surf_idx < surf_count; surf_idx++) {492Mesh::PrimitiveType prim = p_mesh->get_surface_primitive_type(surf_idx);493const uint64_t fmt_compress_flags = p_mesh->get_surface_format(surf_idx);494Array arr = p_mesh->get_surface_arrays(surf_idx);495String name = p_mesh->get_surface_name(surf_idx);496Dictionary lods;497Ref<Material> mat = p_mesh->get_surface_material(surf_idx);498{499Vector<Vector3> vertex_array = arr[ArrayMesh::ARRAY_VERTEX];500for (int vert_arr_i = 0; vert_arr_i < vertex_array.size(); vert_arr_i++) {501vertex_array.write[vert_arr_i] = vertex_array[vert_arr_i] * p_scale;502}503arr[ArrayMesh::ARRAY_VERTEX] = vertex_array;504}505Array blendshapes;506for (int bsidx = 0; bsidx < blendshape_count; bsidx++) {507Array current_bsarr = p_mesh->get_surface_blend_shape_arrays(surf_idx, bsidx);508Vector<Vector3> current_bs_vertex_array = current_bsarr[ArrayMesh::ARRAY_VERTEX];509int current_bs_vert_arr_len = current_bs_vertex_array.size();510for (int32_t bs_vert_arr_i = 0; bs_vert_arr_i < current_bs_vert_arr_len; bs_vert_arr_i++) {511current_bs_vertex_array.write[bs_vert_arr_i] = current_bs_vertex_array[bs_vert_arr_i] * p_scale;512}513current_bsarr[ArrayMesh::ARRAY_VERTEX] = current_bs_vertex_array;514blendshapes.push_back(current_bsarr);515}516517LocalSurfData surf_data_dictionary = LocalSurfData();518surf_data_dictionary.prim = prim;519surf_data_dictionary.arr = arr;520surf_data_dictionary.bsarr = blendshapes;521surf_data_dictionary.lods = lods;522surf_data_dictionary.fmt_compress_flags = fmt_compress_flags;523surf_data_dictionary.name = name;524surf_data_dictionary.mat = mat;525526surf_data_by_mesh.push_back(surf_data_dictionary);527}528529p_mesh->clear();530531for (int bsidx = 0; bsidx < blendshape_count; bsidx++) {532p_mesh->add_blend_shape(blendshape_names[bsidx]);533}534535for (int surf_idx = 0; surf_idx < surf_count; surf_idx++) {536const Mesh::PrimitiveType prim = surf_data_by_mesh[surf_idx].prim;537const Array arr = surf_data_by_mesh[surf_idx].arr;538const Array bsarr = surf_data_by_mesh[surf_idx].bsarr;539const Dictionary lods = surf_data_by_mesh[surf_idx].lods;540const uint64_t fmt_compress_flags = surf_data_by_mesh[surf_idx].fmt_compress_flags;541const String name = surf_data_by_mesh[surf_idx].name;542const Ref<Material> mat = surf_data_by_mesh[surf_idx].mat;543544p_mesh->add_surface(prim, arr, bsarr, lods, mat, name, fmt_compress_flags);545}546547if (!is_shadow && p_mesh->get_shadow_mesh() != p_mesh && p_mesh->get_shadow_mesh().is_valid()) {548_rescale_importer_mesh(p_scale, p_mesh->get_shadow_mesh(), true);549}550}551552void _rescale_skin(Vector3 p_scale, Ref<Skin> p_skin) {553// MESH and SKIN data divide, to compensate for object position multiplying.554for (int i = 0; i < p_skin->get_bind_count(); i++) {555Transform3D transform = p_skin->get_bind_pose(i);556p_skin->set_bind_pose(i, Transform3D(transform.basis, p_scale * transform.origin));557}558}559560void _rescale_animation(Vector3 p_scale, Ref<Animation> p_animation) {561for (int track_idx = 0; track_idx < p_animation->get_track_count(); track_idx++) {562if (p_animation->track_get_type(track_idx) == Animation::TYPE_POSITION_3D) {563for (int key_idx = 0; key_idx < p_animation->track_get_key_count(track_idx); key_idx++) {564Vector3 value = p_animation->track_get_key_value(track_idx, key_idx);565value = p_scale * value;566p_animation->track_set_key_value(track_idx, key_idx, value);567}568}569}570}571572void _apply_scale_to_scalable_node_collection(ScalableNodeCollection &p_collection, Vector3 p_scale) {573for (Node3D *node_3d : p_collection.node_3ds) {574node_3d->set_position(p_scale * node_3d->get_position());575Skeleton3D *skeleton_3d = Object::cast_to<Skeleton3D>(node_3d);576if (skeleton_3d) {577for (int i = 0; i < skeleton_3d->get_bone_count(); i++) {578Transform3D rest = skeleton_3d->get_bone_rest(i);579Vector3 position = skeleton_3d->get_bone_pose_position(i);580skeleton_3d->set_bone_rest(i, Transform3D(rest.basis, p_scale * rest.origin));581skeleton_3d->set_bone_pose_position(i, p_scale * position);582}583}584}585for (Ref<ImporterMesh> mesh : p_collection.importer_meshes) {586_rescale_importer_mesh(p_scale, mesh, false);587}588for (Ref<Skin> skin : p_collection.skins) {589_rescale_skin(p_scale, skin);590}591for (Ref<Animation> animation : p_collection.animations) {592_rescale_animation(p_scale, animation);593}594}595596void _populate_scalable_nodes_collection(Node *p_node, ScalableNodeCollection &p_collection) {597if (!p_node) {598return;599}600Node3D *node_3d = Object::cast_to<Node3D>(p_node);601if (node_3d) {602p_collection.node_3ds.insert(node_3d);603ImporterMeshInstance3D *mesh_instance_3d = Object::cast_to<ImporterMeshInstance3D>(p_node);604if (mesh_instance_3d) {605Ref<ImporterMesh> mesh = mesh_instance_3d->get_mesh();606if (mesh.is_valid()) {607p_collection.importer_meshes.insert(mesh);608}609Ref<Skin> skin = mesh_instance_3d->get_skin();610if (skin.is_valid()) {611p_collection.skins.insert(skin);612}613}614}615AnimationPlayer *animation_player = Object::cast_to<AnimationPlayer>(p_node);616if (animation_player) {617List<StringName> animation_list;618animation_player->get_animation_list(&animation_list);619620for (const StringName &E : animation_list) {621Ref<Animation> animation = animation_player->get_animation(E);622p_collection.animations.insert(animation);623}624}625626for (int i = 0; i < p_node->get_child_count(); i++) {627Node *child = p_node->get_child(i);628_populate_scalable_nodes_collection(child, p_collection);629}630}631632void _apply_permanent_scale_to_descendants(Node *p_root_node, Vector3 p_scale) {633ScalableNodeCollection scalable_node_collection;634_populate_scalable_nodes_collection(p_root_node, scalable_node_collection);635_apply_scale_to_scalable_node_collection(scalable_node_collection, p_scale);636}637638Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames, const HashMap<StringName, Variant> &p_options) {639bool use_name_suffixes = true;640if (p_options.has("nodes/use_name_suffixes")) {641use_name_suffixes = p_options["nodes/use_name_suffixes"];642}643if (!use_name_suffixes) {644return p_node;645}646647// Children first.648for (int i = 0; i < p_node->get_child_count(); i++) {649Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames, p_options);650if (!r) {651i--; // Was erased.652}653}654655String name = p_node->get_name();656NodePath original_path = p_root->get_path_to(p_node); // Used to detect renames due to import hints.657658Ref<Resource> original_meta = memnew(Resource); // Create temp resource to hold original meta659original_meta->merge_meta_from(p_node);660661bool isroot = p_node == p_root;662663if (!isroot && _teststr(name, "noimp")) {664p_node->set_owner(nullptr);665memdelete(p_node);666return nullptr;667}668669if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {670ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);671672Ref<ImporterMesh> m = mi->get_mesh();673674if (m.is_valid()) {675for (int i = 0; i < m->get_surface_count(); i++) {676Ref<BaseMaterial3D> mat = m->get_surface_material(i);677if (mat.is_null()) {678continue;679}680681if (_teststr(mat->get_name(), "alpha")) {682mat->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA);683mat->set_name(_fixstr(mat->get_name(), "alpha"));684}685if (_teststr(mat->get_name(), "vcol")) {686mat->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);687mat->set_flag(BaseMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);688mat->set_name(_fixstr(mat->get_name(), "vcol"));689}690}691}692}693694if (Object::cast_to<AnimationPlayer>(p_node)) {695AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);696697// Node paths in animation tracks are relative to the following path (this is used to fix node paths below).698Node *ap_root = ap->get_node(ap->get_root_node());699NodePath path_prefix = p_root->get_path_to(ap_root);700701bool nodes_were_renamed = r_node_renames.size() != 0;702703List<StringName> anims;704ap->get_animation_list(&anims);705for (const StringName &E : anims) {706Ref<Animation> anim = ap->get_animation(E);707ERR_CONTINUE(anim.is_null());708709// Remove animation tracks referencing non-importable nodes.710for (int i = 0; i < anim->get_track_count(); i++) {711NodePath path = anim->track_get_path(i);712713for (int j = 0; j < path.get_name_count(); j++) {714String node = path.get_name(j);715if (_teststr(node, "noimp")) {716anim->remove_track(i);717i--;718break;719}720}721}722723// Fix node paths in animations, in case nodes were renamed earlier due to import hints.724if (nodes_were_renamed) {725for (int i = 0; i < anim->get_track_count(); i++) {726NodePath path = anim->track_get_path(i);727// Convert track path to absolute node path without subnames (some manual work because we are not in the scene tree).728Vector<StringName> absolute_path_names = path_prefix.get_names();729absolute_path_names.append_array(path.get_names());730NodePath absolute_path(absolute_path_names, false);731absolute_path.simplify();732// Fix paths to renamed nodes.733for (const Pair<NodePath, Node *> &F : r_node_renames) {734if (F.first == absolute_path) {735NodePath new_path(ap_root->get_path_to(F.second).get_names(), path.get_subnames(), false);736print_verbose(vformat("Fix: Correcting node path in animation track: %s should be %s", path, new_path));737anim->track_set_path(i, new_path);738break; // Only one match is possible.739}740}741}742}743744String animname = E;745const int loop_string_count = 3;746static const char *loop_strings[loop_string_count] = { "loop_mode", "loop", "cycle" };747for (int i = 0; i < loop_string_count; i++) {748if (_teststr(animname, loop_strings[i])) {749anim->set_loop_mode(Animation::LOOP_LINEAR);750animname = _fixstr(animname, loop_strings[i]);751752Ref<AnimationLibrary> library = ap->get_animation_library(ap->find_animation_library(anim));753library->rename_animation(E, animname);754}755}756}757}758759bool use_node_type_suffixes = true;760if (p_options.has("nodes/use_node_type_suffixes")) {761use_node_type_suffixes = p_options["nodes/use_node_type_suffixes"];762}763if (!use_node_type_suffixes) {764return p_node;765}766767if (_teststr(name, "colonly") || _teststr(name, "convcolonly")) {768if (isroot) {769return p_node;770}771772String fixed_name;773if (_teststr(name, "colonly")) {774fixed_name = _fixstr(name, "colonly");775} else if (_teststr(name, "convcolonly")) {776fixed_name = _fixstr(name, "convcolonly");777}778779if (fixed_name.is_empty()) {780p_node->set_owner(nullptr);781memdelete(p_node);782ERR_FAIL_V_MSG(nullptr, vformat("Skipped node `%s` because its name is empty after removing the suffix.", name));783}784785ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);786if (mi) {787Ref<ImporterMesh> mesh = mi->get_mesh();788789if (mesh.is_valid()) {790Vector<Ref<Shape3D>> shapes;791if (r_collision_map.has(mesh)) {792shapes = r_collision_map[mesh];793} else if (_teststr(name, "colonly")) {794_pre_gen_shape_list(mesh, shapes, false);795r_collision_map[mesh] = shapes;796} else if (_teststr(name, "convcolonly")) {797_pre_gen_shape_list(mesh, shapes, true);798r_collision_map[mesh] = shapes;799}800801if (shapes.size()) {802StaticBody3D *col = memnew(StaticBody3D);803col->set_transform(mi->get_transform());804col->set_name(fixed_name);805_copy_meta(p_node, col);806p_node->replace_by(col);807p_node->set_owner(nullptr);808memdelete(p_node);809p_node = col;810811_add_shapes(col, shapes);812}813}814815} else if (p_node->has_meta("empty_draw_type")) {816String empty_draw_type = String(p_node->get_meta("empty_draw_type"));817StaticBody3D *sb = memnew(StaticBody3D);818sb->set_name(fixed_name);819Object::cast_to<Node3D>(sb)->set_transform(Object::cast_to<Node3D>(p_node)->get_transform());820_copy_meta(p_node, sb);821p_node->replace_by(sb);822p_node->set_owner(nullptr);823memdelete(p_node);824p_node = sb;825CollisionShape3D *colshape = memnew(CollisionShape3D);826if (empty_draw_type == "CUBE") {827BoxShape3D *boxShape = memnew(BoxShape3D);828boxShape->set_size(Vector3(2, 2, 2));829colshape->set_shape(boxShape);830} else if (empty_draw_type == "SINGLE_ARROW") {831SeparationRayShape3D *rayShape = memnew(SeparationRayShape3D);832rayShape->set_length(1);833colshape->set_shape(rayShape);834Object::cast_to<Node3D>(sb)->rotate_x(Math::PI / 2);835} else if (empty_draw_type == "IMAGE") {836WorldBoundaryShape3D *world_boundary_shape = memnew(WorldBoundaryShape3D);837colshape->set_shape(world_boundary_shape);838} else {839SphereShape3D *sphereShape = memnew(SphereShape3D);840sphereShape->set_radius(1);841colshape->set_shape(sphereShape);842}843sb->add_child(colshape, true);844colshape->set_owner(sb->get_owner());845}846847} else if (_teststr(name, "rigid") && Object::cast_to<ImporterMeshInstance3D>(p_node)) {848if (isroot) {849return p_node;850}851852ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);853Ref<ImporterMesh> mesh = mi->get_mesh();854855if (mesh.is_valid()) {856Vector<Ref<Shape3D>> shapes;857if (r_collision_map.has(mesh)) {858shapes = r_collision_map[mesh];859} else {860_pre_gen_shape_list(mesh, shapes, true);861}862863RigidBody3D *rigid_body = memnew(RigidBody3D);864rigid_body->set_name(_fixstr(name, "rigid_body"));865_copy_meta(p_node, rigid_body);866p_node->replace_by(rigid_body);867rigid_body->set_transform(mi->get_transform());868p_node = rigid_body;869mi->set_transform(Transform3D());870rigid_body->add_child(mi, true);871mi->set_owner(rigid_body->get_owner());872873_add_shapes(rigid_body, shapes);874}875876} else if ((_teststr(name, "col") || (_teststr(name, "convcol"))) && Object::cast_to<ImporterMeshInstance3D>(p_node)) {877ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);878879Ref<ImporterMesh> mesh = mi->get_mesh();880881if (mesh.is_valid()) {882Vector<Ref<Shape3D>> shapes;883String fixed_name;884if (r_collision_map.has(mesh)) {885shapes = r_collision_map[mesh];886} else if (_teststr(name, "col")) {887_pre_gen_shape_list(mesh, shapes, false);888r_collision_map[mesh] = shapes;889} else if (_teststr(name, "convcol")) {890_pre_gen_shape_list(mesh, shapes, true);891r_collision_map[mesh] = shapes;892}893894if (_teststr(name, "col")) {895fixed_name = _fixstr(name, "col");896} else if (_teststr(name, "convcol")) {897fixed_name = _fixstr(name, "convcol");898}899900if (!fixed_name.is_empty()) {901if (mi->get_parent() && !mi->get_parent()->has_node(fixed_name)) {902mi->set_name(fixed_name);903}904}905906if (shapes.size()) {907StaticBody3D *col = memnew(StaticBody3D);908mi->add_child(col, true);909col->set_owner(mi->get_owner());910911_add_shapes(col, shapes);912}913}914915} else if (_teststr(name, "navmesh") && Object::cast_to<ImporterMeshInstance3D>(p_node)) {916if (isroot) {917return p_node;918}919920ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);921922Ref<ImporterMesh> mesh = mi->get_mesh();923ERR_FAIL_COND_V(mesh.is_null(), nullptr);924NavigationRegion3D *nmi = memnew(NavigationRegion3D);925926nmi->set_name(_fixstr(name, "navmesh"));927Ref<NavigationMesh> nmesh = mesh->create_navigation_mesh();928nmi->set_navigation_mesh(nmesh);929Object::cast_to<Node3D>(nmi)->set_transform(mi->get_transform());930_copy_meta(p_node, nmi);931p_node->replace_by(nmi);932p_node->set_owner(nullptr);933memdelete(p_node);934p_node = nmi;935} else if (_teststr(name, "occ") || _teststr(name, "occonly")) {936if (isroot) {937return p_node;938}939ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);940if (mi) {941Ref<ImporterMesh> mesh = mi->get_mesh();942943if (mesh.is_valid()) {944if (r_occluder_arrays) {945OccluderInstance3D::bake_single_node(mi, 0.0f, r_occluder_arrays->first, r_occluder_arrays->second);946}947if (_teststr(name, "occ")) {948String fixed_name = _fixstr(name, "occ");949if (!fixed_name.is_empty()) {950if (mi->get_parent() && !mi->get_parent()->has_node(fixed_name)) {951mi->set_name(fixed_name);952}953}954} else {955p_node->set_owner(nullptr);956memdelete(p_node);957p_node = nullptr;958}959}960}961} else if (_teststr(name, "vehicle")) {962if (isroot) {963return p_node;964}965966Node *owner = p_node->get_owner();967Node3D *s = Object::cast_to<Node3D>(p_node);968VehicleBody3D *bv = memnew(VehicleBody3D);969String n = _fixstr(p_node->get_name(), "vehicle");970bv->set_name(n);971_copy_meta(p_node, bv);972p_node->replace_by(bv);973p_node->set_name(n);974bv->add_child(p_node);975bv->set_owner(owner);976p_node->set_owner(owner);977bv->set_transform(s->get_transform());978s->set_transform(Transform3D());979980p_node = bv;981} else if (_teststr(name, "wheel")) {982if (isroot) {983return p_node;984}985986Node *owner = p_node->get_owner();987Node3D *s = Object::cast_to<Node3D>(p_node);988VehicleWheel3D *bv = memnew(VehicleWheel3D);989String n = _fixstr(p_node->get_name(), "wheel");990bv->set_name(n);991_copy_meta(p_node, bv);992p_node->replace_by(bv);993p_node->set_name(n);994bv->add_child(p_node);995bv->set_owner(owner);996p_node->set_owner(owner);997bv->set_transform(s->get_transform());998s->set_transform(Transform3D());9991000p_node = bv;1001} else if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1002//last attempt, maybe collision inside the mesh data10031004ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);10051006Ref<ImporterMesh> mesh = mi->get_mesh();1007if (mesh.is_valid()) {1008Vector<Ref<Shape3D>> shapes;1009if (r_collision_map.has(mesh)) {1010shapes = r_collision_map[mesh];1011} else if (_teststr(mesh->get_name(), "col")) {1012_pre_gen_shape_list(mesh, shapes, false);1013r_collision_map[mesh] = shapes;1014mesh->set_name(_fixstr(mesh->get_name(), "col"));1015} else if (_teststr(mesh->get_name(), "convcol")) {1016_pre_gen_shape_list(mesh, shapes, true);1017r_collision_map[mesh] = shapes;1018mesh->set_name(_fixstr(mesh->get_name(), "convcol"));1019} else if (_teststr(mesh->get_name(), "occ")) {1020if (r_occluder_arrays) {1021OccluderInstance3D::bake_single_node(mi, 0.0f, r_occluder_arrays->first, r_occluder_arrays->second);1022}1023mesh->set_name(_fixstr(mesh->get_name(), "occ"));1024}10251026if (shapes.size()) {1027StaticBody3D *col = memnew(StaticBody3D);1028p_node->add_child(col, true);1029col->set_owner(p_node->get_owner());10301031_add_shapes(col, shapes);1032}1033}1034}10351036if (p_node) {1037NodePath new_path = p_root->get_path_to(p_node);1038if (new_path != original_path) {1039print_verbose(vformat("Fix: Renamed %s to %s", original_path, new_path));1040r_node_renames.push_back({ original_path, p_node });1041}1042// If we created new node instead, merge meta values from the original node.1043p_node->merge_meta_from(*original_meta);1044}10451046return p_node;1047}10481049Node *ResourceImporterScene::_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps) {1050// children first1051for (int i = 0; i < p_node->get_child_count(); i++) {1052Node *r = _pre_fix_animations(p_node->get_child(i), p_root, p_node_data, p_animation_data, p_animation_fps);1053if (!r) {1054i--; //was erased1055}1056}10571058String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));10591060Dictionary node_settings;1061if (p_node_data.has(import_id)) {1062node_settings = p_node_data[import_id];1063}10641065{1066//make sure this is unique1067node_settings = node_settings.duplicate(true);1068//fill node settings for this node with default values1069List<ImportOption> iopts;1070get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);1071for (const ImportOption &E : iopts) {1072if (!node_settings.has(E.option.name)) {1073node_settings[E.option.name] = E.default_value;1074}1075}1076}10771078if (Object::cast_to<AnimationPlayer>(p_node)) {1079AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);1080List<StringName> anims;1081ap->get_animation_list(&anims);10821083AnimationImportTracks import_tracks_mode[TRACK_CHANNEL_MAX] = {1084AnimationImportTracks(int(node_settings["import_tracks/position"])),1085AnimationImportTracks(int(node_settings["import_tracks/rotation"])),1086AnimationImportTracks(int(node_settings["import_tracks/scale"]))1087};10881089if (!anims.is_empty() && (import_tracks_mode[0] != ANIMATION_IMPORT_TRACKS_IF_PRESENT || import_tracks_mode[1] != ANIMATION_IMPORT_TRACKS_IF_PRESENT || import_tracks_mode[2] != ANIMATION_IMPORT_TRACKS_IF_PRESENT)) {1090_optimize_track_usage(ap, import_tracks_mode);1091}1092}10931094return p_node;1095}10961097Node *ResourceImporterScene::_post_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps, bool p_remove_immutable_tracks) {1098// children first1099for (int i = 0; i < p_node->get_child_count(); i++) {1100Node *r = _post_fix_animations(p_node->get_child(i), p_root, p_node_data, p_animation_data, p_animation_fps, p_remove_immutable_tracks);1101if (!r) {1102i--; //was erased1103}1104}11051106String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));11071108Dictionary node_settings;1109if (p_node_data.has(import_id)) {1110node_settings = p_node_data[import_id];1111}11121113{1114//make sure this is unique1115node_settings = node_settings.duplicate(true);1116//fill node settings for this node with default values1117List<ImportOption> iopts;1118get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);1119for (const ImportOption &E : iopts) {1120if (!node_settings.has(E.option.name)) {1121node_settings[E.option.name] = E.default_value;1122}1123}1124}11251126if (Object::cast_to<AnimationPlayer>(p_node)) {1127AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);1128List<StringName> anims;1129ap->get_animation_list(&anims);11301131if (p_remove_immutable_tracks) {1132AnimationImportTracks import_tracks_mode[TRACK_CHANNEL_MAX] = {1133AnimationImportTracks(int(node_settings["import_tracks/position"])),1134AnimationImportTracks(int(node_settings["import_tracks/rotation"])),1135AnimationImportTracks(int(node_settings["import_tracks/scale"]))1136};1137HashMap<NodePath, bool> used_tracks[TRACK_CHANNEL_MAX];11381139for (const StringName &name : anims) {1140Ref<Animation> anim = ap->get_animation(name);1141int track_count = anim->get_track_count();1142LocalVector<int> tracks_to_keep;1143for (int track_i = 0; track_i < track_count; track_i++) {1144tracks_to_keep.push_back(track_i);1145int track_channel_type = 0;1146switch (anim->track_get_type(track_i)) {1147case Animation::TYPE_POSITION_3D:1148track_channel_type = TRACK_CHANNEL_POSITION;1149break;1150case Animation::TYPE_ROTATION_3D:1151track_channel_type = TRACK_CHANNEL_ROTATION;1152break;1153case Animation::TYPE_SCALE_3D:1154track_channel_type = TRACK_CHANNEL_SCALE;1155break;1156default:1157continue;1158}1159AnimationImportTracks track_mode = import_tracks_mode[track_channel_type];1160NodePath path = anim->track_get_path(track_i);1161Node *n = p_root->get_node(path);1162Node3D *n3d = Object::cast_to<Node3D>(n);1163Skeleton3D *skel = Object::cast_to<Skeleton3D>(n);1164bool keep_track = false;1165Vector3 loc;1166Quaternion rot;1167Vector3 scale;1168if (skel && path.get_subname_count() > 0) {1169StringName bone = path.get_subname(0);1170int bone_idx = skel->find_bone(bone);1171if (bone_idx == -1) {1172continue;1173}1174// Note that this is using get_bone_pose to update the bone pose cache.1175Transform3D bone_rest = skel->get_bone_rest(bone_idx);1176loc = bone_rest.origin / skel->get_motion_scale();1177rot = bone_rest.basis.get_rotation_quaternion();1178scale = bone_rest.basis.get_scale();1179} else if (n3d) {1180loc = n3d->get_position();1181rot = n3d->get_transform().basis.get_rotation_quaternion();1182scale = n3d->get_scale();1183} else {1184continue;1185}11861187if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {1188if (used_tracks[track_channel_type].has(path)) {1189if (used_tracks[track_channel_type][path]) {1190continue;1191}1192} else {1193used_tracks[track_channel_type].insert(path, false);1194}1195}11961197for (int key_i = 0; key_i < anim->track_get_key_count(track_i) && !keep_track; key_i++) {1198switch (track_channel_type) {1199case TRACK_CHANNEL_POSITION: {1200Vector3 key_pos;1201anim->position_track_get_key(track_i, key_i, &key_pos);1202if (!key_pos.is_equal_approx(loc)) {1203keep_track = true;1204if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {1205used_tracks[track_channel_type][path] = true;1206}1207}1208} break;1209case TRACK_CHANNEL_ROTATION: {1210Quaternion key_rot;1211anim->rotation_track_get_key(track_i, key_i, &key_rot);1212if (!key_rot.is_equal_approx(rot)) {1213keep_track = true;1214if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {1215used_tracks[track_channel_type][path] = true;1216}1217}1218} break;1219case TRACK_CHANNEL_SCALE: {1220Vector3 key_scl;1221anim->scale_track_get_key(track_i, key_i, &key_scl);1222if (!key_scl.is_equal_approx(scale)) {1223keep_track = true;1224if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {1225used_tracks[track_channel_type][path] = true;1226}1227}1228} break;1229default:1230break;1231}1232}1233if (track_mode != ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL && !keep_track) {1234tracks_to_keep.remove_at(tracks_to_keep.size() - 1);1235}1236}1237for (int dst_track_i = 0; dst_track_i < (int)tracks_to_keep.size(); dst_track_i++) {1238int src_track_i = tracks_to_keep[dst_track_i];1239if (src_track_i != dst_track_i) {1240anim->track_swap(src_track_i, dst_track_i);1241}1242}1243for (int track_i = track_count - 1; track_i >= (int)tracks_to_keep.size(); track_i--) {1244anim->remove_track(track_i);1245}1246}1247for (const StringName &name : anims) {1248Ref<Animation> anim = ap->get_animation(name);1249int track_count = anim->get_track_count();1250LocalVector<int> tracks_to_keep;1251for (int track_i = 0; track_i < track_count; track_i++) {1252tracks_to_keep.push_back(track_i);1253int track_channel_type = 0;1254switch (anim->track_get_type(track_i)) {1255case Animation::TYPE_POSITION_3D:1256track_channel_type = TRACK_CHANNEL_POSITION;1257break;1258case Animation::TYPE_ROTATION_3D:1259track_channel_type = TRACK_CHANNEL_ROTATION;1260break;1261case Animation::TYPE_SCALE_3D:1262track_channel_type = TRACK_CHANNEL_SCALE;1263break;1264default:1265continue;1266}1267AnimationImportTracks track_mode = import_tracks_mode[track_channel_type];1268if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {1269NodePath path = anim->track_get_path(track_i);1270if (used_tracks[track_channel_type].has(path) && !used_tracks[track_channel_type][path]) {1271tracks_to_keep.remove_at(tracks_to_keep.size() - 1);1272}1273}1274}1275for (int dst_track_i = 0; dst_track_i < (int)tracks_to_keep.size(); dst_track_i++) {1276int src_track_i = tracks_to_keep[dst_track_i];1277if (src_track_i != dst_track_i) {1278anim->track_swap(src_track_i, dst_track_i);1279}1280}1281for (int track_i = track_count - 1; track_i >= (int)tracks_to_keep.size(); track_i--) {1282anim->remove_track(track_i);1283}1284}1285}12861287bool use_optimizer = node_settings["optimizer/enabled"];1288float anim_optimizer_linerr = node_settings["optimizer/max_velocity_error"];1289float anim_optimizer_angerr = node_settings["optimizer/max_angular_error"];1290int anim_optimizer_preerr = node_settings["optimizer/max_precision_error"];12911292if (use_optimizer) {1293_optimize_animations(ap, anim_optimizer_linerr, anim_optimizer_angerr, anim_optimizer_preerr);1294}12951296bool use_compression = node_settings["compression/enabled"];1297int anim_compression_page_size = node_settings["compression/page_size"];12981299if (use_compression) {1300_compress_animations(ap, anim_compression_page_size);1301}13021303for (const StringName &name : anims) {1304Ref<Animation> anim = ap->get_animation(name);1305Array animation_slices;13061307if (p_animation_data.has(name)) {1308Dictionary anim_settings = p_animation_data[name];13091310{1311int slice_count = anim_settings["slices/amount"];13121313for (int i = 0; i < slice_count; i++) {1314String slice_name = anim_settings["slice_" + itos(i + 1) + "/name"];1315int from_frame = anim_settings["slice_" + itos(i + 1) + "/start_frame"];1316int end_frame = anim_settings["slice_" + itos(i + 1) + "/end_frame"];1317Animation::LoopMode loop_mode = static_cast<Animation::LoopMode>((int)anim_settings["slice_" + itos(i + 1) + "/loop_mode"]);1318bool save_to_file = anim_settings["slice_" + itos(i + 1) + "/save_to_file/enabled"];1319String save_to_path = anim_settings["slice_" + itos(i + 1) + "/save_to_file/path"];1320bool save_to_file_keep_custom = anim_settings["slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"];13211322animation_slices.push_back(slice_name);1323animation_slices.push_back(from_frame / p_animation_fps);1324animation_slices.push_back(end_frame / p_animation_fps);1325animation_slices.push_back(loop_mode);1326animation_slices.push_back(save_to_file);1327animation_slices.push_back(save_to_path);1328animation_slices.push_back(save_to_file_keep_custom);1329}13301331if (animation_slices.size() > 0) {1332_create_slices(ap, anim, animation_slices, true);1333}1334}1335{1336//fill with default values1337List<ImportOption> iopts;1338get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION, &iopts);1339for (const ImportOption &F : iopts) {1340if (!anim_settings.has(F.option.name)) {1341anim_settings[F.option.name] = F.default_value;1342}1343}1344}13451346anim->set_loop_mode(static_cast<Animation::LoopMode>((int)anim_settings["settings/loop_mode"]));1347bool save = anim_settings["save_to_file/enabled"];1348String path = anim_settings["save_to_file/path"];1349bool keep_custom = anim_settings["save_to_file/keep_custom_tracks"];13501351Ref<Animation> saved_anim = _save_animation_to_file(anim, save, path, keep_custom);13521353if (saved_anim != anim) {1354Ref<AnimationLibrary> al = ap->get_animation_library(ap->find_animation_library(anim));1355al->add_animation(name, saved_anim); //replace1356}1357}1358}1359}13601361return p_node;1362}13631364Node *ResourceImporterScene::_replace_node_with_type_and_script(Node *p_node, String p_node_type, Ref<Script> p_script) {1365p_node_type = p_node_type.get_slicec(' ', 0); // Full root_type is "ClassName (filename.gd)" for a script global class.1366if (p_script.is_valid()) {1367// Ensure the node type supports the script, or pick one that does.1368String script_base_type = p_script->get_instance_base_type();1369if (ClassDB::is_parent_class(script_base_type, "Node")) {1370if (p_node_type.is_empty() || !ClassDB::is_parent_class(p_node_type, script_base_type)) {1371p_node_type = script_base_type;1372}1373}1374}1375if (!p_node_type.is_empty() && ScriptServer::is_global_class(p_node_type)) {1376// If the user specified a script class, we need to get the base node type.1377if (p_script.is_null()) {1378p_script = ResourceLoader::load(ScriptServer::get_global_class_path(p_node_type));1379}1380p_node_type = ScriptServer::get_global_class_base(p_node_type);1381while (!p_node_type.is_empty()) {1382if (ScriptServer::is_global_class(p_node_type)) {1383p_node_type = ScriptServer::get_global_class_base(p_node_type);1384} else {1385break;1386}1387}1388}1389if (!p_node_type.is_empty() && p_node->get_class_name() != p_node_type) {1390// If the user specified a Godot node type that does not match1391// what the scene import gave us, replace the root node.1392Node *new_base_node = Object::cast_to<Node>(ClassDB::instantiate(p_node_type));1393if (new_base_node) {1394List<PropertyInfo> old_properties;1395p_node->get_property_list(&old_properties);1396for (const PropertyInfo &prop : old_properties) {1397if (!(prop.usage & PROPERTY_USAGE_STORAGE)) {1398continue;1399}1400new_base_node->set(prop.name, p_node->get(prop.name));1401}1402new_base_node->set_name(p_node->get_name());1403_copy_meta(p_node, new_base_node);1404p_node->replace_by(new_base_node);1405p_node->set_owner(nullptr);1406memdelete(p_node);1407p_node = new_base_node;1408}1409}1410if (p_script.is_valid()) {1411p_node->set_script(Variant(p_script));1412}1413return p_node;1414}14151416Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale, const String &p_source_file, const HashMap<StringName, Variant> &p_options) {1417// children first1418for (int i = 0; i < p_node->get_child_count(); i++) {1419Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_occluder_arrays, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps, p_applied_root_scale, p_source_file, p_options);1420if (!r) {1421i--; //was erased1422}1423}14241425int extract_mat = 0;1426if (p_options.has("materials/extract")) {1427extract_mat = p_options["materials/extract"];1428}14291430String spath = p_source_file.get_base_dir();1431if (p_options.has("materials/extract_path")) {1432String extpath = p_options["materials/extract_path"];1433if (!extpath.is_empty()) {1434spath = extpath;1435}1436}14371438bool isroot = p_node == p_root;14391440String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));14411442Dictionary node_settings;1443if (p_node_data.has(import_id)) {1444node_settings = p_node_data[import_id];1445}14461447if (!isroot && (node_settings.has("import/skip_import") && bool(node_settings["import/skip_import"]))) {1448p_node->set_owner(nullptr);1449memdelete(p_node);1450return nullptr;1451}14521453{1454//make sure this is unique1455node_settings = node_settings.duplicate(true);1456//fill node settings for this node with default values1457List<ImportOption> iopts;1458if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1459get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE, &iopts);1460} else if (Object::cast_to<AnimationPlayer>(p_node)) {1461get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);1462} else if (Object::cast_to<Skeleton3D>(p_node)) {1463get_internal_import_options(INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, &iopts);1464} else {1465get_internal_import_options(INTERNAL_IMPORT_CATEGORY_NODE, &iopts);1466}1467for (const ImportOption &E : iopts) {1468if (!node_settings.has(E.option.name)) {1469node_settings[E.option.name] = E.default_value;1470}1471}1472}14731474{1475ObjectID node_id = p_node->get_instance_id();1476for (int i = 0; i < post_importer_plugins.size(); i++) {1477post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_NODE, p_root, p_node, Ref<Resource>(), node_settings);1478if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue1479break;1480}1481}1482}14831484if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1485ObjectID node_id = p_node->get_instance_id();1486for (int i = 0; i < post_importer_plugins.size(); i++) {1487post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE, p_root, p_node, Ref<Resource>(), node_settings);1488if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue1489break;1490}1491}1492}14931494if (Object::cast_to<Skeleton3D>(p_node)) {1495Ref<Animation> rest_animation;1496float rest_animation_timestamp = 0.0;1497Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);1498if (skeleton != nullptr && int(node_settings.get("rest_pose/load_pose", 0)) != 0) {1499String selected_animation_name = node_settings.get("rest_pose/selected_animation", String());1500if (int(node_settings["rest_pose/load_pose"]) == 1) {1501TypedArray<Node> children = p_root->find_children("*", "AnimationPlayer", true, false);1502for (int node_i = 0; node_i < children.size(); node_i++) {1503AnimationPlayer *anim_player = cast_to<AnimationPlayer>(children[node_i]);1504ERR_CONTINUE(anim_player == nullptr);1505List<StringName> anim_list;1506anim_player->get_animation_list(&anim_list);1507if (anim_list.size() == 1) {1508selected_animation_name = anim_list.front()->get();1509}1510rest_animation = anim_player->get_animation(selected_animation_name);1511if (rest_animation.is_valid()) {1512break;1513}1514}1515} else if (int(node_settings["rest_pose/load_pose"]) == 2) {1516Object *external_object = node_settings.get("rest_pose/external_animation_library", Variant());1517rest_animation = external_object;1518if (rest_animation.is_null()) {1519Ref<AnimationLibrary> library(external_object);1520if (library.is_valid()) {1521List<StringName> anim_list;1522library->get_animation_list(&anim_list);1523if (anim_list.size() == 1) {1524selected_animation_name = String(anim_list.front()->get());1525}1526rest_animation = library->get_animation(selected_animation_name);1527}1528}1529}1530rest_animation_timestamp = double(node_settings.get("rest_pose/selected_timestamp", 0.0));1531if (rest_animation.is_valid()) {1532for (int track_i = 0; track_i < rest_animation->get_track_count(); track_i++) {1533NodePath path = rest_animation->track_get_path(track_i);1534StringName node_path = path.get_concatenated_names();1535if (String(node_path).begins_with("%")) {1536continue; // Unique node names are commonly used with retargeted animations, which we do not want to use.1537}1538StringName skeleton_bone = path.get_concatenated_subnames();1539if (skeleton_bone == StringName()) {1540continue;1541}1542int bone_idx = skeleton->find_bone(skeleton_bone);1543if (bone_idx == -1) {1544continue;1545}1546switch (rest_animation->track_get_type(track_i)) {1547case Animation::TYPE_POSITION_3D: {1548Vector3 bone_position = rest_animation->position_track_interpolate(track_i, rest_animation_timestamp);1549skeleton->set_bone_rest(bone_idx, Transform3D(skeleton->get_bone_rest(bone_idx).basis, bone_position));1550} break;1551case Animation::TYPE_ROTATION_3D: {1552Quaternion bone_rotation = rest_animation->rotation_track_interpolate(track_i, rest_animation_timestamp);1553Transform3D current_rest = skeleton->get_bone_rest(bone_idx);1554skeleton->set_bone_rest(bone_idx, Transform3D(Basis(bone_rotation).scaled(current_rest.basis.get_scale()), current_rest.origin));1555} break;1556default:1557break;1558}1559}1560}1561}15621563ObjectID node_id = p_node->get_instance_id();1564for (int i = 0; i < post_importer_plugins.size(); i++) {1565post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, p_root, p_node, Ref<Resource>(), node_settings);1566if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue1567break;1568}1569}1570}15711572if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1573ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);15741575Ref<ImporterMesh> m = mi->get_mesh();15761577if (m.is_valid()) {1578if (!r_scanned_meshes.has(m)) {1579for (int i = 0; i < m->get_surface_count(); i++) {1580Ref<Material> mat = m->get_surface_material(i);1581if (mat.is_valid()) {1582String mat_id = mat->get_meta("import_id", mat->get_name());1583if (!mat_id.is_empty() && p_material_data.has(mat_id)) {1584Dictionary matdata = p_material_data[mat_id];1585{1586//fill node settings for this node with default values1587List<ImportOption> iopts;1588get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MATERIAL, &iopts);1589for (const ImportOption &E : iopts) {1590if (!matdata.has(E.option.name)) {1591matdata[E.option.name] = E.default_value;1592}1593}1594}15951596for (int j = 0; j < post_importer_plugins.size(); j++) {1597post_importer_plugins.write[j]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL, p_root, p_node, mat, matdata);1598}1599}1600if (!mat_id.is_empty() && extract_mat != 0) {1601String ext = material_extension[p_options.has("materials/extract_format") ? (int)p_options["materials/extract_format"] : 0];1602String path = spath.path_join(mat_id.validate_filename() + ext);1603String uid_path = ResourceUID::path_to_uid(path);16041605Dictionary matdata = p_material_data[mat_id];1606matdata["use_external/enabled"] = true;1607matdata["use_external/path"] = uid_path;1608matdata["use_external/fallback_path"] = path;1609if (!FileAccess::exists(path) || extract_mat == 2 /*overwrite*/) {1610ResourceSaver::save(mat, path);1611}16121613Ref<Material> external_mat = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE);1614if (external_mat.is_valid()) {1615m->set_surface_material(i, external_mat);1616}1617}1618if (!mat_id.is_empty() && p_material_data.has(mat_id)) {1619Dictionary matdata = p_material_data[mat_id];1620if (matdata.has("use_external/enabled") && bool(matdata["use_external/enabled"]) && matdata.has("use_external/path")) {1621String path = matdata["use_external/path"];1622Ref<Material> external_mat = ResourceLoader::load(path);1623if (external_mat.is_null()) {1624if (matdata.has("use_external/fallback_path")) {1625String fallback_save_path = matdata["use_external/fallback_path"];1626if (!fallback_save_path.is_empty()) {1627external_mat = ResourceLoader::load(fallback_save_path);1628if (external_mat.is_valid()) {1629path = fallback_save_path;1630}1631}1632}1633}1634if (external_mat.is_valid()) {1635m->set_surface_material(i, external_mat);1636if (!path.begins_with("uid://")) {1637const ResourceUID::ID id = ResourceLoader::get_resource_uid(path);1638if (id != ResourceUID::INVALID_ID) {1639matdata["use_external/path"] = ResourceUID::get_singleton()->id_to_text(id);1640}1641}1642matdata["use_external/fallback_path"] = external_mat->get_path();1643}1644}1645}1646}1647}16481649r_scanned_meshes.insert(m);1650}16511652if (node_settings.has("generate/physics")) {1653int mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_DISABLED;16541655const bool generate_collider = node_settings["generate/physics"];1656if (generate_collider) {1657mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_MESH_AND_STATIC_COLLIDER;1658if (node_settings.has("physics/body_type")) {1659const BodyType body_type = (BodyType)node_settings["physics/body_type"].operator int();1660switch (body_type) {1661case BODY_TYPE_STATIC:1662mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_MESH_AND_STATIC_COLLIDER;1663break;1664case BODY_TYPE_DYNAMIC:1665mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_RIGID_BODY_AND_MESH;1666break;1667case BODY_TYPE_AREA:1668mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_AREA_ONLY;1669break;1670}1671}1672}16731674if (mesh_physics_mode != MeshPhysicsMode::MESH_PHYSICS_DISABLED) {1675Vector<Ref<Shape3D>> shapes;1676if (collision_map.has(m)) {1677shapes = collision_map[m];1678} else {1679shapes = get_collision_shapes(1680m,1681node_settings,1682p_applied_root_scale);1683}16841685if (shapes.size()) {1686CollisionObject3D *base = nullptr;1687switch (mesh_physics_mode) {1688case MESH_PHYSICS_MESH_AND_STATIC_COLLIDER: {1689StaticBody3D *col = memnew(StaticBody3D);1690p_node->add_child(col, true);1691col->set_owner(p_node->get_owner());1692col->set_transform(get_collision_shapes_transform(node_settings));1693col->set_position(p_applied_root_scale * col->get_position());1694const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];1695if (pmo.is_valid()) {1696col->set_physics_material_override(pmo);1697}1698base = col;1699} break;1700case MESH_PHYSICS_RIGID_BODY_AND_MESH: {1701NodePath skeleton_path = mi->get_skeleton_path();1702Skeleton3D *skeleton = nullptr;1703if (!skeleton_path.is_empty()) {1704skeleton = Object::cast_to<Skeleton3D>(mi->get_node_or_null(skeleton_path));1705}1706RigidBody3D *rigid_body = memnew(RigidBody3D);1707rigid_body->set_name(p_node->get_name());1708_copy_meta(p_node, rigid_body);1709p_node->replace_by(rigid_body);1710rigid_body->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));1711rigid_body->set_position(p_applied_root_scale * rigid_body->get_position());1712p_node = rigid_body;1713mi->set_transform(Transform3D());1714rigid_body->add_child(mi, true);1715mi->set_owner(rigid_body->get_owner());1716const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];1717if (pmo.is_valid()) {1718rigid_body->set_physics_material_override(pmo);1719}1720base = rigid_body;1721if (skeleton) {1722skeleton_path = mi->get_path_to(skeleton);1723mi->set_skeleton_path(skeleton_path);1724}1725} break;1726case MESH_PHYSICS_STATIC_COLLIDER_ONLY: {1727StaticBody3D *col = memnew(StaticBody3D);1728col->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));1729col->set_position(p_applied_root_scale * col->get_position());1730col->set_name(p_node->get_name());1731_copy_meta(p_node, col);1732p_node->replace_by(col);1733p_node->set_owner(nullptr);1734memdelete(p_node);1735p_node = col;1736const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];1737if (pmo.is_valid()) {1738col->set_physics_material_override(pmo);1739}1740base = col;1741} break;1742case MESH_PHYSICS_AREA_ONLY: {1743Area3D *area = memnew(Area3D);1744area->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));1745area->set_position(p_applied_root_scale * area->get_position());1746area->set_name(p_node->get_name());1747_copy_meta(p_node, area);1748p_node->replace_by(area);1749p_node->set_owner(nullptr);1750memdelete(p_node);1751p_node = area;1752base = area;17531754} break;1755}17561757base->set_collision_layer(node_settings["physics/layer"]);1758base->set_collision_mask(node_settings["physics/mask"]);17591760for (const Ref<Shape3D> &E : shapes) {1761CollisionShape3D *cshape = memnew(CollisionShape3D);1762cshape->set_shape(E);1763base->add_child(cshape, true);17641765cshape->set_owner(base->get_owner());1766}1767}1768}1769}1770}1771}17721773//navmesh (node may have changed type above)1774if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1775ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);17761777Ref<ImporterMesh> m = mi->get_mesh();17781779if (m.is_valid()) {1780if (node_settings.has("generate/navmesh")) {1781int navmesh_mode = node_settings["generate/navmesh"];17821783if (navmesh_mode != NAVMESH_DISABLED) {1784NavigationRegion3D *nmi = memnew(NavigationRegion3D);17851786Ref<NavigationMesh> nmesh = m->create_navigation_mesh();1787nmi->set_navigation_mesh(nmesh);17881789if (navmesh_mode == NAVMESH_NAVMESH_ONLY) {1790nmi->set_transform(mi->get_transform());1791_copy_meta(p_node, nmi);1792p_node->replace_by(nmi);1793p_node->set_owner(nullptr);1794memdelete(p_node);1795p_node = nmi;1796} else {1797mi->add_child(nmi, true);1798nmi->set_owner(mi->get_owner());1799}1800}1801}1802}1803}18041805if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1806ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);18071808Ref<ImporterMesh> m = mi->get_mesh();18091810if (m.is_valid()) {1811if (node_settings.has("generate/occluder")) {1812int occluder_mode = node_settings["generate/occluder"];18131814if (occluder_mode != OCCLUDER_DISABLED) {1815float simplification_dist = 0.0f;1816if (node_settings.has("occluder/simplification_distance")) {1817simplification_dist = node_settings["occluder/simplification_distance"];1818}18191820OccluderInstance3D::bake_single_node(mi, simplification_dist, r_occluder_arrays.first, r_occluder_arrays.second);18211822if (occluder_mode == OCCLUDER_OCCLUDER_ONLY) {1823p_node->set_owner(nullptr);1824memdelete(p_node);1825p_node = nullptr;1826}1827}1828}1829}1830}18311832if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1833ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);18341835if (node_settings.has("mesh_instance/layers")) {1836mi->set_layer_mask(node_settings["mesh_instance/layers"]);1837}18381839if (node_settings.has("mesh_instance/visibility_range_begin")) {1840mi->set_visibility_range_begin(node_settings["mesh_instance/visibility_range_begin"]);1841}18421843if (node_settings.has("mesh_instance/visibility_range_begin_margin")) {1844mi->set_visibility_range_begin_margin(node_settings["mesh_instance/visibility_range_begin_margin"]);1845}18461847if (node_settings.has("mesh_instance/visibility_range_end")) {1848mi->set_visibility_range_end(node_settings["mesh_instance/visibility_range_end"]);1849}18501851if (node_settings.has("mesh_instance/visibility_range_end_margin")) {1852mi->set_visibility_range_end_margin(node_settings["mesh_instance/visibility_range_end_margin"]);1853}18541855if (node_settings.has("mesh_instance/visibility_range_fade_mode")) {1856const GeometryInstance3D::VisibilityRangeFadeMode range_fade_mode = (GeometryInstance3D::VisibilityRangeFadeMode)node_settings["mesh_instance/visibility_range_fade_mode"].operator int();1857mi->set_visibility_range_fade_mode(range_fade_mode);1858}18591860if (node_settings.has("mesh_instance/cast_shadow")) {1861const GeometryInstance3D::ShadowCastingSetting cast_shadows = (GeometryInstance3D::ShadowCastingSetting)node_settings["mesh_instance/cast_shadow"].operator int();1862mi->set_cast_shadows_setting(cast_shadows);1863}1864}18651866if (Object::cast_to<AnimationPlayer>(p_node)) {1867AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);18681869for (int i = 0; i < post_importer_plugins.size(); i++) {1870post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, p_root, p_node, Ref<Resource>(), node_settings);1871}18721873if (post_importer_plugins.size()) {1874List<StringName> anims;1875ap->get_animation_list(&anims);1876for (const StringName &name : anims) {1877if (p_animation_data.has(name)) {1878Ref<Animation> anim = ap->get_animation(name);1879Dictionary anim_settings = p_animation_data[name];1880{1881//fill with default values1882List<ImportOption> iopts;1883get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION, &iopts);1884for (const ImportOption &F : iopts) {1885if (!anim_settings.has(F.option.name)) {1886anim_settings[F.option.name] = F.default_value;1887}1888}1889}18901891for (int i = 0; i < post_importer_plugins.size(); i++) {1892post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION, p_root, p_node, anim, anim_settings);1893}1894}1895}1896}1897}18981899String node_type = node_settings.get("node/node_type", "");1900Ref<Script> node_script = node_settings.get("node/script", Ref<Script>());1901p_node = _replace_node_with_type_and_script(p_node, node_type, node_script);19021903return p_node;1904}19051906Ref<Animation> ResourceImporterScene::_save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, const String &p_save_to_path, bool p_keep_custom_tracks) {1907String res_path = ResourceUID::ensure_path(p_save_to_path);1908if (!p_save_to_file || !res_path.is_resource_file()) {1909return anim;1910}19111912if (FileAccess::exists(res_path) && p_keep_custom_tracks) {1913// Copy custom animation tracks from previously imported files.1914Ref<Animation> old_anim = ResourceLoader::load(res_path, "Animation", ResourceFormatLoader::CACHE_MODE_IGNORE);1915if (old_anim.is_valid()) {1916for (int i = 0; i < old_anim->get_track_count(); i++) {1917if (!old_anim->track_is_imported(i)) {1918old_anim->copy_track(i, anim);1919}1920}1921anim->set_loop_mode(old_anim->get_loop_mode());1922}1923}19241925if (ResourceCache::has(res_path)) {1926Ref<Animation> old_anim = ResourceCache::get_ref(res_path);1927if (old_anim.is_valid()) {1928old_anim->copy_from(anim);1929anim = old_anim;1930}1931}1932anim->set_path(res_path, true); // Set path to save externally.1933Error err = ResourceSaver::save(anim, res_path, ResourceSaver::FLAG_CHANGE_PATH);19341935ERR_FAIL_COND_V_MSG(err != OK, anim, "Saving of animation failed: " + res_path);1936if (p_save_to_path.begins_with("uid://")) {1937// slow1938ResourceSaver::set_uid(res_path, ResourceUID::get_singleton()->text_to_id(p_save_to_path));1939}1940return anim;1941}19421943void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref<Animation> anim, const Array &p_slices, bool p_bake_all) {1944Ref<AnimationLibrary> al = ap->get_animation_library(ap->find_animation_library(anim));19451946for (int i = 0; i < p_slices.size(); i += 7) {1947String name = p_slices[i];1948float from = p_slices[i + 1];1949float to = p_slices[i + 2];1950Animation::LoopMode loop_mode = static_cast<Animation::LoopMode>((int)p_slices[i + 3]);1951bool save_to_file = p_slices[i + 4];1952String save_to_path = p_slices[i + 5];1953bool keep_current = p_slices[i + 6];1954if (from >= to) {1955continue;1956}19571958Ref<Animation> new_anim = memnew(Animation);19591960for (int j = 0; j < anim->get_track_count(); j++) {1961List<float> keys;1962int kc = anim->track_get_key_count(j);1963int dtrack = -1;1964for (int k = 0; k < kc; k++) {1965float kt = anim->track_get_key_time(j, k);1966if (kt >= from && kt < to) {1967//found a key within range, so create track1968if (dtrack == -1) {1969new_anim->add_track(anim->track_get_type(j));1970dtrack = new_anim->get_track_count() - 1;1971new_anim->track_set_path(dtrack, anim->track_get_path(j));1972new_anim->track_set_imported(dtrack, true);19731974if (kt > (from + 0.01) && k > 0) {1975if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {1976Vector3 p;1977anim->try_position_track_interpolate(j, from, &p);1978new_anim->position_track_insert_key(dtrack, 0, p);1979} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {1980Quaternion r;1981anim->try_rotation_track_interpolate(j, from, &r);1982new_anim->rotation_track_insert_key(dtrack, 0, r);1983} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {1984Vector3 s;1985anim->try_scale_track_interpolate(j, from, &s);1986new_anim->scale_track_insert_key(dtrack, 0, s);1987} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {1988Variant var = anim->value_track_interpolate(j, from);1989new_anim->track_insert_key(dtrack, 0, var);1990} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {1991float interp;1992anim->try_blend_shape_track_interpolate(j, from, &interp);1993new_anim->blend_shape_track_insert_key(dtrack, 0, interp);1994}1995}1996}19971998if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {1999Vector3 p;2000anim->position_track_get_key(j, k, &p);2001new_anim->position_track_insert_key(dtrack, kt - from, p);2002} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {2003Quaternion r;2004anim->rotation_track_get_key(j, k, &r);2005new_anim->rotation_track_insert_key(dtrack, kt - from, r);2006} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {2007Vector3 s;2008anim->scale_track_get_key(j, k, &s);2009new_anim->scale_track_insert_key(dtrack, kt - from, s);2010} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {2011Variant var = anim->track_get_key_value(j, k);2012new_anim->track_insert_key(dtrack, kt - from, var);2013} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {2014float interp;2015anim->blend_shape_track_get_key(j, k, &interp);2016new_anim->blend_shape_track_insert_key(dtrack, kt - from, interp);2017}2018}20192020if (dtrack != -1 && kt >= to) {2021if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {2022Vector3 p;2023anim->try_position_track_interpolate(j, to, &p);2024new_anim->position_track_insert_key(dtrack, to - from, p);2025} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {2026Quaternion r;2027anim->try_rotation_track_interpolate(j, to, &r);2028new_anim->rotation_track_insert_key(dtrack, to - from, r);2029} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {2030Vector3 s;2031anim->try_scale_track_interpolate(j, to, &s);2032new_anim->scale_track_insert_key(dtrack, to - from, s);2033} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {2034Variant var = anim->value_track_interpolate(j, to);2035new_anim->track_insert_key(dtrack, to - from, var);2036} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {2037float interp;2038anim->try_blend_shape_track_interpolate(j, to, &interp);2039new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);2040}2041}2042}20432044if (dtrack == -1 && p_bake_all) {2045new_anim->add_track(anim->track_get_type(j));2046dtrack = new_anim->get_track_count() - 1;2047new_anim->track_set_path(dtrack, anim->track_get_path(j));2048new_anim->track_set_imported(dtrack, true);2049if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {2050Vector3 p;2051anim->try_position_track_interpolate(j, from, &p);2052new_anim->position_track_insert_key(dtrack, 0, p);2053anim->try_position_track_interpolate(j, to, &p);2054new_anim->position_track_insert_key(dtrack, to - from, p);2055} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {2056Quaternion r;2057anim->try_rotation_track_interpolate(j, from, &r);2058new_anim->rotation_track_insert_key(dtrack, 0, r);2059anim->try_rotation_track_interpolate(j, to, &r);2060new_anim->rotation_track_insert_key(dtrack, to - from, r);2061} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {2062Vector3 s;2063anim->try_scale_track_interpolate(j, from, &s);2064new_anim->scale_track_insert_key(dtrack, 0, s);2065anim->try_scale_track_interpolate(j, to, &s);2066new_anim->scale_track_insert_key(dtrack, to - from, s);2067} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {2068Variant var = anim->value_track_interpolate(j, from);2069new_anim->track_insert_key(dtrack, 0, var);2070Variant to_var = anim->value_track_interpolate(j, to);2071new_anim->track_insert_key(dtrack, to - from, to_var);2072} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {2073float interp;2074anim->try_blend_shape_track_interpolate(j, from, &interp);2075new_anim->blend_shape_track_insert_key(dtrack, 0, interp);2076anim->try_blend_shape_track_interpolate(j, to, &interp);2077new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);2078}2079}2080}20812082new_anim->set_name(name);2083new_anim->set_loop_mode(loop_mode);2084new_anim->set_length(to - from);2085new_anim->set_step(anim->get_step());20862087al->add_animation(name, new_anim);20882089Ref<Animation> saved_anim = _save_animation_to_file(new_anim, save_to_file, save_to_path, keep_current);2090if (saved_anim != new_anim) {2091al->add_animation(name, saved_anim);2092}2093}20942095al->remove_animation(ap->find_animation(anim)); // Remove original animation (no longer needed).2096}20972098void ResourceImporterScene::_optimize_animations(AnimationPlayer *anim, float p_max_vel_error, float p_max_ang_error, int p_prc_error) {2099List<StringName> anim_names;2100anim->get_animation_list(&anim_names);2101for (const StringName &E : anim_names) {2102Ref<Animation> a = anim->get_animation(E);2103a->optimize(p_max_vel_error, p_max_ang_error, p_prc_error);2104}2105}21062107void ResourceImporterScene::_compress_animations(AnimationPlayer *anim, int p_page_size_kb) {2108List<StringName> anim_names;2109anim->get_animation_list(&anim_names);2110for (const StringName &E : anim_names) {2111Ref<Animation> a = anim->get_animation(E);2112a->compress(p_page_size_kb * 1024);2113}2114}21152116void ResourceImporterScene::get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const {2117switch (p_category) {2118case INTERNAL_IMPORT_CATEGORY_NODE: {2119r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "node/node_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));2120r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static()), Variant()));2121r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2122} break;2123case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {2124r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static()), Variant()));2125r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2126r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate/physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2127r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));2128r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/body_type", PROPERTY_HINT_ENUM, "StaticBody3D,RigidBody3D,Area3D"), 0));2129r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/shape_type", PROPERTY_HINT_ENUM, "Decompose Convex (Slow),Single Convex (Average),Trimesh (Slow),Box (Fast),Sphere (Fast),Cylinder (Average),Capsule (Fast),Automatic", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 7));2130r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "physics/physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, PhysicsMaterial::get_class_static()), Variant()));2131r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1));2132r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1));21332134r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mesh_instance/layers", PROPERTY_HINT_LAYERS_3D_RENDER), 1));2135r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "mesh_instance/visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), 0.0f));2136r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "mesh_instance/visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), 0.0f));2137r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "mesh_instance/visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), 0.0f));2138r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "mesh_instance/visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), 0.0f));2139r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mesh_instance/visibility_range_fade_mode", PROPERTY_HINT_ENUM, "Disabled,Self,Dependencies"), GeometryInstance3D::VISIBILITY_RANGE_FADE_DISABLED));2140r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mesh_instance/cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), GeometryInstance3D::SHADOW_CASTING_SETTING_ON));21412142// Decomposition2143Ref<MeshConvexDecompositionSettings> decomposition_default = Ref<MeshConvexDecompositionSettings>();2144decomposition_default.instantiate();2145r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/advanced", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2146r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/precision", PROPERTY_HINT_RANGE, "1,10,1"), 5));2147r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/max_concavity", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT), decomposition_default->get_max_concavity()));2148r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/symmetry_planes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT), decomposition_default->get_symmetry_planes_clipping_bias()));2149r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/revolution_axes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT), decomposition_default->get_revolution_axes_clipping_bias()));2150r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/min_volume_per_convex_hull", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_min_volume_per_convex_hull()));2151r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/resolution", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_resolution()));2152r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_num_vertices_per_convex_hull", PROPERTY_HINT_RANGE, "5,512,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_max_num_vertices_per_convex_hull()));2153r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/plane_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_plane_downsampling()));2154r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/convexhull_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_convex_hull_downsampling()));2155r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/normalize_mesh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_normalize_mesh()));2156r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/mode", PROPERTY_HINT_ENUM, "Voxel,Tetrahedron", PROPERTY_USAGE_DEFAULT), static_cast<int>(decomposition_default->get_mode())));2157r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/convexhull_approximation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_convex_hull_approximation()));2158r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_max_convex_hulls()));2159r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_project_hull_vertices()));21602161// Primitives: Box, Sphere, Cylinder, Capsule.2162r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3(2.0, 2.0, 2.0)));2163r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/height", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), 1.0));2164r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/radius", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), 1.0));2165r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3()));2166r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3()));21672168r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/occluder", PROPERTY_HINT_ENUM, "Disabled,Mesh + Occluder,Occluder Only", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));2169r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "occluder/simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01", PROPERTY_USAGE_DEFAULT), 0.1f));2170} break;2171case INTERNAL_IMPORT_CATEGORY_MESH: {2172r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2173r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.tres"), ""));2174r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));2175r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/shadow_meshes", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));2176r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lightmap_uv", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));2177r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));2178r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_merge_angle", PROPERTY_HINT_RANGE, "0,180,1,degrees"), 20.0f));2179} break;2180case INTERNAL_IMPORT_CATEGORY_MATERIAL: {2181r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "use_external/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2182r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "use_external/path", PROPERTY_HINT_FILE, "*.material,*.res,*.tres"), ""));2183r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "use_external/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));2184} break;2185case INTERNAL_IMPORT_CATEGORY_ANIMATION: {2186r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "settings/loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Pingpong"), 0));2187r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2188r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.anim,*.tres"), ""));2189r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));2190r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/keep_custom_tracks"), ""));2191r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));21922193for (int i = 0; i < 256; i++) {2194r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/name"), ""));2195r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/start_frame"), 0));2196r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/end_frame"), 0));2197r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Pingpong"), 0));2198r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2199r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.anim,*.tres"), ""));2200r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/save_to_file/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));2201r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"), false));2202}2203} break;2204case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {2205r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static()), Variant()));2206r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2207r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));2208r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_velocity_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));2209r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));2210r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "optimizer/max_precision_error", PROPERTY_HINT_NONE, "1,6,1"), 3));2211r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compression/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2212r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compression/page_size", PROPERTY_HINT_RANGE, "4,512,1,suffix:kb"), 8));2213r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));2214r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));2215r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));2216} break;2217case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {2218r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static()), Variant()));2219r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2220r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "rest_pose/load_pose", PROPERTY_HINT_ENUM, "Default Pose,Use AnimationPlayer,Load External Animation", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));2221r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "rest_pose/external_animation_library", PROPERTY_HINT_RESOURCE_TYPE, "Animation,AnimationLibrary", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Variant()));2222r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "rest_pose/selected_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), ""));2223r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "rest_pose/selected_timestamp", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,suffix:s", PROPERTY_USAGE_DEFAULT), 0.0f));2224String mismatched_or_empty_profile_warning = String(2225"The external rest animation is missing some bones. "2226"Consider disabling Remove Immutable Tracks on the other file."); // TODO: translate.2227r_options->push_back(ImportOption(2228PropertyInfo(2229Variant::STRING, U"rest_pose/\u26A0_validation_warning/mismatched_or_empty_profile",2230PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),2231Variant(mismatched_or_empty_profile_warning)));2232String profile_must_not_be_retargeted_warning = String(2233"This external rest animation appears to have been imported with a BoneMap. "2234"Disable the bone map when exporting a rest animation from the reference model."); // TODO: translate.2235r_options->push_back(ImportOption(2236PropertyInfo(2237Variant::STRING, U"rest_pose/\u26A0_validation_warning/profile_must_not_be_retargeted",2238PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),2239Variant(profile_must_not_be_retargeted_warning)));2240String no_animation_warning = String(2241"Select an animation: Find a FBX or glTF in a compatible rest pose "2242"and export a compatible animation from its import settings."); // TODO: translate.2243r_options->push_back(ImportOption(2244PropertyInfo(2245Variant::STRING, U"rest_pose//no_animation_chosen",2246PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),2247Variant(no_animation_warning)));2248r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "retarget/bone_map", PROPERTY_HINT_RESOURCE_TYPE, BoneMap::get_class_static(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Variant()));2249} break;2250default: {2251}2252}22532254for (int i = 0; i < post_importer_plugins.size(); i++) {2255post_importer_plugins.write[i]->get_internal_import_options(EditorScenePostImportPlugin::InternalImportCategory(p_category), r_options);2256}2257}22582259bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const {2260if (p_options.has("import/skip_import") && p_option != "import/skip_import" && bool(p_options["import/skip_import"])) {2261return false; //if skip import2262}2263switch (p_category) {2264case INTERNAL_IMPORT_CATEGORY_NODE: {2265} break;2266case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {2267const bool generate_physics =2268p_options.has("generate/physics") &&2269p_options["generate/physics"].operator bool();22702271if (p_option.contains("physics/")) {2272// Show if need to generate collisions.2273return generate_physics;2274}22752276if (p_option.contains("decomposition/")) {2277// Show if need to generate collisions.2278if (generate_physics &&2279// Show if convex is enabled.2280p_options["physics/shape_type"] == Variant(SHAPE_TYPE_DECOMPOSE_CONVEX)) {2281if (p_option == "decomposition/advanced") {2282return true;2283}22842285const bool decomposition_advanced =2286p_options.has("decomposition/advanced") &&2287p_options["decomposition/advanced"].operator bool();22882289if (p_option == "decomposition/precision") {2290return !decomposition_advanced;2291} else {2292return decomposition_advanced;2293}2294}22952296return false;2297}22982299if (p_option == "primitive/position" || p_option == "primitive/rotation") {2300const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();2301return generate_physics &&2302physics_shape >= SHAPE_TYPE_BOX;2303}23042305if (p_option == "primitive/size") {2306const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();2307return generate_physics &&2308physics_shape == SHAPE_TYPE_BOX;2309}23102311if (p_option == "primitive/radius") {2312const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();2313return generate_physics &&2314(physics_shape == SHAPE_TYPE_SPHERE ||2315physics_shape == SHAPE_TYPE_CYLINDER ||2316physics_shape == SHAPE_TYPE_CAPSULE);2317}23182319if (p_option == "primitive/height") {2320const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();2321return generate_physics &&2322(physics_shape == SHAPE_TYPE_CYLINDER ||2323physics_shape == SHAPE_TYPE_CAPSULE);2324}23252326if (p_option == "occluder/simplification_distance") {2327// Show only if occluder generation is enabled2328return p_options.has("generate/occluder") && p_options["generate/occluder"].operator signed int() != OCCLUDER_DISABLED;2329}2330} break;2331case INTERNAL_IMPORT_CATEGORY_MESH: {2332if (p_option == "save_to_file/path") {2333return p_options["save_to_file/enabled"];2334}2335} break;2336case INTERNAL_IMPORT_CATEGORY_MATERIAL: {2337if (p_option == "use_external/path") {2338return p_options["use_external/enabled"];2339}2340} break;2341case INTERNAL_IMPORT_CATEGORY_ANIMATION: {2342if (p_option == "save_to_file/path" || p_option == "save_to_file/keep_custom_tracks") {2343return p_options["save_to_file/enabled"];2344}2345if (p_option.begins_with("slice_")) {2346int max_slice = p_options["slices/amount"];2347int slice = p_option.get_slicec('_', 1).to_int() - 1;2348if (slice >= max_slice) {2349return false;2350}2351}2352} break;2353case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {2354if (p_option.begins_with("optimizer/") && p_option != "optimizer/enabled" && !bool(p_options["optimizer/enabled"])) {2355return false;2356}2357if (p_option.begins_with("compression/") && p_option != "compression/enabled" && !bool(p_options["compression/enabled"])) {2358return false;2359}2360} break;2361case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {2362const bool use_retarget = Object::cast_to<BoneMap>(p_options["retarget/bone_map"].get_validated_object()) != nullptr;2363if (!use_retarget && p_option != "retarget/bone_map" && p_option.begins_with("retarget/")) {2364return false;2365}2366int rest_warning = 0;2367if (p_option.begins_with("rest_pose/")) {2368if (!p_options.has("rest_pose/load_pose") || int(p_options["rest_pose/load_pose"]) == 0) {2369if (p_option != "rest_pose/load_pose") {2370return false;2371}2372} else if (int(p_options["rest_pose/load_pose"]) == 1) {2373if (p_option == "rest_pose/external_animation_library") {2374return false;2375}2376} else if (int(p_options["rest_pose/load_pose"]) == 2) {2377Object *res = p_options["rest_pose/external_animation_library"];2378Ref<Animation> anim(res);2379if (anim.is_valid() && p_option == "rest_pose/selected_animation") {2380return false;2381}2382Ref<AnimationLibrary> library(res);2383String selected_animation_name = p_options["rest_pose/selected_animation"];2384if (library.is_valid()) {2385List<StringName> anim_list;2386library->get_animation_list(&anim_list);2387if (anim_list.size() == 1) {2388selected_animation_name = String(anim_list.front()->get());2389}2390if (library->has_animation(selected_animation_name)) {2391anim = library->get_animation(selected_animation_name);2392}2393}2394int found_bone_count = 0;2395Ref<BoneMap> bone_map;2396Ref<SkeletonProfile> prof;2397if (p_options.has("retarget/bone_map")) {2398bone_map = p_options["retarget/bone_map"];2399}2400if (bone_map.is_valid()) {2401prof = bone_map->get_profile();2402}2403if (anim.is_valid()) {2404HashSet<StringName> target_bones;2405if (bone_map.is_valid() && prof.is_valid()) {2406for (int target_i = 0; target_i < prof->get_bone_size(); target_i++) {2407StringName skeleton_bone_name = bone_map->get_skeleton_bone_name(prof->get_bone_name(target_i));2408if (skeleton_bone_name) {2409target_bones.insert(skeleton_bone_name);2410}2411}2412}2413for (int track_i = 0; track_i < anim->get_track_count(); track_i++) {2414if (anim->track_get_type(track_i) != Animation::TYPE_POSITION_3D && anim->track_get_type(track_i) != Animation::TYPE_ROTATION_3D) {2415continue;2416}2417NodePath path = anim->track_get_path(track_i);2418StringName node_path = path.get_concatenated_names();2419StringName skeleton_bone = path.get_concatenated_subnames();2420if (skeleton_bone) {2421if (String(node_path).begins_with("%")) {2422rest_warning = 1;2423}2424if (target_bones.has(skeleton_bone)) {2425target_bones.erase(skeleton_bone);2426}2427found_bone_count++;2428}2429}2430if ((found_bone_count < 15 || !target_bones.is_empty()) && rest_warning != 1) {2431rest_warning = 2; // heuristic: animation targeted too few bones.2432}2433} else {2434rest_warning = 3;2435}2436}2437if (p_option.begins_with("rest_pose/") && p_option.ends_with("profile_must_not_be_retargeted")) {2438return rest_warning == 1;2439}2440if (p_option.begins_with("rest_pose/") && p_option.ends_with("mismatched_or_empty_profile")) {2441return rest_warning == 2;2442}2443if (p_option.begins_with("rest_pose/") && p_option.ends_with("no_animation_chosen")) {2444return rest_warning == 3;2445}2446}2447} break;2448default: {2449}2450}24512452// TODO: If there are more than 2 or equal get_internal_option_visibility method, visibility state is broken.2453for (int i = 0; i < post_importer_plugins.size(); i++) {2454Variant ret = post_importer_plugins.write[i]->get_internal_option_visibility(EditorScenePostImportPlugin::InternalImportCategory(p_category), _scene_import_type, p_option, p_options);2455if (ret.get_type() == Variant::BOOL) {2456return ret;2457}2458}24592460return true;2461}24622463bool ResourceImporterScene::get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const {2464switch (p_category) {2465case INTERNAL_IMPORT_CATEGORY_NODE: {2466} break;2467case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {2468if (2469p_option == "generate/physics" ||2470p_option == "physics/shape_type" ||2471p_option.contains("decomposition/") ||2472p_option.contains("primitive/")) {2473return true;2474}2475} break;2476case INTERNAL_IMPORT_CATEGORY_MESH: {2477} break;2478case INTERNAL_IMPORT_CATEGORY_MATERIAL: {2479} break;2480case INTERNAL_IMPORT_CATEGORY_ANIMATION: {2481} break;2482case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {2483} break;2484case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {2485} break;2486default: {2487}2488}24892490for (int i = 0; i < post_importer_plugins.size(); i++) {2491Variant ret = post_importer_plugins.write[i]->get_internal_option_update_view_required(EditorScenePostImportPlugin::InternalImportCategory(p_category), p_option, p_options);2492if (ret.get_type() == Variant::BOOL) {2493return ret;2494}2495}24962497return false;2498}24992500void ResourceImporterScene::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {2501r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));2502r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_name"), ""));2503r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "nodes/root_script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static()), Variant()));25042505List<String> script_extensions;2506ResourceLoader::get_recognized_extensions_for_type("Script", &script_extensions);25072508String script_ext_hint;25092510for (const String &E : script_extensions) {2511if (!script_ext_hint.is_empty()) {2512script_ext_hint += ",";2513}2514script_ext_hint += "*." + E;2515}2516bool trimming_defaults_on = p_path.has_extension("fbx");25172518r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true));2519r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0));2520r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/import_as_skeleton_bones"), false));2521r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/use_name_suffixes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));2522r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/use_node_type_suffixes"), true));2523r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));2524r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true));2525r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));2526r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Static,Static Lightmaps,Dynamic", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));2527r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2));2528r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/force_disable_compression"), false));2529r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));2530r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));2531r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));2532r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), trimming_defaults_on));2533r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true));2534r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import_rest_as_RESET"), false));2535r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));2536r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/extract", PROPERTY_HINT_ENUM, "Keep Internal,Extract Once,Extract and Overwrite"), 0));2537r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/extract_format", PROPERTY_HINT_ENUM, "Text (*.tres),Binary (*.res),Material (*.material)"), 0));2538r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "materials/extract_path", PROPERTY_HINT_DIR, ""), ""));25392540r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Dictionary()));25412542for (int i = 0; i < post_importer_plugins.size(); i++) {2543post_importer_plugins.write[i]->get_import_options(p_path, r_options);2544}25452546for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {2547importer_elem->get_import_options(p_path, r_options);2548}2549}25502551void ResourceImporterScene::handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {2552for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {2553importer_elem->handle_compatibility_options(p_import_params);2554}2555}25562557void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner) {2558if (p_node != p_new_owner && p_node->get_owner() == p_scene) {2559p_node->set_owner(p_new_owner);2560}25612562for (int i = 0; i < p_node->get_child_count(); i++) {2563Node *n = p_node->get_child(i);2564_replace_owner(n, p_scene, p_new_owner);2565}2566}25672568Array ResourceImporterScene::_get_skinned_pose_transforms(ImporterMeshInstance3D *p_src_mesh_node) {2569Array skin_pose_transform_array;25702571const Ref<Skin> skin = p_src_mesh_node->get_skin();2572if (skin.is_valid()) {2573NodePath skeleton_path = p_src_mesh_node->get_skeleton_path();2574const Node *node = p_src_mesh_node->get_node_or_null(skeleton_path);2575const Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);2576if (skeleton) {2577int bind_count = skin->get_bind_count();25782579for (int i = 0; i < bind_count; i++) {2580Transform3D bind_pose = skin->get_bind_pose(i);2581String bind_name = skin->get_bind_name(i);25822583int bone_idx = bind_name.is_empty() ? skin->get_bind_bone(i) : skeleton->find_bone(bind_name);2584ERR_FAIL_COND_V(bone_idx >= skeleton->get_bone_count(), Array());25852586Transform3D bp_global_rest;2587if (bone_idx >= 0) {2588bp_global_rest = skeleton->get_bone_global_pose(bone_idx);2589} else {2590bp_global_rest = skeleton->get_bone_global_pose(i);2591}25922593skin_pose_transform_array.push_back(bp_global_rest * bind_pose);2594}2595}2596}25972598return skin_pose_transform_array;2599}26002601Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches) {2602ImporterMeshInstance3D *src_mesh_node = Object::cast_to<ImporterMeshInstance3D>(p_node);2603if (src_mesh_node) {2604//is mesh2605MeshInstance3D *mesh_node = memnew(MeshInstance3D);2606mesh_node->set_name(src_mesh_node->get_name());2607mesh_node->set_transform(src_mesh_node->get_transform());2608mesh_node->set_skin(src_mesh_node->get_skin());2609mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());2610mesh_node->merge_meta_from(src_mesh_node);26112612Ref<ImporterMesh> importer_mesh = src_mesh_node->get_mesh();2613if (importer_mesh.is_valid()) {2614Ref<ArrayMesh> mesh;2615if (!importer_mesh->has_mesh()) {2616//do mesh processing26172618bool generate_lods = p_generate_lods;2619float merge_angle = 20.0f;2620bool create_shadow_meshes = p_create_shadow_meshes;2621bool bake_lightmaps = p_light_bake_mode == LIGHT_BAKE_STATIC_LIGHTMAPS;2622String save_to_file;26232624String mesh_id = importer_mesh->get_meta("import_id", importer_mesh->get_name());26252626if (!mesh_id.is_empty() && p_mesh_data.has(mesh_id)) {2627Dictionary mesh_settings = p_mesh_data[mesh_id];2628{2629//fill node settings for this node with default values2630List<ImportOption> iopts;2631get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MESH, &iopts);2632for (const ImportOption &E : iopts) {2633if (!mesh_settings.has(E.option.name)) {2634mesh_settings[E.option.name] = E.default_value;2635}2636}2637}26382639if (mesh_settings.has("generate/shadow_meshes")) {2640int shadow_meshes = mesh_settings["generate/shadow_meshes"];2641if (shadow_meshes == MESH_OVERRIDE_ENABLE) {2642create_shadow_meshes = true;2643} else if (shadow_meshes == MESH_OVERRIDE_DISABLE) {2644create_shadow_meshes = false;2645}2646}26472648if (mesh_settings.has("generate/lightmap_uv")) {2649int lightmap_uv = mesh_settings["generate/lightmap_uv"];2650if (lightmap_uv == MESH_OVERRIDE_ENABLE) {2651bake_lightmaps = true;2652} else if (lightmap_uv == MESH_OVERRIDE_DISABLE) {2653bake_lightmaps = false;2654}2655}26562657if (mesh_settings.has("generate/lods")) {2658int lods = mesh_settings["generate/lods"];2659if (lods == MESH_OVERRIDE_ENABLE) {2660generate_lods = true;2661} else if (lods == MESH_OVERRIDE_DISABLE) {2662generate_lods = false;2663}2664}26652666if (mesh_settings.has("lods/normal_merge_angle")) {2667merge_angle = mesh_settings["lods/normal_merge_angle"];2668}26692670if (bool(mesh_settings.get("save_to_file/enabled", false))) {2671save_to_file = mesh_settings.get("save_to_file/path", String());2672if (!ResourceUID::ensure_path(save_to_file).is_resource_file()) {2673save_to_file = "";2674}2675}26762677for (int i = 0; i < post_importer_plugins.size(); i++) {2678post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH, nullptr, src_mesh_node, importer_mesh, mesh_settings);2679}2680}26812682if (bake_lightmaps) {2683Transform3D xf;2684Node3D *n = src_mesh_node;2685while (n) {2686xf = n->get_transform() * xf;2687n = n->get_parent_node_3d();2688}26892690Vector<uint8_t> lightmap_cache;2691importer_mesh->lightmap_unwrap_cached(xf, p_lightmap_texel_size, p_src_lightmap_cache, lightmap_cache);26922693if (!lightmap_cache.is_empty()) {2694if (r_lightmap_caches.is_empty()) {2695r_lightmap_caches.push_back(lightmap_cache);2696} else {2697String new_md5 = String::md5(lightmap_cache.ptr()); // MD5 is stored at the beginning of the cache data26982699for (int i = 0; i < r_lightmap_caches.size(); i++) {2700String md5 = String::md5(r_lightmap_caches[i].ptr());2701if (new_md5 < md5) {2702r_lightmap_caches.insert(i, lightmap_cache);2703break;2704}27052706if (new_md5 == md5) {2707break;2708}2709}2710}2711}2712}27132714if (generate_lods) {2715Array skin_pose_transform_array = _get_skinned_pose_transforms(src_mesh_node);2716importer_mesh->generate_lods(merge_angle, skin_pose_transform_array);2717}27182719if (create_shadow_meshes) {2720importer_mesh->create_shadow_mesh();2721}27222723importer_mesh->optimize_indices();27242725if (!save_to_file.is_empty()) {2726String save_res_path = ResourceUID::ensure_path(save_to_file);2727Ref<Mesh> existing = ResourceCache::get_ref(save_res_path);2728if (existing.is_valid()) {2729//if somehow an existing one is useful, create2730existing->reset_state();2731}2732mesh = importer_mesh->get_mesh(existing);27332734Error err = ResourceSaver::save(mesh, save_res_path); //override2735if (err != OK) {2736WARN_PRINT(vformat("Failed to save mesh %s to '%s'.", mesh->get_name(), save_res_path));2737}2738if (err == OK && save_to_file.begins_with("uid://")) {2739// slow2740ResourceSaver::set_uid(save_res_path, ResourceUID::get_singleton()->text_to_id(save_to_file));2741}27422743mesh->set_path(save_res_path, true); //takeover existing, if needed27442745} else {2746mesh = importer_mesh->get_mesh();2747}2748} else {2749mesh = importer_mesh->get_mesh();2750}27512752if (mesh.is_valid()) {2753_copy_meta(importer_mesh.ptr(), mesh.ptr());2754mesh_node->set_mesh(mesh);2755for (int i = 0; i < mesh->get_surface_count(); i++) {2756mesh_node->set_surface_override_material(i, src_mesh_node->get_surface_material(i));2757}2758mesh->merge_meta_from(*importer_mesh);2759}2760}27612762switch (p_light_bake_mode) {2763case LIGHT_BAKE_DISABLED: {2764mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_DISABLED);2765} break;2766case LIGHT_BAKE_DYNAMIC: {2767mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_DYNAMIC);2768} break;2769case LIGHT_BAKE_STATIC:2770case LIGHT_BAKE_STATIC_LIGHTMAPS: {2771mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_STATIC);2772} break;2773}27742775mesh_node->set_layer_mask(src_mesh_node->get_layer_mask());2776mesh_node->set_cast_shadows_setting(src_mesh_node->get_cast_shadows_setting());2777mesh_node->set_visible(src_mesh_node->is_visible());2778mesh_node->set_visibility_range_begin(src_mesh_node->get_visibility_range_begin());2779mesh_node->set_visibility_range_begin_margin(src_mesh_node->get_visibility_range_begin_margin());2780mesh_node->set_visibility_range_end(src_mesh_node->get_visibility_range_end());2781mesh_node->set_visibility_range_end_margin(src_mesh_node->get_visibility_range_end_margin());2782mesh_node->set_visibility_range_fade_mode(src_mesh_node->get_visibility_range_fade_mode());27832784_copy_meta(p_node, mesh_node);27852786p_node->replace_by(mesh_node);2787p_node->set_owner(nullptr);2788memdelete(p_node);2789p_node = mesh_node;2790}27912792for (int i = 0; i < p_node->get_child_count(); i++) {2793_generate_meshes(p_node->get_child(i), p_mesh_data, p_generate_lods, p_create_shadow_meshes, p_light_bake_mode, p_lightmap_texel_size, p_src_lightmap_cache, r_lightmap_caches);2794}27952796return p_node;2797}27982799void ResourceImporterScene::_add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes) {2800for (const Ref<Shape3D> &E : p_shapes) {2801CollisionShape3D *cshape = memnew(CollisionShape3D);2802cshape->set_shape(E);2803p_node->add_child(cshape, true);28042805cshape->set_owner(p_node->get_owner());2806}2807}28082809void ResourceImporterScene::_copy_meta(Object *p_src_object, Object *p_dst_object) {2810List<StringName> meta_list;2811p_src_object->get_meta_list(&meta_list);2812for (const StringName &meta_key : meta_list) {2813Variant meta_value = p_src_object->get_meta(meta_key);2814p_dst_object->set_meta(meta_key, meta_value);2815}2816}28172818void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions) {2819List<StringName> anims;2820p_player->get_animation_list(&anims);2821Node *parent = p_player->get_parent();2822ERR_FAIL_NULL(parent);2823HashMap<NodePath, uint32_t> used_tracks[TRACK_CHANNEL_MAX];2824bool tracks_to_add = false;2825static const Animation::TrackType track_types[TRACK_CHANNEL_MAX] = { Animation::TYPE_POSITION_3D, Animation::TYPE_ROTATION_3D, Animation::TYPE_SCALE_3D, Animation::TYPE_BLEND_SHAPE };2826for (const StringName &I : anims) {2827Ref<Animation> anim = p_player->get_animation(I);2828for (int i = 0; i < anim->get_track_count(); i++) {2829for (int j = 0; j < TRACK_CHANNEL_MAX; j++) {2830if (anim->track_get_type(i) != track_types[j]) {2831continue;2832}2833switch (p_track_actions[j]) {2834case ANIMATION_IMPORT_TRACKS_IF_PRESENT: {2835// Do Nothing.2836} break;2837case ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL: {2838used_tracks[j].insert(anim->track_get_path(i), 0);2839tracks_to_add = true;2840} break;2841case ANIMATION_IMPORT_TRACKS_NEVER: {2842anim->remove_track(i);2843i--;2844} break;2845}2846}2847}2848}28492850if (!tracks_to_add) {2851return;2852}28532854uint32_t pass = 0;2855for (const StringName &I : anims) {2856Ref<Animation> anim = p_player->get_animation(I);2857for (int j = 0; j < TRACK_CHANNEL_MAX; j++) {2858if (p_track_actions[j] != ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {2859continue;2860}28612862pass++;28632864for (int i = 0; i < anim->get_track_count(); i++) {2865if (anim->track_get_type(i) != track_types[j]) {2866continue;2867}28682869NodePath path = anim->track_get_path(i);28702871ERR_CONTINUE(!used_tracks[j].has(path)); // Should never happen.28722873used_tracks[j][path] = pass;2874}28752876for (const KeyValue<NodePath, uint32_t> &J : used_tracks[j]) {2877if (J.value == pass) {2878continue;2879}28802881NodePath path = J.key;2882Node *n = parent->get_node(path);28832884if (j == TRACK_CHANNEL_BLEND_SHAPE) {2885MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(n);2886if (mi && path.get_subname_count() > 0) {2887StringName bs = path.get_subname(0);2888bool valid;2889float value = mi->get(bs, &valid);2890if (valid) {2891int track_idx = anim->add_track(track_types[j]);2892anim->track_set_path(track_idx, path);2893anim->track_set_imported(track_idx, true);2894anim->blend_shape_track_insert_key(track_idx, 0, value);2895}2896}28972898} else {2899Skeleton3D *skel = Object::cast_to<Skeleton3D>(n);2900Node3D *n3d = Object::cast_to<Node3D>(n);2901Vector3 loc;2902Quaternion rot;2903Vector3 scale;2904if (skel && path.get_subname_count() > 0) {2905StringName bone = path.get_subname(0);2906int bone_idx = skel->find_bone(bone);2907if (bone_idx == -1) {2908continue;2909}2910// Note that this is using get_bone_pose to update the bone pose cache.2911_ALLOW_DISCARD_ skel->get_bone_pose(bone_idx);2912loc = skel->get_bone_pose_position(bone_idx);2913rot = skel->get_bone_pose_rotation(bone_idx);2914scale = skel->get_bone_pose_scale(bone_idx);2915} else if (n3d) {2916loc = n3d->get_position();2917rot = n3d->get_transform().basis.get_rotation_quaternion();2918scale = n3d->get_scale();2919} else {2920continue;2921}29222923// Ensure insertion keeps tracks together and ordered by type (loc/rot/scale)2924int insert_at_pos = -1;2925for (int k = 0; k < anim->get_track_count(); k++) {2926NodePath tpath = anim->track_get_path(k);29272928if (path == tpath) {2929Animation::TrackType ttype = anim->track_get_type(k);2930if (insert_at_pos == -1) {2931// First insert, determine whether replacing or kicking back2932if (track_types[j] < ttype) {2933insert_at_pos = k;2934break; // No point in continuing.2935} else {2936insert_at_pos = k + 1;2937}2938} else if (ttype < track_types[j]) {2939// Kick back.2940insert_at_pos = k + 1;2941}2942} else if (insert_at_pos >= 0) {2943break;2944}2945}2946int track_idx = anim->add_track(track_types[j], insert_at_pos);29472948anim->track_set_path(track_idx, path);2949anim->track_set_imported(track_idx, true);2950switch (j) {2951case TRACK_CHANNEL_POSITION: {2952anim->position_track_insert_key(track_idx, 0, loc);2953} break;2954case TRACK_CHANNEL_ROTATION: {2955anim->rotation_track_insert_key(track_idx, 0, rot);2956} break;2957case TRACK_CHANNEL_SCALE: {2958anim->scale_track_insert_key(track_idx, 0, scale);2959} break;2960default: {2961}2962}2963}2964}2965}2966}2967}29682969Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashMap<StringName, Variant> &p_options) {2970Ref<EditorSceneFormatImporter> importer;2971String ext = p_source_file.get_extension().to_lower();29722973// TRANSLATORS: This is an editor progress label.2974EditorProgress progress("pre-import", TTR("Pre-Import Scene"), 0);2975progress.step(TTR("Importing Scene..."), 0);29762977for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {2978List<String> extensions;2979importer_elem->get_extensions(&extensions);29802981for (const String &F : extensions) {2982if (F.to_lower() == ext) {2983importer = importer_elem;2984break;2985}2986}29872988if (importer.is_valid()) {2989break;2990}2991}29922993ERR_FAIL_COND_V(importer.is_null(), nullptr);2994ERR_FAIL_COND_V(p_options.is_empty(), nullptr);29952996Error err = OK;29972998Node *scene = importer->import_scene(p_source_file, EditorSceneFormatImporter::IMPORT_ANIMATION | EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS | EditorSceneFormatImporter::IMPORT_FORCE_DISABLE_MESH_COMPRESSION, p_options, nullptr, &err);2999if (!scene || err != OK) {3000return nullptr;3001}30023003_pre_fix_global(scene, p_options);30043005HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;3006List<Pair<NodePath, Node *>> node_renames;3007_pre_fix_node(scene, scene, collision_map, nullptr, node_renames, p_options);30083009return scene;3010}30113012static Error convert_path_to_uid(ResourceUID::ID p_source_id, const String &p_hash_str, Dictionary &p_settings, const String &p_path_key, const String &p_fallback_path_key) {3013const String &raw_save_path = p_settings[p_path_key];3014String save_path = ResourceUID::ensure_path(raw_save_path);3015if (raw_save_path.begins_with("uid://")) {3016if (save_path.is_empty() || !DirAccess::exists(save_path.get_base_dir())) {3017if (p_settings.has(p_fallback_path_key)) {3018String fallback_save_path = p_settings[p_fallback_path_key];3019if (!fallback_save_path.is_empty() && DirAccess::exists(fallback_save_path.get_base_dir())) {3020save_path = fallback_save_path;3021ResourceUID::get_singleton()->add_id(ResourceUID::get_singleton()->text_to_id(raw_save_path), save_path);3022}3023}3024} else {3025p_settings[p_fallback_path_key] = save_path;3026}3027}3028ERR_FAIL_COND_V(!save_path.is_empty() && !DirAccess::exists(save_path.get_base_dir()), ERR_FILE_BAD_PATH);3029if (!save_path.is_empty() && !raw_save_path.begins_with("uid://")) {3030const ResourceUID::ID id = ResourceLoader::get_resource_uid(save_path);3031if (id != ResourceUID::INVALID_ID) {3032p_settings[p_path_key] = ResourceUID::get_singleton()->id_to_text(id);3033} else {3034ResourceUID::ID save_id = hash64_murmur3_64(p_hash_str.hash64(), p_source_id) & 0x7FFFFFFFFFFFFFFF;3035if (ResourceUID::get_singleton()->has_id(save_id)) {3036if (save_path != ResourceUID::get_singleton()->get_id_path(save_id)) {3037// The user has specified a path which does not match the default UID.3038save_id = ResourceUID::get_singleton()->create_id_for_path(save_path);3039}3040}3041p_settings[p_path_key] = ResourceUID::get_singleton()->id_to_text(save_id);3042ResourceUID::get_singleton()->add_id(save_id, save_path);3043}3044p_settings[p_fallback_path_key] = save_path;3045}3046return OK;3047}30483049Error ResourceImporterScene::_check_resource_save_paths(ResourceUID::ID p_source_id, const String &p_hash_suffix, const Dictionary &p_data) {3050for (const KeyValue<Variant, Variant> &kv : p_data) {3051Dictionary settings = kv.value;30523053if (bool(settings.get("save_to_file/enabled", false)) && settings.has("save_to_file/path")) {3054String to_hash = kv.key.operator String() + p_hash_suffix;3055Error ret = convert_path_to_uid(p_source_id, to_hash, settings, "save_to_file/path", "save_to_file/fallback_path");3056ERR_FAIL_COND_V_MSG(ret != OK, ret, vformat("Resource save path %s not valid. Ensure parent directory has been created.", settings.has("save_to_file/path")));3057}30583059if (settings.has("slices/amount")) {3060int slice_count = settings["slices/amount"];3061for (int si = 0; si < slice_count; si++) {3062if (bool(settings.get("slice_" + itos(si + 1) + "/save_to_file/enabled", false)) &&3063settings.has("slice_" + itos(si + 1) + "/save_to_file/path")) {3064String to_hash = kv.key.operator String() + p_hash_suffix + itos(si + 1);3065Error ret = convert_path_to_uid(p_source_id, to_hash, settings,3066"slice_" + itos(si + 1) + "/save_to_file/path",3067"slice_" + itos(si + 1) + "/save_to_file/fallback_path");3068ERR_FAIL_COND_V_MSG(ret != OK, ret, vformat("Slice save path %s not valid. Ensure parent directory has been created.", settings.has("save_to_file/path")));3069}3070}3071}3072}30733074return OK;3075}30763077Error ResourceImporterScene::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {3078const String &src_path = p_source_file;30793080Ref<EditorSceneFormatImporter> importer;3081String ext = src_path.get_extension().to_lower();30823083EditorProgress progress("import", TTR("Import Scene"), 104);3084progress.step(TTR("Importing Scene..."), 0);30853086for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {3087List<String> extensions;3088importer_elem->get_extensions(&extensions);30893090for (const String &F : extensions) {3091if (F.to_lower() == ext) {3092importer = importer_elem;3093break;3094}3095}30963097if (importer.is_valid()) {3098break;3099}3100}31013102ERR_FAIL_COND_V(importer.is_null(), ERR_FILE_UNRECOGNIZED);3103ERR_FAIL_COND_V(p_options.is_empty(), ERR_BUG);31043105int import_flags = 0;31063107if (_scene_import_type == "AnimationLibrary") {3108import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION;3109import_flags |= EditorSceneFormatImporter::IMPORT_DISCARD_MESHES_AND_MATERIALS;3110} else if (bool(p_options["animation/import"])) {3111import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION;3112}31133114if (bool(p_options["skins/use_named_skins"])) {3115import_flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;3116}31173118bool ensure_tangents = p_options["meshes/ensure_tangents"];3119if (ensure_tangents) {3120import_flags |= EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS;3121}31223123bool force_disable_compression = p_options["meshes/force_disable_compression"];3124if (force_disable_compression) {3125import_flags |= EditorSceneFormatImporter::IMPORT_FORCE_DISABLE_MESH_COMPRESSION;3126}31273128Dictionary subresources = p_options["_subresources"];31293130Error err = OK;31313132// Check whether any of the meshes or animations have nonexistent save paths3133// and if they do, fail the import immediately.3134if (subresources.has("meshes")) {3135err = _check_resource_save_paths(p_source_id, "m", subresources["meshes"]);3136if (err != OK) {3137return err;3138}3139}31403141if (subresources.has("animations")) {3142err = _check_resource_save_paths(p_source_id, "a", subresources["animations"]);3143if (err != OK) {3144return err;3145}3146}31473148List<String> missing_deps; // for now, not much will be done with this3149Node *scene = importer->import_scene(src_path, import_flags, p_options, &missing_deps, &err);3150if (!scene || err != OK) {3151return err;3152}31533154bool apply_root = true;3155if (p_options.has("nodes/apply_root_scale")) {3156apply_root = p_options["nodes/apply_root_scale"];3157}3158real_t root_scale = 1;3159if (p_options.has("nodes/root_scale")) {3160root_scale = p_options["nodes/root_scale"];3161}3162if (Object::cast_to<Node3D>(scene)) {3163Node3D *scene_3d = Object::cast_to<Node3D>(scene);3164Vector3 scale = Vector3(root_scale, root_scale, root_scale);3165if (apply_root) {3166_apply_permanent_scale_to_descendants(scene, scale);3167} else {3168scene_3d->scale(scale);3169}3170}31713172_pre_fix_global(scene, p_options);31733174HashSet<Ref<ImporterMesh>> scanned_meshes;3175HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;3176Pair<PackedVector3Array, PackedInt32Array> occluder_arrays;3177List<Pair<NodePath, Node *>> node_renames;31783179_pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames, p_options);31803181for (int i = 0; i < post_importer_plugins.size(); i++) {3182post_importer_plugins.write[i]->pre_process(scene, p_options);3183}31843185// data in _subresources may be modified by pre_process(), so wait until now to check.3186Dictionary node_data;3187if (subresources.has("nodes")) {3188node_data = subresources["nodes"];3189}31903191Dictionary material_data;3192if (subresources.has("materials")) {3193material_data = subresources["materials"];3194}31953196Dictionary animation_data;3197if (subresources.has("animations")) {3198animation_data = subresources["animations"];3199}32003201Dictionary mesh_data;3202if (subresources.has("meshes")) {3203mesh_data = subresources["meshes"];3204}32053206float fps = 30;3207if (p_options.has(SNAME("animation/fps"))) {3208fps = (float)p_options[SNAME("animation/fps")];3209}3210bool remove_immutable_tracks = p_options.has("animation/remove_immutable_tracks") ? (bool)p_options["animation/remove_immutable_tracks"] : true;3211_pre_fix_animations(scene, scene, node_data, animation_data, fps);3212_post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps, apply_root ? root_scale : 1.0, p_source_file, p_options);3213_post_fix_animations(scene, scene, node_data, animation_data, fps, remove_immutable_tracks);32143215String root_type = p_options["nodes/root_type"];3216Ref<Script> root_script = p_options["nodes/root_script"];3217scene = _replace_node_with_type_and_script(scene, root_type, root_script);32183219String root_name = p_options["nodes/root_name"];3220if (!root_name.is_empty() && root_name != "Scene Root") {3221// TODO: Remove `&& root_name != "Scene Root"` for Godot 5.0.3222// For backwards compatibility with existing .import files,3223// treat "Scene Root" as having no root name override.3224scene->set_name(root_name);3225} else if (String(scene->get_name()).is_empty()) {3226scene->set_name(p_save_path.get_file().get_basename());3227}32283229if (!occluder_arrays.first.is_empty() && !occluder_arrays.second.is_empty()) {3230Ref<ArrayOccluder3D> occ = memnew(ArrayOccluder3D);3231occ->set_arrays(occluder_arrays.first, occluder_arrays.second);3232OccluderInstance3D *occluder_instance = memnew(OccluderInstance3D);3233occluder_instance->set_occluder(occ);3234scene->add_child(occluder_instance, true);3235occluder_instance->set_owner(scene);3236}32373238bool gen_lods = bool(p_options["meshes/generate_lods"]);3239bool create_shadow_meshes = bool(p_options["meshes/create_shadow_meshes"]);3240int light_bake_mode = p_options["meshes/light_baking"];3241float texel_size = p_options["meshes/lightmap_texel_size"];3242float lightmap_texel_size = MAX(0.001, texel_size);32433244Vector<uint8_t> src_lightmap_cache;3245Vector<Vector<uint8_t>> mesh_lightmap_caches;32463247{3248src_lightmap_cache = FileAccess::get_file_as_bytes(p_source_file + ".unwrap_cache", &err);3249if (err != OK) {3250src_lightmap_cache.clear();3251}3252}32533254scene = _generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, mesh_lightmap_caches);32553256if (mesh_lightmap_caches.size()) {3257Ref<FileAccess> f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE);3258if (f.is_valid()) {3259f->store_32(mesh_lightmap_caches.size());3260for (int i = 0; i < mesh_lightmap_caches.size(); i++) {3261String md5 = String::md5(mesh_lightmap_caches[i].ptr());3262f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size());3263}3264}3265}3266err = OK;32673268progress.step(TTR("Running Custom Script..."), 2);32693270String post_import_script_path = p_options["import_script/path"];32713272Ref<EditorScenePostImport> post_import_script;32733274if (!post_import_script_path.is_empty()) {3275if (post_import_script_path.is_relative_path()) {3276post_import_script_path = p_source_file.get_base_dir().path_join(post_import_script_path);3277}3278Ref<Script> scr = ResourceLoader::load(post_import_script_path);3279if (scr.is_null()) {3280EditorNode::add_io_error(TTR("Couldn't load post-import script:") + " " + post_import_script_path);3281} else if (scr->get_instance_base_type() != "EditorScenePostImport") {3282EditorNode::add_io_error(TTR("Script is not a subtype of EditorScenePostImport:") + " " + post_import_script_path);3283} else {3284post_import_script.instantiate();3285post_import_script->set_script(scr);3286if (!post_import_script->get_script_instance()) {3287EditorNode::add_io_error(TTR("Invalid/broken script for post-import (check console):") + " " + post_import_script_path);3288post_import_script.unref();3289return ERR_CANT_CREATE;3290}3291}3292}32933294// Apply RESET animation before serializing.3295if (_scene_import_type == "PackedScene") {3296int scene_child_count = scene->get_child_count();3297for (int i = 0; i < scene_child_count; i++) {3298AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(scene->get_child(i));3299if (ap) {3300if (ap->can_apply_reset()) {3301ap->apply_reset();3302}3303}3304}3305}33063307if (post_import_script.is_valid()) {3308post_import_script->init(p_source_file);3309scene = post_import_script->post_import(scene);3310if (!scene) {3311EditorNode::add_io_error(3312TTR("Error running post-import script:") + " " + post_import_script_path + "\n" +3313TTR("Did you return a Node-derived object in the `_post_import()` method?"));3314return err;3315}3316}33173318for (int i = 0; i < post_importer_plugins.size(); i++) {3319post_importer_plugins.write[i]->post_process(scene, p_options);3320}33213322progress.step(TTR("Saving..."), 104);33233324int flags = 0;3325if (EditorSettings::get_singleton() && EDITOR_GET("filesystem/on_save/compress_binary_resources")) {3326flags |= ResourceSaver::FLAG_COMPRESS;3327}33283329if (_scene_import_type == "AnimationLibrary") {3330Ref<AnimationLibrary> library;3331for (int i = 0; i < scene->get_child_count(); i++) {3332AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(scene->get_child(i));3333if (ap) {3334List<StringName> libs;3335ap->get_animation_library_list(&libs);3336if (libs.size()) {3337library = ap->get_animation_library(libs.front()->get());3338break;3339}3340}3341}33423343if (library.is_null()) {3344library.instantiate(); // Will be empty3345}33463347print_verbose("Saving animation to: " + p_save_path + ".res");3348err = ResourceSaver::save(library, p_save_path + ".res", flags); //do not take over, let the changed files reload themselves3349ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save animation to file '" + p_save_path + ".res'.");3350} else if (_scene_import_type == "PackedScene") {3351Ref<PackedScene> packer = memnew(PackedScene);3352packer->pack(scene);3353print_verbose("Saving scene to: " + p_save_path + ".scn");3354err = ResourceSaver::save(packer, p_save_path + ".scn", flags); //do not take over, let the changed files reload themselves3355ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save scene to file '" + p_save_path + ".scn'.");3356EditorInterface::get_singleton()->make_scene_preview(p_source_file, scene, 1024);3357} else {3358ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown scene import type: " + _scene_import_type);3359}33603361memdelete(scene);33623363//this is not the time to reimport, wait until import process is done, import file is saved, etc.3364//EditorNode::get_singleton()->reload_scene(p_source_file);33653366return OK;3367}33683369Vector<Ref<EditorSceneFormatImporter>> ResourceImporterScene::scene_importers;3370Vector<Ref<EditorScenePostImportPlugin>> ResourceImporterScene::post_importer_plugins;33713372bool ResourceImporterScene::has_advanced_options() const {3373return true;3374}33753376void ResourceImporterScene::show_advanced_options(const String &p_path) {3377SceneImportSettingsDialog::get_singleton()->open_settings(p_path, _scene_import_type);3378}33793380ResourceImporterScene::ResourceImporterScene(const String &p_scene_import_type) {3381_scene_import_type = p_scene_import_type;3382}33833384void ResourceImporterScene::add_scene_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority) {3385ERR_FAIL_COND(p_importer.is_null());3386if (p_first_priority) {3387scene_importers.insert(0, p_importer);3388} else {3389scene_importers.push_back(p_importer);3390}3391}33923393void ResourceImporterScene::remove_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) {3394post_importer_plugins.erase(p_plugin);3395}33963397void ResourceImporterScene::add_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority) {3398ERR_FAIL_COND(p_plugin.is_null());3399if (p_first_priority) {3400post_importer_plugins.insert(0, p_plugin);3401} else {3402post_importer_plugins.push_back(p_plugin);3403}3404}34053406void ResourceImporterScene::remove_scene_importer(Ref<EditorSceneFormatImporter> p_importer) {3407scene_importers.erase(p_importer);3408}34093410void ResourceImporterScene::clean_up_importer_plugins() {3411scene_importers.clear();3412post_importer_plugins.clear();3413}34143415void ResourceImporterScene::get_scene_importer_extensions(List<String> *p_extensions) {3416for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {3417importer_elem->get_extensions(p_extensions);3418}3419}34203421///////////////////////////////////////34223423void EditorSceneFormatImporterESCN::get_extensions(List<String> *r_extensions) const {3424r_extensions->push_back("escn");3425}34263427Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {3428Error error;3429Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error);3430ERR_FAIL_COND_V_MSG(ps.is_null(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'.");3431Node *scene = ps->instantiate();3432TypedArray<Node> nodes = scene->find_children("*", "MeshInstance3D");3433for (int32_t node_i = 0; node_i < nodes.size(); node_i++) {3434MeshInstance3D *mesh_3d = cast_to<MeshInstance3D>(nodes[node_i]);3435Ref<ImporterMesh> mesh;3436mesh.instantiate();3437// Ignore the aabb, it will be recomputed.3438ImporterMeshInstance3D *importer_mesh_3d = memnew(ImporterMeshInstance3D);3439importer_mesh_3d->set_name(mesh_3d->get_name());3440importer_mesh_3d->set_transform(mesh_3d->get_relative_transform(mesh_3d->get_parent()));3441importer_mesh_3d->set_skin(mesh_3d->get_skin());3442importer_mesh_3d->set_skeleton_path(mesh_3d->get_skeleton_path());3443Ref<ArrayMesh> array_mesh_3d_mesh = mesh_3d->get_mesh();3444if (array_mesh_3d_mesh.is_valid()) {3445// For the MeshInstance3D nodes, we need to convert the ArrayMesh to an ImporterMesh specially.3446mesh->set_name(array_mesh_3d_mesh->get_name());3447for (int32_t blend_i = 0; blend_i < array_mesh_3d_mesh->get_blend_shape_count(); blend_i++) {3448mesh->add_blend_shape(array_mesh_3d_mesh->get_blend_shape_name(blend_i));3449}3450for (int32_t surface_i = 0; surface_i < array_mesh_3d_mesh->get_surface_count(); surface_i++) {3451mesh->add_surface(array_mesh_3d_mesh->surface_get_primitive_type(surface_i),3452array_mesh_3d_mesh->surface_get_arrays(surface_i),3453array_mesh_3d_mesh->surface_get_blend_shape_arrays(surface_i),3454array_mesh_3d_mesh->surface_get_lods(surface_i),3455array_mesh_3d_mesh->surface_get_material(surface_i),3456array_mesh_3d_mesh->surface_get_name(surface_i),3457array_mesh_3d_mesh->surface_get_format(surface_i));3458}3459mesh->set_blend_shape_mode(array_mesh_3d_mesh->get_blend_shape_mode());3460importer_mesh_3d->set_mesh(mesh);3461mesh_3d->replace_by(importer_mesh_3d);3462continue;3463}3464Ref<Mesh> mesh_3d_mesh = mesh_3d->get_mesh();3465if (mesh_3d_mesh.is_valid()) {3466// For the MeshInstance3D nodes, we need to convert the Mesh to an ImporterMesh specially.3467mesh->set_name(mesh_3d_mesh->get_name());3468for (int32_t surface_i = 0; surface_i < mesh_3d_mesh->get_surface_count(); surface_i++) {3469mesh->add_surface(mesh_3d_mesh->surface_get_primitive_type(surface_i),3470mesh_3d_mesh->surface_get_arrays(surface_i),3471Array(),3472mesh_3d_mesh->surface_get_lods(surface_i),3473mesh_3d_mesh->surface_get_material(surface_i),3474mesh_3d_mesh->surface_get_material(surface_i).is_valid() ? mesh_3d_mesh->surface_get_material(surface_i)->get_name() : String(),3475mesh_3d_mesh->surface_get_format(surface_i));3476}3477importer_mesh_3d->set_mesh(mesh);3478mesh_3d->replace_by(importer_mesh_3d);3479continue;3480}3481}34823483ERR_FAIL_NULL_V(scene, nullptr);34843485return scene;3486}348734883489