Path: blob/master/editor/import/3d/resource_importer_scene.cpp
9903 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;61if (GDVIRTUAL_CALL(_get_extensions, arr)) {62for (int i = 0; i < arr.size(); i++) {63r_extensions->push_back(arr[i]);64}65return;66}6768ERR_FAIL();69}7071Node *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) {72Dictionary options_dict;73for (const KeyValue<StringName, Variant> &elem : p_options) {74options_dict[elem.key] = elem.value;75}76Object *ret = nullptr;77if (GDVIRTUAL_CALL(_import_scene, p_path, p_flags, options_dict, ret)) {78return Object::cast_to<Node>(ret);79}8081ERR_FAIL_V(nullptr);82}8384void EditorSceneFormatImporter::add_import_option(const String &p_name, const Variant &p_default_value) {85ERR_FAIL_NULL_MSG(current_option_list, "add_import_option() can only be called from get_import_options().");86add_import_option_advanced(p_default_value.get_type(), p_name, p_default_value);87}8889void 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) {90ERR_FAIL_NULL_MSG(current_option_list, "add_import_option_advanced() can only be called from get_import_options().");91current_option_list->push_back(ResourceImporter::ImportOption(PropertyInfo(p_type, p_name, p_hint, p_hint_string, p_usage_flags), p_default_value));92}9394void EditorSceneFormatImporter::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) {95current_option_list = r_options;96GDVIRTUAL_CALL(_get_import_options, p_path);97current_option_list = nullptr;98}99100Variant EditorSceneFormatImporter::get_option_visibility(const String &p_path, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) {101Variant ret;102// For compatibility with the old API, pass the import type as a boolean.103GDVIRTUAL_CALL(_get_option_visibility, p_path, p_scene_import_type == "AnimationLibrary", p_option, ret);104return ret;105}106107void EditorSceneFormatImporter::_bind_methods() {108ClassDB::bind_method(D_METHOD("add_import_option", "name", "value"), &EditorSceneFormatImporter::add_import_option);109ClassDB::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));110111GDVIRTUAL_BIND(_get_extensions);112GDVIRTUAL_BIND(_import_scene, "path", "flags", "options");113GDVIRTUAL_BIND(_get_import_options, "path");114GDVIRTUAL_BIND(_get_option_visibility, "path", "for_animation", "option");115116BIND_CONSTANT(IMPORT_SCENE);117BIND_CONSTANT(IMPORT_ANIMATION);118BIND_CONSTANT(IMPORT_FAIL_ON_MISSING_DEPENDENCIES);119BIND_CONSTANT(IMPORT_GENERATE_TANGENT_ARRAYS);120BIND_CONSTANT(IMPORT_USE_NAMED_SKIN_BINDS);121BIND_CONSTANT(IMPORT_DISCARD_MESHES_AND_MATERIALS);122BIND_CONSTANT(IMPORT_FORCE_DISABLE_MESH_COMPRESSION);123}124125/////////////////////////////////126void EditorScenePostImport::_bind_methods() {127GDVIRTUAL_BIND(_post_import, "scene")128ClassDB::bind_method(D_METHOD("get_source_file"), &EditorScenePostImport::get_source_file);129}130131Node *EditorScenePostImport::post_import(Node *p_scene) {132Object *ret;133if (GDVIRTUAL_CALL(_post_import, p_scene, ret)) {134return Object::cast_to<Node>(ret);135}136137return p_scene;138}139140String EditorScenePostImport::get_source_file() const {141return source_file;142}143144void EditorScenePostImport::init(const String &p_source_file) {145source_file = p_source_file;146}147148///////////////////////////////////////////////////////149150Variant EditorScenePostImportPlugin::get_option_value(const StringName &p_name) const {151ERR_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.");152ERR_FAIL_COND_V_MSG(current_options && !current_options->has(p_name), Variant(), "get_option_value called with unexisting option argument: " + String(p_name));153ERR_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));154if (current_options && current_options->has(p_name)) {155return (*current_options)[p_name];156}157if (current_options_dict && current_options_dict->has(p_name)) {158return (*current_options_dict)[p_name];159}160return Variant();161}162void EditorScenePostImportPlugin::add_import_option(const String &p_name, const Variant &p_default_value) {163ERR_FAIL_NULL_MSG(current_option_list, "add_import_option() can only be called from get_import_options().");164add_import_option_advanced(p_default_value.get_type(), p_name, p_default_value);165}166void 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) {167ERR_FAIL_NULL_MSG(current_option_list, "add_import_option_advanced() can only be called from get_import_options().");168current_option_list->push_back(ResourceImporter::ImportOption(PropertyInfo(p_type, p_name, p_hint, p_hint_string, p_usage_flags), p_default_value));169}170171void EditorScenePostImportPlugin::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {172current_option_list = r_options;173GDVIRTUAL_CALL(_get_internal_import_options, p_category);174current_option_list = nullptr;175}176177Variant 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 {178current_options = &p_options;179Variant ret;180// For compatibility with the old API, pass the import type as a boolean.181GDVIRTUAL_CALL(_get_internal_option_visibility, p_category, p_scene_import_type == "AnimationLibrary", p_option, ret);182current_options = nullptr;183return ret;184}185186Variant EditorScenePostImportPlugin::get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const {187current_options = &p_options;188Variant ret;189GDVIRTUAL_CALL(_get_internal_option_update_view_required, p_category, p_option, ret);190current_options = nullptr;191return ret;192}193194void EditorScenePostImportPlugin::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) {195current_options_dict = &p_options;196GDVIRTUAL_CALL(_internal_process, p_category, p_base_scene, p_node, p_resource);197current_options_dict = nullptr;198}199200void EditorScenePostImportPlugin::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) {201current_option_list = r_options;202GDVIRTUAL_CALL(_get_import_options, p_path);203current_option_list = nullptr;204}205Variant 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 {206current_options = &p_options;207Variant ret;208GDVIRTUAL_CALL(_get_option_visibility, p_path, p_scene_import_type == "AnimationLibrary", p_option, ret);209current_options = nullptr;210return ret;211}212213void EditorScenePostImportPlugin::pre_process(Node *p_scene, const HashMap<StringName, Variant> &p_options) {214current_options = &p_options;215GDVIRTUAL_CALL(_pre_process, p_scene);216current_options = nullptr;217}218void EditorScenePostImportPlugin::post_process(Node *p_scene, const HashMap<StringName, Variant> &p_options) {219current_options = &p_options;220GDVIRTUAL_CALL(_post_process, p_scene);221current_options = nullptr;222}223224void EditorScenePostImportPlugin::_bind_methods() {225ClassDB::bind_method(D_METHOD("get_option_value", "name"), &EditorScenePostImportPlugin::get_option_value);226227ClassDB::bind_method(D_METHOD("add_import_option", "name", "value"), &EditorScenePostImportPlugin::add_import_option);228ClassDB::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));229230GDVIRTUAL_BIND(_get_internal_import_options, "category");231GDVIRTUAL_BIND(_get_internal_option_visibility, "category", "for_animation", "option");232GDVIRTUAL_BIND(_get_internal_option_update_view_required, "category", "option");233GDVIRTUAL_BIND(_internal_process, "category", "base_node", "node", "resource");234GDVIRTUAL_BIND(_get_import_options, "path");235GDVIRTUAL_BIND(_get_option_visibility, "path", "for_animation", "option");236GDVIRTUAL_BIND(_pre_process, "scene");237GDVIRTUAL_BIND(_post_process, "scene");238239BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_NODE);240BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE);241BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MESH);242BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MATERIAL);243BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION);244BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE);245BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE);246BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MAX);247}248249/////////////////////////////////////////////////////////250251const String ResourceImporterScene::material_extension[3] = { ".tres", ".res", ".material" };252253String ResourceImporterScene::get_importer_name() const {254// For compatibility with 4.2 and earlier we need to keep the "scene" and "animation_library" names.255// However this is arbitrary so for new import types we can use any string.256if (_scene_import_type == "PackedScene") {257return "scene";258} else if (_scene_import_type == "AnimationLibrary") {259return "animation_library";260}261return _scene_import_type;262}263264String ResourceImporterScene::get_visible_name() const {265// This is displayed on the UI. Friendly names here are nice but not vital, so fall back to the type.266if (_scene_import_type == "PackedScene") {267return "Scene";268}269return _scene_import_type.capitalize();270}271272void ResourceImporterScene::get_recognized_extensions(List<String> *p_extensions) const {273get_scene_importer_extensions(p_extensions);274}275276String ResourceImporterScene::get_save_extension() const {277if (_scene_import_type == "PackedScene") {278return "scn";279}280return "res";281}282283String ResourceImporterScene::get_resource_type() const {284return _scene_import_type;285}286287int ResourceImporterScene::get_format_version() const {288return 1;289}290291bool ResourceImporterScene::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {292if (_scene_import_type == "PackedScene") {293if (p_option.begins_with("animation/")) {294if (p_option != "animation/import" && !bool(p_options["animation/import"])) {295return false;296}297}298} else if (_scene_import_type == "AnimationLibrary") {299if (p_option == "animation/import") { // Option ignored, animation always imported.300return false;301}302if (p_option == "nodes/root_type" || p_option == "nodes/root_name" || p_option.begins_with("meshes/") || p_option.begins_with("skins/")) {303return false; // Nothing to do here for animations.304}305}306307if (p_option == "nodes/use_node_type_suffixes" && p_options.has("nodes/use_name_suffixes")) {308return p_options["nodes/use_name_suffixes"];309}310if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) != 2) {311// Only display the lightmap texel size import option when using the Static Lightmaps light baking mode.312return false;313}314315for (int i = 0; i < post_importer_plugins.size(); i++) {316Variant ret = post_importer_plugins.write[i]->get_option_visibility(p_path, _scene_import_type, p_option, p_options);317if (ret.get_type() == Variant::BOOL) {318if (!ret) {319return false;320}321}322}323324for (Ref<EditorSceneFormatImporter> importer : scene_importers) {325Variant ret = importer->get_option_visibility(p_path, _scene_import_type, p_option, p_options);326if (ret.get_type() == Variant::BOOL) {327if (!ret) {328return false;329}330}331}332333return true;334}335336int ResourceImporterScene::get_preset_count() const {337return 0;338}339340String ResourceImporterScene::get_preset_name(int p_idx) const {341return String();342}343344void ResourceImporterScene::_pre_fix_global(Node *p_scene, const HashMap<StringName, Variant> &p_options) const {345if (p_options.has("animation/import_rest_as_RESET") && (bool)p_options["animation/import_rest_as_RESET"]) {346TypedArray<Node> anim_players = p_scene->find_children("*", "AnimationPlayer");347if (anim_players.is_empty()) {348AnimationPlayer *anim_player = memnew(AnimationPlayer);349anim_player->set_name("AnimationPlayer");350p_scene->add_child(anim_player);351anim_player->set_owner(p_scene);352anim_players.append(anim_player);353}354Ref<Animation> reset_anim;355for (int i = 0; i < anim_players.size(); i++) {356AnimationPlayer *player = cast_to<AnimationPlayer>(anim_players[i]);357if (player->has_animation(SceneStringName(RESET))) {358reset_anim = player->get_animation(SceneStringName(RESET));359break;360}361}362if (reset_anim.is_null()) {363AnimationPlayer *anim_player = cast_to<AnimationPlayer>(anim_players[0]);364reset_anim.instantiate();365Ref<AnimationLibrary> anim_library;366if (anim_player->has_animation_library(StringName())) {367anim_library = anim_player->get_animation_library(StringName());368} else {369anim_library.instantiate();370anim_player->add_animation_library(StringName(), anim_library);371}372anim_library->add_animation(SceneStringName(RESET), reset_anim);373}374TypedArray<Node> skeletons = p_scene->find_children("*", "Skeleton3D");375for (int i = 0; i < skeletons.size(); i++) {376Skeleton3D *skeleton = cast_to<Skeleton3D>(skeletons[i]);377NodePath skeleton_path = p_scene->get_path_to(skeleton);378379HashSet<NodePath> existing_pos_tracks;380HashSet<NodePath> existing_rot_tracks;381for (int trk_i = 0; trk_i < reset_anim->get_track_count(); trk_i++) {382NodePath np = reset_anim->track_get_path(trk_i);383if (reset_anim->track_get_type(trk_i) == Animation::TYPE_POSITION_3D) {384existing_pos_tracks.insert(np);385}386if (reset_anim->track_get_type(trk_i) == Animation::TYPE_ROTATION_3D) {387existing_rot_tracks.insert(np);388}389}390for (int bone_i = 0; bone_i < skeleton->get_bone_count(); bone_i++) {391NodePath bone_path(skeleton_path.get_names(), Vector<StringName>{ skeleton->get_bone_name(bone_i) }, false);392if (!existing_pos_tracks.has(bone_path)) {393int pos_t = reset_anim->add_track(Animation::TYPE_POSITION_3D);394reset_anim->track_set_path(pos_t, bone_path);395reset_anim->position_track_insert_key(pos_t, 0.0, skeleton->get_bone_rest(bone_i).origin);396reset_anim->track_set_imported(pos_t, true);397}398if (!existing_rot_tracks.has(bone_path)) {399int rot_t = reset_anim->add_track(Animation::TYPE_ROTATION_3D);400reset_anim->track_set_path(rot_t, bone_path);401reset_anim->rotation_track_insert_key(rot_t, 0.0, skeleton->get_bone_rest(bone_i).basis.get_rotation_quaternion());402reset_anim->track_set_imported(rot_t, true);403}404}405}406}407}408409static bool _teststr(const String &p_what, const String &p_str) {410String what = p_what;411412// Remove trailing spaces and numbers, some apps like blender add ".number" to duplicates413// (dot is replaced with _ as invalid character) so also compensate for this.414while (what.length() && (is_digit(what[what.length() - 1]) || what[what.length() - 1] <= 32 || what[what.length() - 1] == '_')) {415what = what.substr(0, what.length() - 1);416}417418if (what.containsn("$" + p_str)) { // Blender and other stuff.419return true;420}421if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters422return true;423}424if (what.to_lower().ends_with("_" + p_str)) { //collada only supports "_" and "-" besides letters425return true;426}427return false;428}429430static String _fixstr(const String &p_what, const String &p_str) {431String what = p_what;432433// Remove trailing spaces and numbers, some apps like blender add ".number" to duplicates434// (dot is replaced with _ as invalid character) so also compensate for this.435while (what.length() && (is_digit(what[what.length() - 1]) || what[what.length() - 1] <= 32 || what[what.length() - 1] == '_')) {436what = what.substr(0, what.length() - 1);437}438439String end = p_what.substr(what.length());440441if (what.containsn("$" + p_str)) { // Blender and other stuff.442return what.replace("$" + p_str, "") + end;443}444if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters445return what.substr(0, what.length() - (p_str.length() + 1)) + end;446}447if (what.to_lower().ends_with("_" + p_str)) { //collada only supports "_" and "-" besides letters448return what.substr(0, what.length() - (p_str.length() + 1)) + end;449}450return what;451}452453static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r_shape_list, bool p_convex) {454ERR_FAIL_COND_MSG(mesh.is_null(), "Cannot generate shape list with null mesh value.");455if (!p_convex) {456Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape();457r_shape_list.push_back(shape);458} else {459Vector<Ref<Shape3D>> cd;460cd.push_back(mesh->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false));461if (cd.size()) {462for (int i = 0; i < cd.size(); i++) {463r_shape_list.push_back(cd[i]);464}465}466}467}468469struct ScalableNodeCollection {470HashSet<Node3D *> node_3ds;471HashSet<Ref<ImporterMesh>> importer_meshes;472HashSet<Ref<Skin>> skins;473HashSet<Ref<Animation>> animations;474};475476void _rescale_importer_mesh(Vector3 p_scale, Ref<ImporterMesh> p_mesh, bool is_shadow = false) {477// MESH and SKIN data divide, to compensate for object position multiplying.478479const int surf_count = p_mesh->get_surface_count();480const int blendshape_count = p_mesh->get_blend_shape_count();481struct LocalSurfData {482Mesh::PrimitiveType prim = {};483Array arr;484Array bsarr;485Dictionary lods;486String name;487Ref<Material> mat;488uint64_t fmt_compress_flags = 0;489};490491Vector<LocalSurfData> surf_data_by_mesh;492493Vector<String> blendshape_names;494for (int bsidx = 0; bsidx < blendshape_count; bsidx++) {495blendshape_names.append(p_mesh->get_blend_shape_name(bsidx));496}497498for (int surf_idx = 0; surf_idx < surf_count; surf_idx++) {499Mesh::PrimitiveType prim = p_mesh->get_surface_primitive_type(surf_idx);500const uint64_t fmt_compress_flags = p_mesh->get_surface_format(surf_idx);501Array arr = p_mesh->get_surface_arrays(surf_idx);502String name = p_mesh->get_surface_name(surf_idx);503Dictionary lods;504Ref<Material> mat = p_mesh->get_surface_material(surf_idx);505{506Vector<Vector3> vertex_array = arr[ArrayMesh::ARRAY_VERTEX];507for (int vert_arr_i = 0; vert_arr_i < vertex_array.size(); vert_arr_i++) {508vertex_array.write[vert_arr_i] = vertex_array[vert_arr_i] * p_scale;509}510arr[ArrayMesh::ARRAY_VERTEX] = vertex_array;511}512Array blendshapes;513for (int bsidx = 0; bsidx < blendshape_count; bsidx++) {514Array current_bsarr = p_mesh->get_surface_blend_shape_arrays(surf_idx, bsidx);515Vector<Vector3> current_bs_vertex_array = current_bsarr[ArrayMesh::ARRAY_VERTEX];516int current_bs_vert_arr_len = current_bs_vertex_array.size();517for (int32_t bs_vert_arr_i = 0; bs_vert_arr_i < current_bs_vert_arr_len; bs_vert_arr_i++) {518current_bs_vertex_array.write[bs_vert_arr_i] = current_bs_vertex_array[bs_vert_arr_i] * p_scale;519}520current_bsarr[ArrayMesh::ARRAY_VERTEX] = current_bs_vertex_array;521blendshapes.push_back(current_bsarr);522}523524LocalSurfData surf_data_dictionary = LocalSurfData();525surf_data_dictionary.prim = prim;526surf_data_dictionary.arr = arr;527surf_data_dictionary.bsarr = blendshapes;528surf_data_dictionary.lods = lods;529surf_data_dictionary.fmt_compress_flags = fmt_compress_flags;530surf_data_dictionary.name = name;531surf_data_dictionary.mat = mat;532533surf_data_by_mesh.push_back(surf_data_dictionary);534}535536p_mesh->clear();537538for (int bsidx = 0; bsidx < blendshape_count; bsidx++) {539p_mesh->add_blend_shape(blendshape_names[bsidx]);540}541542for (int surf_idx = 0; surf_idx < surf_count; surf_idx++) {543const Mesh::PrimitiveType prim = surf_data_by_mesh[surf_idx].prim;544const Array arr = surf_data_by_mesh[surf_idx].arr;545const Array bsarr = surf_data_by_mesh[surf_idx].bsarr;546const Dictionary lods = surf_data_by_mesh[surf_idx].lods;547const uint64_t fmt_compress_flags = surf_data_by_mesh[surf_idx].fmt_compress_flags;548const String name = surf_data_by_mesh[surf_idx].name;549const Ref<Material> mat = surf_data_by_mesh[surf_idx].mat;550551p_mesh->add_surface(prim, arr, bsarr, lods, mat, name, fmt_compress_flags);552}553554if (!is_shadow && p_mesh->get_shadow_mesh() != p_mesh && p_mesh->get_shadow_mesh().is_valid()) {555_rescale_importer_mesh(p_scale, p_mesh->get_shadow_mesh(), true);556}557}558559void _rescale_skin(Vector3 p_scale, Ref<Skin> p_skin) {560// MESH and SKIN data divide, to compensate for object position multiplying.561for (int i = 0; i < p_skin->get_bind_count(); i++) {562Transform3D transform = p_skin->get_bind_pose(i);563p_skin->set_bind_pose(i, Transform3D(transform.basis, p_scale * transform.origin));564}565}566567void _rescale_animation(Vector3 p_scale, Ref<Animation> p_animation) {568for (int track_idx = 0; track_idx < p_animation->get_track_count(); track_idx++) {569if (p_animation->track_get_type(track_idx) == Animation::TYPE_POSITION_3D) {570for (int key_idx = 0; key_idx < p_animation->track_get_key_count(track_idx); key_idx++) {571Vector3 value = p_animation->track_get_key_value(track_idx, key_idx);572value = p_scale * value;573p_animation->track_set_key_value(track_idx, key_idx, value);574}575}576}577}578579void _apply_scale_to_scalable_node_collection(ScalableNodeCollection &p_collection, Vector3 p_scale) {580for (Node3D *node_3d : p_collection.node_3ds) {581node_3d->set_position(p_scale * node_3d->get_position());582Skeleton3D *skeleton_3d = Object::cast_to<Skeleton3D>(node_3d);583if (skeleton_3d) {584for (int i = 0; i < skeleton_3d->get_bone_count(); i++) {585Transform3D rest = skeleton_3d->get_bone_rest(i);586Vector3 position = skeleton_3d->get_bone_pose_position(i);587skeleton_3d->set_bone_rest(i, Transform3D(rest.basis, p_scale * rest.origin));588skeleton_3d->set_bone_pose_position(i, p_scale * position);589}590}591}592for (Ref<ImporterMesh> mesh : p_collection.importer_meshes) {593_rescale_importer_mesh(p_scale, mesh, false);594}595for (Ref<Skin> skin : p_collection.skins) {596_rescale_skin(p_scale, skin);597}598for (Ref<Animation> animation : p_collection.animations) {599_rescale_animation(p_scale, animation);600}601}602603void _populate_scalable_nodes_collection(Node *p_node, ScalableNodeCollection &p_collection) {604if (!p_node) {605return;606}607Node3D *node_3d = Object::cast_to<Node3D>(p_node);608if (node_3d) {609p_collection.node_3ds.insert(node_3d);610ImporterMeshInstance3D *mesh_instance_3d = Object::cast_to<ImporterMeshInstance3D>(p_node);611if (mesh_instance_3d) {612Ref<ImporterMesh> mesh = mesh_instance_3d->get_mesh();613if (mesh.is_valid()) {614p_collection.importer_meshes.insert(mesh);615}616Ref<Skin> skin = mesh_instance_3d->get_skin();617if (skin.is_valid()) {618p_collection.skins.insert(skin);619}620}621}622AnimationPlayer *animation_player = Object::cast_to<AnimationPlayer>(p_node);623if (animation_player) {624List<StringName> animation_list;625animation_player->get_animation_list(&animation_list);626627for (const StringName &E : animation_list) {628Ref<Animation> animation = animation_player->get_animation(E);629p_collection.animations.insert(animation);630}631}632633for (int i = 0; i < p_node->get_child_count(); i++) {634Node *child = p_node->get_child(i);635_populate_scalable_nodes_collection(child, p_collection);636}637}638639void _apply_permanent_scale_to_descendants(Node *p_root_node, Vector3 p_scale) {640ScalableNodeCollection scalable_node_collection;641_populate_scalable_nodes_collection(p_root_node, scalable_node_collection);642_apply_scale_to_scalable_node_collection(scalable_node_collection, p_scale);643}644645Node *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) {646bool use_name_suffixes = true;647if (p_options.has("nodes/use_name_suffixes")) {648use_name_suffixes = p_options["nodes/use_name_suffixes"];649}650if (!use_name_suffixes) {651return p_node;652}653654// Children first.655for (int i = 0; i < p_node->get_child_count(); i++) {656Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames, p_options);657if (!r) {658i--; // Was erased.659}660}661662String name = p_node->get_name();663NodePath original_path = p_root->get_path_to(p_node); // Used to detect renames due to import hints.664665Ref<Resource> original_meta = memnew(Resource); // Create temp resource to hold original meta666original_meta->merge_meta_from(p_node);667668bool isroot = p_node == p_root;669670if (!isroot && _teststr(name, "noimp")) {671p_node->set_owner(nullptr);672memdelete(p_node);673return nullptr;674}675676if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {677ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);678679Ref<ImporterMesh> m = mi->get_mesh();680681if (m.is_valid()) {682for (int i = 0; i < m->get_surface_count(); i++) {683Ref<BaseMaterial3D> mat = m->get_surface_material(i);684if (mat.is_null()) {685continue;686}687688if (_teststr(mat->get_name(), "alpha")) {689mat->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA);690mat->set_name(_fixstr(mat->get_name(), "alpha"));691}692if (_teststr(mat->get_name(), "vcol")) {693mat->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);694mat->set_flag(BaseMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);695mat->set_name(_fixstr(mat->get_name(), "vcol"));696}697}698}699}700701if (Object::cast_to<AnimationPlayer>(p_node)) {702AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);703704// Node paths in animation tracks are relative to the following path (this is used to fix node paths below).705Node *ap_root = ap->get_node(ap->get_root_node());706NodePath path_prefix = p_root->get_path_to(ap_root);707708bool nodes_were_renamed = r_node_renames.size() != 0;709710List<StringName> anims;711ap->get_animation_list(&anims);712for (const StringName &E : anims) {713Ref<Animation> anim = ap->get_animation(E);714ERR_CONTINUE(anim.is_null());715716// Remove animation tracks referencing non-importable nodes.717for (int i = 0; i < anim->get_track_count(); i++) {718NodePath path = anim->track_get_path(i);719720for (int j = 0; j < path.get_name_count(); j++) {721String node = path.get_name(j);722if (_teststr(node, "noimp")) {723anim->remove_track(i);724i--;725break;726}727}728}729730// Fix node paths in animations, in case nodes were renamed earlier due to import hints.731if (nodes_were_renamed) {732for (int i = 0; i < anim->get_track_count(); i++) {733NodePath path = anim->track_get_path(i);734// Convert track path to absolute node path without subnames (some manual work because we are not in the scene tree).735Vector<StringName> absolute_path_names = path_prefix.get_names();736absolute_path_names.append_array(path.get_names());737NodePath absolute_path(absolute_path_names, false);738absolute_path.simplify();739// Fix paths to renamed nodes.740for (const Pair<NodePath, Node *> &F : r_node_renames) {741if (F.first == absolute_path) {742NodePath new_path(ap_root->get_path_to(F.second).get_names(), path.get_subnames(), false);743print_verbose(vformat("Fix: Correcting node path in animation track: %s should be %s", path, new_path));744anim->track_set_path(i, new_path);745break; // Only one match is possible.746}747}748}749}750751String animname = E;752const int loop_string_count = 3;753static const char *loop_strings[loop_string_count] = { "loop_mode", "loop", "cycle" };754for (int i = 0; i < loop_string_count; i++) {755if (_teststr(animname, loop_strings[i])) {756anim->set_loop_mode(Animation::LOOP_LINEAR);757animname = _fixstr(animname, loop_strings[i]);758759Ref<AnimationLibrary> library = ap->get_animation_library(ap->find_animation_library(anim));760library->rename_animation(E, animname);761}762}763}764}765766bool use_node_type_suffixes = true;767if (p_options.has("nodes/use_node_type_suffixes")) {768use_node_type_suffixes = p_options["nodes/use_node_type_suffixes"];769}770if (!use_node_type_suffixes) {771return p_node;772}773774if (_teststr(name, "colonly") || _teststr(name, "convcolonly")) {775if (isroot) {776return p_node;777}778779String fixed_name;780if (_teststr(name, "colonly")) {781fixed_name = _fixstr(name, "colonly");782} else if (_teststr(name, "convcolonly")) {783fixed_name = _fixstr(name, "convcolonly");784}785786if (fixed_name.is_empty()) {787p_node->set_owner(nullptr);788memdelete(p_node);789ERR_FAIL_V_MSG(nullptr, vformat("Skipped node `%s` because its name is empty after removing the suffix.", name));790}791792ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);793if (mi) {794Ref<ImporterMesh> mesh = mi->get_mesh();795796if (mesh.is_valid()) {797Vector<Ref<Shape3D>> shapes;798if (r_collision_map.has(mesh)) {799shapes = r_collision_map[mesh];800} else if (_teststr(name, "colonly")) {801_pre_gen_shape_list(mesh, shapes, false);802r_collision_map[mesh] = shapes;803} else if (_teststr(name, "convcolonly")) {804_pre_gen_shape_list(mesh, shapes, true);805r_collision_map[mesh] = shapes;806}807808if (shapes.size()) {809StaticBody3D *col = memnew(StaticBody3D);810col->set_transform(mi->get_transform());811col->set_name(fixed_name);812_copy_meta(p_node, col);813p_node->replace_by(col);814p_node->set_owner(nullptr);815memdelete(p_node);816p_node = col;817818_add_shapes(col, shapes);819}820}821822} else if (p_node->has_meta("empty_draw_type")) {823String empty_draw_type = String(p_node->get_meta("empty_draw_type"));824StaticBody3D *sb = memnew(StaticBody3D);825sb->set_name(fixed_name);826Object::cast_to<Node3D>(sb)->set_transform(Object::cast_to<Node3D>(p_node)->get_transform());827_copy_meta(p_node, sb);828p_node->replace_by(sb);829p_node->set_owner(nullptr);830memdelete(p_node);831p_node = sb;832CollisionShape3D *colshape = memnew(CollisionShape3D);833if (empty_draw_type == "CUBE") {834BoxShape3D *boxShape = memnew(BoxShape3D);835boxShape->set_size(Vector3(2, 2, 2));836colshape->set_shape(boxShape);837} else if (empty_draw_type == "SINGLE_ARROW") {838SeparationRayShape3D *rayShape = memnew(SeparationRayShape3D);839rayShape->set_length(1);840colshape->set_shape(rayShape);841Object::cast_to<Node3D>(sb)->rotate_x(Math::PI / 2);842} else if (empty_draw_type == "IMAGE") {843WorldBoundaryShape3D *world_boundary_shape = memnew(WorldBoundaryShape3D);844colshape->set_shape(world_boundary_shape);845} else {846SphereShape3D *sphereShape = memnew(SphereShape3D);847sphereShape->set_radius(1);848colshape->set_shape(sphereShape);849}850sb->add_child(colshape, true);851colshape->set_owner(sb->get_owner());852}853854} else if (_teststr(name, "rigid") && Object::cast_to<ImporterMeshInstance3D>(p_node)) {855if (isroot) {856return p_node;857}858859ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);860Ref<ImporterMesh> mesh = mi->get_mesh();861862if (mesh.is_valid()) {863Vector<Ref<Shape3D>> shapes;864if (r_collision_map.has(mesh)) {865shapes = r_collision_map[mesh];866} else {867_pre_gen_shape_list(mesh, shapes, true);868}869870RigidBody3D *rigid_body = memnew(RigidBody3D);871rigid_body->set_name(_fixstr(name, "rigid_body"));872_copy_meta(p_node, rigid_body);873p_node->replace_by(rigid_body);874rigid_body->set_transform(mi->get_transform());875p_node = rigid_body;876mi->set_transform(Transform3D());877rigid_body->add_child(mi, true);878mi->set_owner(rigid_body->get_owner());879880_add_shapes(rigid_body, shapes);881}882883} else if ((_teststr(name, "col") || (_teststr(name, "convcol"))) && Object::cast_to<ImporterMeshInstance3D>(p_node)) {884ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);885886Ref<ImporterMesh> mesh = mi->get_mesh();887888if (mesh.is_valid()) {889Vector<Ref<Shape3D>> shapes;890String fixed_name;891if (r_collision_map.has(mesh)) {892shapes = r_collision_map[mesh];893} else if (_teststr(name, "col")) {894_pre_gen_shape_list(mesh, shapes, false);895r_collision_map[mesh] = shapes;896} else if (_teststr(name, "convcol")) {897_pre_gen_shape_list(mesh, shapes, true);898r_collision_map[mesh] = shapes;899}900901if (_teststr(name, "col")) {902fixed_name = _fixstr(name, "col");903} else if (_teststr(name, "convcol")) {904fixed_name = _fixstr(name, "convcol");905}906907if (!fixed_name.is_empty()) {908if (mi->get_parent() && !mi->get_parent()->has_node(fixed_name)) {909mi->set_name(fixed_name);910}911}912913if (shapes.size()) {914StaticBody3D *col = memnew(StaticBody3D);915mi->add_child(col, true);916col->set_owner(mi->get_owner());917918_add_shapes(col, shapes);919}920}921922} else if (_teststr(name, "navmesh") && Object::cast_to<ImporterMeshInstance3D>(p_node)) {923if (isroot) {924return p_node;925}926927ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);928929Ref<ImporterMesh> mesh = mi->get_mesh();930ERR_FAIL_COND_V(mesh.is_null(), nullptr);931NavigationRegion3D *nmi = memnew(NavigationRegion3D);932933nmi->set_name(_fixstr(name, "navmesh"));934Ref<NavigationMesh> nmesh = mesh->create_navigation_mesh();935nmi->set_navigation_mesh(nmesh);936Object::cast_to<Node3D>(nmi)->set_transform(mi->get_transform());937_copy_meta(p_node, nmi);938p_node->replace_by(nmi);939p_node->set_owner(nullptr);940memdelete(p_node);941p_node = nmi;942} else if (_teststr(name, "occ") || _teststr(name, "occonly")) {943if (isroot) {944return p_node;945}946ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);947if (mi) {948Ref<ImporterMesh> mesh = mi->get_mesh();949950if (mesh.is_valid()) {951if (r_occluder_arrays) {952OccluderInstance3D::bake_single_node(mi, 0.0f, r_occluder_arrays->first, r_occluder_arrays->second);953}954if (_teststr(name, "occ")) {955String fixed_name = _fixstr(name, "occ");956if (!fixed_name.is_empty()) {957if (mi->get_parent() && !mi->get_parent()->has_node(fixed_name)) {958mi->set_name(fixed_name);959}960}961} else {962p_node->set_owner(nullptr);963memdelete(p_node);964p_node = nullptr;965}966}967}968} else if (_teststr(name, "vehicle")) {969if (isroot) {970return p_node;971}972973Node *owner = p_node->get_owner();974Node3D *s = Object::cast_to<Node3D>(p_node);975VehicleBody3D *bv = memnew(VehicleBody3D);976String n = _fixstr(p_node->get_name(), "vehicle");977bv->set_name(n);978_copy_meta(p_node, bv);979p_node->replace_by(bv);980p_node->set_name(n);981bv->add_child(p_node);982bv->set_owner(owner);983p_node->set_owner(owner);984bv->set_transform(s->get_transform());985s->set_transform(Transform3D());986987p_node = bv;988} else if (_teststr(name, "wheel")) {989if (isroot) {990return p_node;991}992993Node *owner = p_node->get_owner();994Node3D *s = Object::cast_to<Node3D>(p_node);995VehicleWheel3D *bv = memnew(VehicleWheel3D);996String n = _fixstr(p_node->get_name(), "wheel");997bv->set_name(n);998_copy_meta(p_node, bv);999p_node->replace_by(bv);1000p_node->set_name(n);1001bv->add_child(p_node);1002bv->set_owner(owner);1003p_node->set_owner(owner);1004bv->set_transform(s->get_transform());1005s->set_transform(Transform3D());10061007p_node = bv;1008} else if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1009//last attempt, maybe collision inside the mesh data10101011ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);10121013Ref<ImporterMesh> mesh = mi->get_mesh();1014if (mesh.is_valid()) {1015Vector<Ref<Shape3D>> shapes;1016if (r_collision_map.has(mesh)) {1017shapes = r_collision_map[mesh];1018} else if (_teststr(mesh->get_name(), "col")) {1019_pre_gen_shape_list(mesh, shapes, false);1020r_collision_map[mesh] = shapes;1021mesh->set_name(_fixstr(mesh->get_name(), "col"));1022} else if (_teststr(mesh->get_name(), "convcol")) {1023_pre_gen_shape_list(mesh, shapes, true);1024r_collision_map[mesh] = shapes;1025mesh->set_name(_fixstr(mesh->get_name(), "convcol"));1026} else if (_teststr(mesh->get_name(), "occ")) {1027if (r_occluder_arrays) {1028OccluderInstance3D::bake_single_node(mi, 0.0f, r_occluder_arrays->first, r_occluder_arrays->second);1029}1030mesh->set_name(_fixstr(mesh->get_name(), "occ"));1031}10321033if (shapes.size()) {1034StaticBody3D *col = memnew(StaticBody3D);1035p_node->add_child(col, true);1036col->set_owner(p_node->get_owner());10371038_add_shapes(col, shapes);1039}1040}1041}10421043if (p_node) {1044NodePath new_path = p_root->get_path_to(p_node);1045if (new_path != original_path) {1046print_verbose(vformat("Fix: Renamed %s to %s", original_path, new_path));1047r_node_renames.push_back({ original_path, p_node });1048}1049// If we created new node instead, merge meta values from the original node.1050p_node->merge_meta_from(*original_meta);1051}10521053return p_node;1054}10551056Node *ResourceImporterScene::_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps) {1057// children first1058for (int i = 0; i < p_node->get_child_count(); i++) {1059Node *r = _pre_fix_animations(p_node->get_child(i), p_root, p_node_data, p_animation_data, p_animation_fps);1060if (!r) {1061i--; //was erased1062}1063}10641065String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));10661067Dictionary node_settings;1068if (p_node_data.has(import_id)) {1069node_settings = p_node_data[import_id];1070}10711072{1073//make sure this is unique1074node_settings = node_settings.duplicate(true);1075//fill node settings for this node with default values1076List<ImportOption> iopts;1077get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);1078for (const ImportOption &E : iopts) {1079if (!node_settings.has(E.option.name)) {1080node_settings[E.option.name] = E.default_value;1081}1082}1083}10841085if (Object::cast_to<AnimationPlayer>(p_node)) {1086AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);1087List<StringName> anims;1088ap->get_animation_list(&anims);10891090AnimationImportTracks import_tracks_mode[TRACK_CHANNEL_MAX] = {1091AnimationImportTracks(int(node_settings["import_tracks/position"])),1092AnimationImportTracks(int(node_settings["import_tracks/rotation"])),1093AnimationImportTracks(int(node_settings["import_tracks/scale"]))1094};10951096if (!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)) {1097_optimize_track_usage(ap, import_tracks_mode);1098}1099}11001101return p_node;1102}11031104Node *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) {1105// children first1106for (int i = 0; i < p_node->get_child_count(); i++) {1107Node *r = _post_fix_animations(p_node->get_child(i), p_root, p_node_data, p_animation_data, p_animation_fps, p_remove_immutable_tracks);1108if (!r) {1109i--; //was erased1110}1111}11121113String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));11141115Dictionary node_settings;1116if (p_node_data.has(import_id)) {1117node_settings = p_node_data[import_id];1118}11191120{1121//make sure this is unique1122node_settings = node_settings.duplicate(true);1123//fill node settings for this node with default values1124List<ImportOption> iopts;1125get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);1126for (const ImportOption &E : iopts) {1127if (!node_settings.has(E.option.name)) {1128node_settings[E.option.name] = E.default_value;1129}1130}1131}11321133if (Object::cast_to<AnimationPlayer>(p_node)) {1134AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);1135List<StringName> anims;1136ap->get_animation_list(&anims);11371138if (p_remove_immutable_tracks) {1139AnimationImportTracks import_tracks_mode[TRACK_CHANNEL_MAX] = {1140AnimationImportTracks(int(node_settings["import_tracks/position"])),1141AnimationImportTracks(int(node_settings["import_tracks/rotation"])),1142AnimationImportTracks(int(node_settings["import_tracks/scale"]))1143};1144HashMap<NodePath, bool> used_tracks[TRACK_CHANNEL_MAX];11451146for (const StringName &name : anims) {1147Ref<Animation> anim = ap->get_animation(name);1148int track_count = anim->get_track_count();1149LocalVector<int> tracks_to_keep;1150for (int track_i = 0; track_i < track_count; track_i++) {1151tracks_to_keep.push_back(track_i);1152int track_channel_type = 0;1153switch (anim->track_get_type(track_i)) {1154case Animation::TYPE_POSITION_3D:1155track_channel_type = TRACK_CHANNEL_POSITION;1156break;1157case Animation::TYPE_ROTATION_3D:1158track_channel_type = TRACK_CHANNEL_ROTATION;1159break;1160case Animation::TYPE_SCALE_3D:1161track_channel_type = TRACK_CHANNEL_SCALE;1162break;1163default:1164continue;1165}1166AnimationImportTracks track_mode = import_tracks_mode[track_channel_type];1167NodePath path = anim->track_get_path(track_i);1168Node *n = p_root->get_node(path);1169Node3D *n3d = Object::cast_to<Node3D>(n);1170Skeleton3D *skel = Object::cast_to<Skeleton3D>(n);1171bool keep_track = false;1172Vector3 loc;1173Quaternion rot;1174Vector3 scale;1175if (skel && path.get_subname_count() > 0) {1176StringName bone = path.get_subname(0);1177int bone_idx = skel->find_bone(bone);1178if (bone_idx == -1) {1179continue;1180}1181// Note that this is using get_bone_pose to update the bone pose cache.1182Transform3D bone_rest = skel->get_bone_rest(bone_idx);1183loc = bone_rest.origin / skel->get_motion_scale();1184rot = bone_rest.basis.get_rotation_quaternion();1185scale = bone_rest.basis.get_scale();1186} else if (n3d) {1187loc = n3d->get_position();1188rot = n3d->get_transform().basis.get_rotation_quaternion();1189scale = n3d->get_scale();1190} else {1191continue;1192}11931194if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {1195if (used_tracks[track_channel_type].has(path)) {1196if (used_tracks[track_channel_type][path]) {1197continue;1198}1199} else {1200used_tracks[track_channel_type].insert(path, false);1201}1202}12031204for (int key_i = 0; key_i < anim->track_get_key_count(track_i) && !keep_track; key_i++) {1205switch (track_channel_type) {1206case TRACK_CHANNEL_POSITION: {1207Vector3 key_pos;1208anim->position_track_get_key(track_i, key_i, &key_pos);1209if (!key_pos.is_equal_approx(loc)) {1210keep_track = true;1211if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {1212used_tracks[track_channel_type][path] = true;1213}1214}1215} break;1216case TRACK_CHANNEL_ROTATION: {1217Quaternion key_rot;1218anim->rotation_track_get_key(track_i, key_i, &key_rot);1219if (!key_rot.is_equal_approx(rot)) {1220keep_track = true;1221if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {1222used_tracks[track_channel_type][path] = true;1223}1224}1225} break;1226case TRACK_CHANNEL_SCALE: {1227Vector3 key_scl;1228anim->scale_track_get_key(track_i, key_i, &key_scl);1229if (!key_scl.is_equal_approx(scale)) {1230keep_track = true;1231if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {1232used_tracks[track_channel_type][path] = true;1233}1234}1235} break;1236default:1237break;1238}1239}1240if (track_mode != ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL && !keep_track) {1241tracks_to_keep.remove_at(tracks_to_keep.size() - 1);1242}1243}1244for (int dst_track_i = 0; dst_track_i < (int)tracks_to_keep.size(); dst_track_i++) {1245int src_track_i = tracks_to_keep[dst_track_i];1246if (src_track_i != dst_track_i) {1247anim->track_swap(src_track_i, dst_track_i);1248}1249}1250for (int track_i = track_count - 1; track_i >= (int)tracks_to_keep.size(); track_i--) {1251anim->remove_track(track_i);1252}1253}1254for (const StringName &name : anims) {1255Ref<Animation> anim = ap->get_animation(name);1256int track_count = anim->get_track_count();1257LocalVector<int> tracks_to_keep;1258for (int track_i = 0; track_i < track_count; track_i++) {1259tracks_to_keep.push_back(track_i);1260int track_channel_type = 0;1261switch (anim->track_get_type(track_i)) {1262case Animation::TYPE_POSITION_3D:1263track_channel_type = TRACK_CHANNEL_POSITION;1264break;1265case Animation::TYPE_ROTATION_3D:1266track_channel_type = TRACK_CHANNEL_ROTATION;1267break;1268case Animation::TYPE_SCALE_3D:1269track_channel_type = TRACK_CHANNEL_SCALE;1270break;1271default:1272continue;1273}1274AnimationImportTracks track_mode = import_tracks_mode[track_channel_type];1275if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {1276NodePath path = anim->track_get_path(track_i);1277if (used_tracks[track_channel_type].has(path) && !used_tracks[track_channel_type][path]) {1278tracks_to_keep.remove_at(tracks_to_keep.size() - 1);1279}1280}1281}1282for (int dst_track_i = 0; dst_track_i < (int)tracks_to_keep.size(); dst_track_i++) {1283int src_track_i = tracks_to_keep[dst_track_i];1284if (src_track_i != dst_track_i) {1285anim->track_swap(src_track_i, dst_track_i);1286}1287}1288for (int track_i = track_count - 1; track_i >= (int)tracks_to_keep.size(); track_i--) {1289anim->remove_track(track_i);1290}1291}1292}12931294bool use_optimizer = node_settings["optimizer/enabled"];1295float anim_optimizer_linerr = node_settings["optimizer/max_velocity_error"];1296float anim_optimizer_angerr = node_settings["optimizer/max_angular_error"];1297int anim_optimizer_preerr = node_settings["optimizer/max_precision_error"];12981299if (use_optimizer) {1300_optimize_animations(ap, anim_optimizer_linerr, anim_optimizer_angerr, anim_optimizer_preerr);1301}13021303bool use_compression = node_settings["compression/enabled"];1304int anim_compression_page_size = node_settings["compression/page_size"];13051306if (use_compression) {1307_compress_animations(ap, anim_compression_page_size);1308}13091310for (const StringName &name : anims) {1311Ref<Animation> anim = ap->get_animation(name);1312Array animation_slices;13131314if (p_animation_data.has(name)) {1315Dictionary anim_settings = p_animation_data[name];13161317{1318int slice_count = anim_settings["slices/amount"];13191320for (int i = 0; i < slice_count; i++) {1321String slice_name = anim_settings["slice_" + itos(i + 1) + "/name"];1322int from_frame = anim_settings["slice_" + itos(i + 1) + "/start_frame"];1323int end_frame = anim_settings["slice_" + itos(i + 1) + "/end_frame"];1324Animation::LoopMode loop_mode = static_cast<Animation::LoopMode>((int)anim_settings["slice_" + itos(i + 1) + "/loop_mode"]);1325bool save_to_file = anim_settings["slice_" + itos(i + 1) + "/save_to_file/enabled"];1326String save_to_path = anim_settings["slice_" + itos(i + 1) + "/save_to_file/path"];1327bool save_to_file_keep_custom = anim_settings["slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"];13281329animation_slices.push_back(slice_name);1330animation_slices.push_back(from_frame / p_animation_fps);1331animation_slices.push_back(end_frame / p_animation_fps);1332animation_slices.push_back(loop_mode);1333animation_slices.push_back(save_to_file);1334animation_slices.push_back(save_to_path);1335animation_slices.push_back(save_to_file_keep_custom);1336}13371338if (animation_slices.size() > 0) {1339_create_slices(ap, anim, animation_slices, true);1340}1341}1342{1343//fill with default values1344List<ImportOption> iopts;1345get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION, &iopts);1346for (const ImportOption &F : iopts) {1347if (!anim_settings.has(F.option.name)) {1348anim_settings[F.option.name] = F.default_value;1349}1350}1351}13521353anim->set_loop_mode(static_cast<Animation::LoopMode>((int)anim_settings["settings/loop_mode"]));1354bool save = anim_settings["save_to_file/enabled"];1355String path = anim_settings["save_to_file/path"];1356bool keep_custom = anim_settings["save_to_file/keep_custom_tracks"];13571358Ref<Animation> saved_anim = _save_animation_to_file(anim, save, path, keep_custom);13591360if (saved_anim != anim) {1361Ref<AnimationLibrary> al = ap->get_animation_library(ap->find_animation_library(anim));1362al->add_animation(name, saved_anim); //replace1363}1364}1365}1366}13671368return p_node;1369}13701371Node *ResourceImporterScene::_replace_node_with_type_and_script(Node *p_node, String p_node_type, Ref<Script> p_script) {1372p_node_type = p_node_type.get_slicec(' ', 0); // Full root_type is "ClassName (filename.gd)" for a script global class.1373if (p_script.is_valid()) {1374// Ensure the node type supports the script, or pick one that does.1375String script_base_type = p_script->get_instance_base_type();1376if (ClassDB::is_parent_class(script_base_type, "Node")) {1377if (p_node_type.is_empty() || !ClassDB::is_parent_class(p_node_type, script_base_type)) {1378p_node_type = script_base_type;1379}1380}1381}1382if (!p_node_type.is_empty() && ScriptServer::is_global_class(p_node_type)) {1383// If the user specified a script class, we need to get the base node type.1384if (p_script.is_null()) {1385p_script = ResourceLoader::load(ScriptServer::get_global_class_path(p_node_type));1386}1387p_node_type = ScriptServer::get_global_class_base(p_node_type);1388while (!p_node_type.is_empty()) {1389if (ScriptServer::is_global_class(p_node_type)) {1390p_node_type = ScriptServer::get_global_class_base(p_node_type);1391} else {1392break;1393}1394}1395}1396if (!p_node_type.is_empty() && p_node->get_class_name() != p_node_type) {1397// If the user specified a Godot node type that does not match1398// what the scene import gave us, replace the root node.1399Node *new_base_node = Object::cast_to<Node>(ClassDB::instantiate(p_node_type));1400if (new_base_node) {1401List<PropertyInfo> old_properties;1402p_node->get_property_list(&old_properties);1403for (const PropertyInfo &prop : old_properties) {1404if (!(prop.usage & PROPERTY_USAGE_STORAGE)) {1405continue;1406}1407new_base_node->set(prop.name, p_node->get(prop.name));1408}1409new_base_node->set_name(p_node->get_name());1410_copy_meta(p_node, new_base_node);1411p_node->replace_by(new_base_node);1412p_node->set_owner(nullptr);1413memdelete(p_node);1414p_node = new_base_node;1415}1416}1417if (p_script.is_valid()) {1418p_node->set_script(Variant(p_script));1419}1420return p_node;1421}14221423Node *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) {1424// children first1425for (int i = 0; i < p_node->get_child_count(); i++) {1426Node *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);1427if (!r) {1428i--; //was erased1429}1430}14311432int extract_mat = 0;1433if (p_options.has("materials/extract")) {1434extract_mat = p_options["materials/extract"];1435}14361437String spath = p_source_file.get_base_dir();1438if (p_options.has("materials/extract_path")) {1439String extpath = p_options["materials/extract_path"];1440if (!extpath.is_empty()) {1441spath = extpath;1442}1443}14441445bool isroot = p_node == p_root;14461447String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));14481449Dictionary node_settings;1450if (p_node_data.has(import_id)) {1451node_settings = p_node_data[import_id];1452}14531454if (!isroot && (node_settings.has("import/skip_import") && bool(node_settings["import/skip_import"]))) {1455p_node->set_owner(nullptr);1456memdelete(p_node);1457return nullptr;1458}14591460{1461//make sure this is unique1462node_settings = node_settings.duplicate(true);1463//fill node settings for this node with default values1464List<ImportOption> iopts;1465if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1466get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE, &iopts);1467} else if (Object::cast_to<AnimationPlayer>(p_node)) {1468get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);1469} else if (Object::cast_to<Skeleton3D>(p_node)) {1470get_internal_import_options(INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, &iopts);1471} else {1472get_internal_import_options(INTERNAL_IMPORT_CATEGORY_NODE, &iopts);1473}1474for (const ImportOption &E : iopts) {1475if (!node_settings.has(E.option.name)) {1476node_settings[E.option.name] = E.default_value;1477}1478}1479}14801481{1482ObjectID node_id = p_node->get_instance_id();1483for (int i = 0; i < post_importer_plugins.size(); i++) {1484post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_NODE, p_root, p_node, Ref<Resource>(), node_settings);1485if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue1486break;1487}1488}1489}14901491if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1492ObjectID node_id = p_node->get_instance_id();1493for (int i = 0; i < post_importer_plugins.size(); i++) {1494post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE, p_root, p_node, Ref<Resource>(), node_settings);1495if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue1496break;1497}1498}1499}15001501if (Object::cast_to<Skeleton3D>(p_node)) {1502Ref<Animation> rest_animation;1503float rest_animation_timestamp = 0.0;1504Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);1505if (skeleton != nullptr && int(node_settings.get("rest_pose/load_pose", 0)) != 0) {1506String selected_animation_name = node_settings.get("rest_pose/selected_animation", String());1507if (int(node_settings["rest_pose/load_pose"]) == 1) {1508TypedArray<Node> children = p_root->find_children("*", "AnimationPlayer", true, false);1509for (int node_i = 0; node_i < children.size(); node_i++) {1510AnimationPlayer *anim_player = cast_to<AnimationPlayer>(children[node_i]);1511ERR_CONTINUE(anim_player == nullptr);1512List<StringName> anim_list;1513anim_player->get_animation_list(&anim_list);1514if (anim_list.size() == 1) {1515selected_animation_name = anim_list.front()->get();1516}1517rest_animation = anim_player->get_animation(selected_animation_name);1518if (rest_animation.is_valid()) {1519break;1520}1521}1522} else if (int(node_settings["rest_pose/load_pose"]) == 2) {1523Object *external_object = node_settings.get("rest_pose/external_animation_library", Variant());1524rest_animation = external_object;1525if (rest_animation.is_null()) {1526Ref<AnimationLibrary> library(external_object);1527if (library.is_valid()) {1528List<StringName> anim_list;1529library->get_animation_list(&anim_list);1530if (anim_list.size() == 1) {1531selected_animation_name = String(anim_list.front()->get());1532}1533rest_animation = library->get_animation(selected_animation_name);1534}1535}1536}1537rest_animation_timestamp = double(node_settings.get("rest_pose/selected_timestamp", 0.0));1538if (rest_animation.is_valid()) {1539for (int track_i = 0; track_i < rest_animation->get_track_count(); track_i++) {1540NodePath path = rest_animation->track_get_path(track_i);1541StringName node_path = path.get_concatenated_names();1542if (String(node_path).begins_with("%")) {1543continue; // Unique node names are commonly used with retargeted animations, which we do not want to use.1544}1545StringName skeleton_bone = path.get_concatenated_subnames();1546if (skeleton_bone == StringName()) {1547continue;1548}1549int bone_idx = skeleton->find_bone(skeleton_bone);1550if (bone_idx == -1) {1551continue;1552}1553switch (rest_animation->track_get_type(track_i)) {1554case Animation::TYPE_POSITION_3D: {1555Vector3 bone_position = rest_animation->position_track_interpolate(track_i, rest_animation_timestamp);1556skeleton->set_bone_rest(bone_idx, Transform3D(skeleton->get_bone_rest(bone_idx).basis, bone_position));1557} break;1558case Animation::TYPE_ROTATION_3D: {1559Quaternion bone_rotation = rest_animation->rotation_track_interpolate(track_i, rest_animation_timestamp);1560Transform3D current_rest = skeleton->get_bone_rest(bone_idx);1561skeleton->set_bone_rest(bone_idx, Transform3D(Basis(bone_rotation).scaled(current_rest.basis.get_scale()), current_rest.origin));1562} break;1563default:1564break;1565}1566}1567}1568}15691570ObjectID node_id = p_node->get_instance_id();1571for (int i = 0; i < post_importer_plugins.size(); i++) {1572post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, p_root, p_node, Ref<Resource>(), node_settings);1573if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue1574break;1575}1576}1577}15781579if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1580ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);15811582Ref<ImporterMesh> m = mi->get_mesh();15831584if (m.is_valid()) {1585if (!r_scanned_meshes.has(m)) {1586for (int i = 0; i < m->get_surface_count(); i++) {1587Ref<Material> mat = m->get_surface_material(i);1588if (mat.is_valid()) {1589String mat_id = mat->get_meta("import_id", mat->get_name());1590if (!mat_id.is_empty() && p_material_data.has(mat_id)) {1591Dictionary matdata = p_material_data[mat_id];1592{1593//fill node settings for this node with default values1594List<ImportOption> iopts;1595get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MATERIAL, &iopts);1596for (const ImportOption &E : iopts) {1597if (!matdata.has(E.option.name)) {1598matdata[E.option.name] = E.default_value;1599}1600}1601}16021603for (int j = 0; j < post_importer_plugins.size(); j++) {1604post_importer_plugins.write[j]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL, p_root, p_node, mat, matdata);1605}1606}1607if (!mat_id.is_empty() && extract_mat != 0) {1608String ext = material_extension[p_options.has("materials/extract_format") ? (int)p_options["materials/extract_format"] : 0];1609String path = spath.path_join(mat_id.validate_filename() + ext);1610String uid_path = ResourceUID::path_to_uid(path);16111612Dictionary matdata = p_material_data[mat_id];1613matdata["use_external/enabled"] = true;1614matdata["use_external/path"] = uid_path;1615matdata["use_external/fallback_path"] = path;1616if (!FileAccess::exists(path) || extract_mat == 2 /*overwrite*/) {1617ResourceSaver::save(mat, path);1618}16191620Ref<Material> external_mat = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE);1621if (external_mat.is_valid()) {1622m->set_surface_material(i, external_mat);1623}1624}1625if (!mat_id.is_empty() && p_material_data.has(mat_id)) {1626Dictionary matdata = p_material_data[mat_id];1627if (matdata.has("use_external/enabled") && bool(matdata["use_external/enabled"]) && matdata.has("use_external/path")) {1628String path = matdata["use_external/path"];1629Ref<Material> external_mat = ResourceLoader::load(path);1630if (external_mat.is_null()) {1631if (matdata.has("use_external/fallback_path")) {1632String fallback_save_path = matdata["use_external/fallback_path"];1633if (!fallback_save_path.is_empty()) {1634external_mat = ResourceLoader::load(fallback_save_path);1635if (external_mat.is_valid()) {1636path = fallback_save_path;1637}1638}1639}1640}1641if (external_mat.is_valid()) {1642m->set_surface_material(i, external_mat);1643if (!path.begins_with("uid://")) {1644const ResourceUID::ID id = ResourceLoader::get_resource_uid(path);1645if (id != ResourceUID::INVALID_ID) {1646matdata["use_external/path"] = ResourceUID::get_singleton()->id_to_text(id);1647}1648}1649matdata["use_external/fallback_path"] = external_mat->get_path();1650}1651}1652}1653}1654}16551656r_scanned_meshes.insert(m);1657}16581659if (node_settings.has("generate/physics")) {1660int mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_DISABLED;16611662const bool generate_collider = node_settings["generate/physics"];1663if (generate_collider) {1664mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_MESH_AND_STATIC_COLLIDER;1665if (node_settings.has("physics/body_type")) {1666const BodyType body_type = (BodyType)node_settings["physics/body_type"].operator int();1667switch (body_type) {1668case BODY_TYPE_STATIC:1669mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_MESH_AND_STATIC_COLLIDER;1670break;1671case BODY_TYPE_DYNAMIC:1672mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_RIGID_BODY_AND_MESH;1673break;1674case BODY_TYPE_AREA:1675mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_AREA_ONLY;1676break;1677}1678}1679}16801681if (mesh_physics_mode != MeshPhysicsMode::MESH_PHYSICS_DISABLED) {1682Vector<Ref<Shape3D>> shapes;1683if (collision_map.has(m)) {1684shapes = collision_map[m];1685} else {1686shapes = get_collision_shapes(1687m,1688node_settings,1689p_applied_root_scale);1690}16911692if (shapes.size()) {1693CollisionObject3D *base = nullptr;1694switch (mesh_physics_mode) {1695case MESH_PHYSICS_MESH_AND_STATIC_COLLIDER: {1696StaticBody3D *col = memnew(StaticBody3D);1697p_node->add_child(col, true);1698col->set_owner(p_node->get_owner());1699col->set_transform(get_collision_shapes_transform(node_settings));1700col->set_position(p_applied_root_scale * col->get_position());1701const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];1702if (pmo.is_valid()) {1703col->set_physics_material_override(pmo);1704}1705base = col;1706} break;1707case MESH_PHYSICS_RIGID_BODY_AND_MESH: {1708RigidBody3D *rigid_body = memnew(RigidBody3D);1709rigid_body->set_name(p_node->get_name());1710_copy_meta(p_node, rigid_body);1711p_node->replace_by(rigid_body);1712rigid_body->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));1713rigid_body->set_position(p_applied_root_scale * rigid_body->get_position());1714p_node = rigid_body;1715mi->set_transform(Transform3D());1716rigid_body->add_child(mi, true);1717mi->set_owner(rigid_body->get_owner());1718const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];1719if (pmo.is_valid()) {1720rigid_body->set_physics_material_override(pmo);1721}1722base = rigid_body;1723} break;1724case MESH_PHYSICS_STATIC_COLLIDER_ONLY: {1725StaticBody3D *col = memnew(StaticBody3D);1726col->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));1727col->set_position(p_applied_root_scale * col->get_position());1728col->set_name(p_node->get_name());1729_copy_meta(p_node, col);1730p_node->replace_by(col);1731p_node->set_owner(nullptr);1732memdelete(p_node);1733p_node = col;1734const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];1735if (pmo.is_valid()) {1736col->set_physics_material_override(pmo);1737}1738base = col;1739} break;1740case MESH_PHYSICS_AREA_ONLY: {1741Area3D *area = memnew(Area3D);1742area->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));1743area->set_position(p_applied_root_scale * area->get_position());1744area->set_name(p_node->get_name());1745_copy_meta(p_node, area);1746p_node->replace_by(area);1747p_node->set_owner(nullptr);1748memdelete(p_node);1749p_node = area;1750base = area;17511752} break;1753}17541755base->set_collision_layer(node_settings["physics/layer"]);1756base->set_collision_mask(node_settings["physics/mask"]);17571758for (const Ref<Shape3D> &E : shapes) {1759CollisionShape3D *cshape = memnew(CollisionShape3D);1760cshape->set_shape(E);1761base->add_child(cshape, true);17621763cshape->set_owner(base->get_owner());1764}1765}1766}1767}1768}1769}17701771//navmesh (node may have changed type above)1772if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1773ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);17741775Ref<ImporterMesh> m = mi->get_mesh();17761777if (m.is_valid()) {1778if (node_settings.has("generate/navmesh")) {1779int navmesh_mode = node_settings["generate/navmesh"];17801781if (navmesh_mode != NAVMESH_DISABLED) {1782NavigationRegion3D *nmi = memnew(NavigationRegion3D);17831784Ref<NavigationMesh> nmesh = m->create_navigation_mesh();1785nmi->set_navigation_mesh(nmesh);17861787if (navmesh_mode == NAVMESH_NAVMESH_ONLY) {1788nmi->set_transform(mi->get_transform());1789_copy_meta(p_node, nmi);1790p_node->replace_by(nmi);1791p_node->set_owner(nullptr);1792memdelete(p_node);1793p_node = nmi;1794} else {1795mi->add_child(nmi, true);1796nmi->set_owner(mi->get_owner());1797}1798}1799}1800}1801}18021803if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1804ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);18051806Ref<ImporterMesh> m = mi->get_mesh();18071808if (m.is_valid()) {1809if (node_settings.has("generate/occluder")) {1810int occluder_mode = node_settings["generate/occluder"];18111812if (occluder_mode != OCCLUDER_DISABLED) {1813float simplification_dist = 0.0f;1814if (node_settings.has("occluder/simplification_distance")) {1815simplification_dist = node_settings["occluder/simplification_distance"];1816}18171818OccluderInstance3D::bake_single_node(mi, simplification_dist, r_occluder_arrays.first, r_occluder_arrays.second);18191820if (occluder_mode == OCCLUDER_OCCLUDER_ONLY) {1821p_node->set_owner(nullptr);1822memdelete(p_node);1823p_node = nullptr;1824}1825}1826}1827}1828}18291830if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {1831ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);18321833if (node_settings.has("mesh_instance/layers")) {1834mi->set_layer_mask(node_settings["mesh_instance/layers"]);1835}18361837if (node_settings.has("mesh_instance/visibility_range_begin")) {1838mi->set_visibility_range_begin(node_settings["mesh_instance/visibility_range_begin"]);1839}18401841if (node_settings.has("mesh_instance/visibility_range_begin_margin")) {1842mi->set_visibility_range_begin_margin(node_settings["mesh_instance/visibility_range_begin_margin"]);1843}18441845if (node_settings.has("mesh_instance/visibility_range_end")) {1846mi->set_visibility_range_end(node_settings["mesh_instance/visibility_range_end"]);1847}18481849if (node_settings.has("mesh_instance/visibility_range_end_margin")) {1850mi->set_visibility_range_end_margin(node_settings["mesh_instance/visibility_range_end_margin"]);1851}18521853if (node_settings.has("mesh_instance/visibility_range_fade_mode")) {1854const GeometryInstance3D::VisibilityRangeFadeMode range_fade_mode = (GeometryInstance3D::VisibilityRangeFadeMode)node_settings["mesh_instance/visibility_range_fade_mode"].operator int();1855mi->set_visibility_range_fade_mode(range_fade_mode);1856}18571858if (node_settings.has("mesh_instance/cast_shadow")) {1859const GeometryInstance3D::ShadowCastingSetting cast_shadows = (GeometryInstance3D::ShadowCastingSetting)node_settings["mesh_instance/cast_shadow"].operator int();1860mi->set_cast_shadows_setting(cast_shadows);1861}1862}18631864if (Object::cast_to<AnimationPlayer>(p_node)) {1865AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);18661867for (int i = 0; i < post_importer_plugins.size(); i++) {1868post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, p_root, p_node, Ref<Resource>(), node_settings);1869}18701871if (post_importer_plugins.size()) {1872List<StringName> anims;1873ap->get_animation_list(&anims);1874for (const StringName &name : anims) {1875if (p_animation_data.has(name)) {1876Ref<Animation> anim = ap->get_animation(name);1877Dictionary anim_settings = p_animation_data[name];1878{1879//fill with default values1880List<ImportOption> iopts;1881get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION, &iopts);1882for (const ImportOption &F : iopts) {1883if (!anim_settings.has(F.option.name)) {1884anim_settings[F.option.name] = F.default_value;1885}1886}1887}18881889for (int i = 0; i < post_importer_plugins.size(); i++) {1890post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION, p_root, p_node, anim, anim_settings);1891}1892}1893}1894}1895}18961897String node_type = node_settings.get("node/node_type", "");1898Ref<Script> node_script = node_settings.get("node/script", Ref<Script>());1899p_node = _replace_node_with_type_and_script(p_node, node_type, node_script);19001901return p_node;1902}19031904Ref<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) {1905String res_path = ResourceUID::ensure_path(p_save_to_path);1906if (!p_save_to_file || !res_path.is_resource_file()) {1907return anim;1908}19091910if (FileAccess::exists(res_path) && p_keep_custom_tracks) {1911// Copy custom animation tracks from previously imported files.1912Ref<Animation> old_anim = ResourceLoader::load(res_path, "Animation", ResourceFormatLoader::CACHE_MODE_IGNORE);1913if (old_anim.is_valid()) {1914for (int i = 0; i < old_anim->get_track_count(); i++) {1915if (!old_anim->track_is_imported(i)) {1916old_anim->copy_track(i, anim);1917}1918}1919anim->set_loop_mode(old_anim->get_loop_mode());1920}1921}19221923if (ResourceCache::has(res_path)) {1924Ref<Animation> old_anim = ResourceCache::get_ref(res_path);1925if (old_anim.is_valid()) {1926old_anim->copy_from(anim);1927anim = old_anim;1928}1929}1930anim->set_path(res_path, true); // Set path to save externally.1931Error err = ResourceSaver::save(anim, res_path, ResourceSaver::FLAG_CHANGE_PATH);19321933ERR_FAIL_COND_V_MSG(err != OK, anim, "Saving of animation failed: " + res_path);1934if (p_save_to_path.begins_with("uid://")) {1935// slow1936ResourceSaver::set_uid(res_path, ResourceUID::get_singleton()->text_to_id(p_save_to_path));1937}1938return anim;1939}19401941void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref<Animation> anim, const Array &p_slices, bool p_bake_all) {1942Ref<AnimationLibrary> al = ap->get_animation_library(ap->find_animation_library(anim));19431944for (int i = 0; i < p_slices.size(); i += 7) {1945String name = p_slices[i];1946float from = p_slices[i + 1];1947float to = p_slices[i + 2];1948Animation::LoopMode loop_mode = static_cast<Animation::LoopMode>((int)p_slices[i + 3]);1949bool save_to_file = p_slices[i + 4];1950String save_to_path = p_slices[i + 5];1951bool keep_current = p_slices[i + 6];1952if (from >= to) {1953continue;1954}19551956Ref<Animation> new_anim = memnew(Animation);19571958for (int j = 0; j < anim->get_track_count(); j++) {1959List<float> keys;1960int kc = anim->track_get_key_count(j);1961int dtrack = -1;1962for (int k = 0; k < kc; k++) {1963float kt = anim->track_get_key_time(j, k);1964if (kt >= from && kt < to) {1965//found a key within range, so create track1966if (dtrack == -1) {1967new_anim->add_track(anim->track_get_type(j));1968dtrack = new_anim->get_track_count() - 1;1969new_anim->track_set_path(dtrack, anim->track_get_path(j));1970new_anim->track_set_imported(dtrack, true);19711972if (kt > (from + 0.01) && k > 0) {1973if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {1974Vector3 p;1975anim->try_position_track_interpolate(j, from, &p);1976new_anim->position_track_insert_key(dtrack, 0, p);1977} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {1978Quaternion r;1979anim->try_rotation_track_interpolate(j, from, &r);1980new_anim->rotation_track_insert_key(dtrack, 0, r);1981} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {1982Vector3 s;1983anim->try_scale_track_interpolate(j, from, &s);1984new_anim->scale_track_insert_key(dtrack, 0, s);1985} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {1986Variant var = anim->value_track_interpolate(j, from);1987new_anim->track_insert_key(dtrack, 0, var);1988} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {1989float interp;1990anim->try_blend_shape_track_interpolate(j, from, &interp);1991new_anim->blend_shape_track_insert_key(dtrack, 0, interp);1992}1993}1994}19951996if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {1997Vector3 p;1998anim->position_track_get_key(j, k, &p);1999new_anim->position_track_insert_key(dtrack, kt - from, p);2000} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {2001Quaternion r;2002anim->rotation_track_get_key(j, k, &r);2003new_anim->rotation_track_insert_key(dtrack, kt - from, r);2004} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {2005Vector3 s;2006anim->scale_track_get_key(j, k, &s);2007new_anim->scale_track_insert_key(dtrack, kt - from, s);2008} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {2009Variant var = anim->track_get_key_value(j, k);2010new_anim->track_insert_key(dtrack, kt - from, var);2011} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {2012float interp;2013anim->blend_shape_track_get_key(j, k, &interp);2014new_anim->blend_shape_track_insert_key(dtrack, kt - from, interp);2015}2016}20172018if (dtrack != -1 && kt >= to) {2019if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {2020Vector3 p;2021anim->try_position_track_interpolate(j, to, &p);2022new_anim->position_track_insert_key(dtrack, to - from, p);2023} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {2024Quaternion r;2025anim->try_rotation_track_interpolate(j, to, &r);2026new_anim->rotation_track_insert_key(dtrack, to - from, r);2027} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {2028Vector3 s;2029anim->try_scale_track_interpolate(j, to, &s);2030new_anim->scale_track_insert_key(dtrack, to - from, s);2031} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {2032Variant var = anim->value_track_interpolate(j, to);2033new_anim->track_insert_key(dtrack, to - from, var);2034} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {2035float interp;2036anim->try_blend_shape_track_interpolate(j, to, &interp);2037new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);2038}2039}2040}20412042if (dtrack == -1 && p_bake_all) {2043new_anim->add_track(anim->track_get_type(j));2044dtrack = new_anim->get_track_count() - 1;2045new_anim->track_set_path(dtrack, anim->track_get_path(j));2046new_anim->track_set_imported(dtrack, true);2047if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {2048Vector3 p;2049anim->try_position_track_interpolate(j, from, &p);2050new_anim->position_track_insert_key(dtrack, 0, p);2051anim->try_position_track_interpolate(j, to, &p);2052new_anim->position_track_insert_key(dtrack, to - from, p);2053} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {2054Quaternion r;2055anim->try_rotation_track_interpolate(j, from, &r);2056new_anim->rotation_track_insert_key(dtrack, 0, r);2057anim->try_rotation_track_interpolate(j, to, &r);2058new_anim->rotation_track_insert_key(dtrack, to - from, r);2059} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {2060Vector3 s;2061anim->try_scale_track_interpolate(j, from, &s);2062new_anim->scale_track_insert_key(dtrack, 0, s);2063anim->try_scale_track_interpolate(j, to, &s);2064new_anim->scale_track_insert_key(dtrack, to - from, s);2065} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {2066Variant var = anim->value_track_interpolate(j, from);2067new_anim->track_insert_key(dtrack, 0, var);2068Variant to_var = anim->value_track_interpolate(j, to);2069new_anim->track_insert_key(dtrack, to - from, to_var);2070} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {2071float interp;2072anim->try_blend_shape_track_interpolate(j, from, &interp);2073new_anim->blend_shape_track_insert_key(dtrack, 0, interp);2074anim->try_blend_shape_track_interpolate(j, to, &interp);2075new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);2076}2077}2078}20792080new_anim->set_name(name);2081new_anim->set_loop_mode(loop_mode);2082new_anim->set_length(to - from);2083new_anim->set_step(anim->get_step());20842085al->add_animation(name, new_anim);20862087Ref<Animation> saved_anim = _save_animation_to_file(new_anim, save_to_file, save_to_path, keep_current);2088if (saved_anim != new_anim) {2089al->add_animation(name, saved_anim);2090}2091}20922093al->remove_animation(ap->find_animation(anim)); // Remove original animation (no longer needed).2094}20952096void ResourceImporterScene::_optimize_animations(AnimationPlayer *anim, float p_max_vel_error, float p_max_ang_error, int p_prc_error) {2097List<StringName> anim_names;2098anim->get_animation_list(&anim_names);2099for (const StringName &E : anim_names) {2100Ref<Animation> a = anim->get_animation(E);2101a->optimize(p_max_vel_error, p_max_ang_error, p_prc_error);2102}2103}21042105void ResourceImporterScene::_compress_animations(AnimationPlayer *anim, int p_page_size_kb) {2106List<StringName> anim_names;2107anim->get_animation_list(&anim_names);2108for (const StringName &E : anim_names) {2109Ref<Animation> a = anim->get_animation(E);2110a->compress(p_page_size_kb * 1024);2111}2112}21132114void ResourceImporterScene::get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const {2115switch (p_category) {2116case INTERNAL_IMPORT_CATEGORY_NODE: {2117r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "node/node_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));2118r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));2119r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2120} break;2121case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {2122r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));2123r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2124r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate/physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2125r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));2126r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/body_type", PROPERTY_HINT_ENUM, "Static,Dynamic,Area"), 0));2127r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/shape_type", PROPERTY_HINT_ENUM, "Decompose Convex,Simple Convex,Trimesh,Box,Sphere,Cylinder,Capsule,Automatic", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 7));2128r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "physics/physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), Variant()));2129r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1));2130r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1));21312132r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mesh_instance/layers", PROPERTY_HINT_LAYERS_3D_RENDER), 1));2133r_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));2134r_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));2135r_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));2136r_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));2137r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mesh_instance/visibility_range_fade_mode", PROPERTY_HINT_ENUM, "Disabled,Self,Dependencies"), GeometryInstance3D::VISIBILITY_RANGE_FADE_DISABLED));2138r_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));21392140// Decomposition2141Ref<MeshConvexDecompositionSettings> decomposition_default = Ref<MeshConvexDecompositionSettings>();2142decomposition_default.instantiate();2143r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/advanced", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2144r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/precision", PROPERTY_HINT_RANGE, "1,10,1"), 5));2145r_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()));2146r_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()));2147r_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()));2148r_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()));2149r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/resolution", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_resolution()));2150r_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()));2151r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/plane_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_plane_downsampling()));2152r_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()));2153r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/normalize_mesh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_normalize_mesh()));2154r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/mode", PROPERTY_HINT_ENUM, "Voxel,Tetrahedron", PROPERTY_USAGE_DEFAULT), static_cast<int>(decomposition_default->get_mode())));2155r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/convexhull_approximation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_convex_hull_approximation()));2156r_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()));2157r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_project_hull_vertices()));21582159// Primitives: Box, Sphere, Cylinder, Capsule.2160r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3(2.0, 2.0, 2.0)));2161r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/height", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), 1.0));2162r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/radius", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), 1.0));2163r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3()));2164r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3()));21652166r_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));2167r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "occluder/simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01", PROPERTY_USAGE_DEFAULT), 0.1f));2168} break;2169case INTERNAL_IMPORT_CATEGORY_MESH: {2170r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2171r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.tres"), ""));2172r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));2173r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/shadow_meshes", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));2174r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lightmap_uv", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));2175r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));2176r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_merge_angle", PROPERTY_HINT_RANGE, "0,180,1,degrees"), 20.0f));2177} break;2178case INTERNAL_IMPORT_CATEGORY_MATERIAL: {2179r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "use_external/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2180r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "use_external/path", PROPERTY_HINT_FILE, "*.material,*.res,*.tres"), ""));2181r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "use_external/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));2182} break;2183case INTERNAL_IMPORT_CATEGORY_ANIMATION: {2184r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "settings/loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Pingpong"), 0));2185r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2186r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.anim,*.tres"), ""));2187r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));2188r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/keep_custom_tracks"), ""));2189r_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));21902191for (int i = 0; i < 256; i++) {2192r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/name"), ""));2193r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/start_frame"), 0));2194r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/end_frame"), 0));2195r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Pingpong"), 0));2196r_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));2197r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.anim,*.tres"), ""));2198r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/save_to_file/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));2199r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"), false));2200}2201} break;2202case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {2203r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));2204r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2205r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));2206r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_velocity_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));2207r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));2208r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "optimizer/max_precision_error", PROPERTY_HINT_NONE, "1,6,1"), 3));2209r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compression/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2210r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compression/page_size", PROPERTY_HINT_RANGE, "4,512,1,suffix:kb"), 8));2211r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));2212r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));2213r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));2214} break;2215case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {2216r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));2217r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));2218r_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));2219r_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()));2220r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "rest_pose/selected_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), ""));2221r_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));2222String mismatched_or_empty_profile_warning = String(2223"The external rest animation is missing some bones. "2224"Consider disabling Remove Immutable Tracks on the other file."); // TODO: translate.2225r_options->push_back(ImportOption(2226PropertyInfo(2227Variant::STRING, U"rest_pose/\u26A0_validation_warning/mismatched_or_empty_profile",2228PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),2229Variant(mismatched_or_empty_profile_warning)));2230String profile_must_not_be_retargeted_warning = String(2231"This external rest animation appears to have been imported with a BoneMap. "2232"Disable the bone map when exporting a rest animation from the reference model."); // TODO: translate.2233r_options->push_back(ImportOption(2234PropertyInfo(2235Variant::STRING, U"rest_pose/\u26A0_validation_warning/profile_must_not_be_retargeted",2236PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),2237Variant(profile_must_not_be_retargeted_warning)));2238String no_animation_warning = String(2239"Select an animation: Find a FBX or glTF in a compatible rest pose "2240"and export a compatible animation from its import settings."); // TODO: translate.2241r_options->push_back(ImportOption(2242PropertyInfo(2243Variant::STRING, U"rest_pose//no_animation_chosen",2244PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),2245Variant(no_animation_warning)));2246r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "retarget/bone_map", PROPERTY_HINT_RESOURCE_TYPE, "BoneMap", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Variant()));2247} break;2248default: {2249}2250}22512252for (int i = 0; i < post_importer_plugins.size(); i++) {2253post_importer_plugins.write[i]->get_internal_import_options(EditorScenePostImportPlugin::InternalImportCategory(p_category), r_options);2254}2255}22562257bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const {2258if (p_options.has("import/skip_import") && p_option != "import/skip_import" && bool(p_options["import/skip_import"])) {2259return false; //if skip import2260}2261switch (p_category) {2262case INTERNAL_IMPORT_CATEGORY_NODE: {2263} break;2264case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {2265const bool generate_physics =2266p_options.has("generate/physics") &&2267p_options["generate/physics"].operator bool();22682269if (p_option.contains("physics/")) {2270// Show if need to generate collisions.2271return generate_physics;2272}22732274if (p_option.contains("decomposition/")) {2275// Show if need to generate collisions.2276if (generate_physics &&2277// Show if convex is enabled.2278p_options["physics/shape_type"] == Variant(SHAPE_TYPE_DECOMPOSE_CONVEX)) {2279if (p_option == "decomposition/advanced") {2280return true;2281}22822283const bool decomposition_advanced =2284p_options.has("decomposition/advanced") &&2285p_options["decomposition/advanced"].operator bool();22862287if (p_option == "decomposition/precision") {2288return !decomposition_advanced;2289} else {2290return decomposition_advanced;2291}2292}22932294return false;2295}22962297if (p_option == "primitive/position" || p_option == "primitive/rotation") {2298const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();2299return generate_physics &&2300physics_shape >= SHAPE_TYPE_BOX;2301}23022303if (p_option == "primitive/size") {2304const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();2305return generate_physics &&2306physics_shape == SHAPE_TYPE_BOX;2307}23082309if (p_option == "primitive/radius") {2310const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();2311return generate_physics &&2312(physics_shape == SHAPE_TYPE_SPHERE ||2313physics_shape == SHAPE_TYPE_CYLINDER ||2314physics_shape == SHAPE_TYPE_CAPSULE);2315}23162317if (p_option == "primitive/height") {2318const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();2319return generate_physics &&2320(physics_shape == SHAPE_TYPE_CYLINDER ||2321physics_shape == SHAPE_TYPE_CAPSULE);2322}23232324if (p_option == "occluder/simplification_distance") {2325// Show only if occluder generation is enabled2326return p_options.has("generate/occluder") && p_options["generate/occluder"].operator signed int() != OCCLUDER_DISABLED;2327}2328} break;2329case INTERNAL_IMPORT_CATEGORY_MESH: {2330if (p_option == "save_to_file/path") {2331return p_options["save_to_file/enabled"];2332}2333} break;2334case INTERNAL_IMPORT_CATEGORY_MATERIAL: {2335if (p_option == "use_external/path") {2336return p_options["use_external/enabled"];2337}2338} break;2339case INTERNAL_IMPORT_CATEGORY_ANIMATION: {2340if (p_option == "save_to_file/path" || p_option == "save_to_file/keep_custom_tracks") {2341return p_options["save_to_file/enabled"];2342}2343if (p_option.begins_with("slice_")) {2344int max_slice = p_options["slices/amount"];2345int slice = p_option.get_slicec('_', 1).to_int() - 1;2346if (slice >= max_slice) {2347return false;2348}2349}2350} break;2351case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {2352if (p_option.begins_with("optimizer/") && p_option != "optimizer/enabled" && !bool(p_options["optimizer/enabled"])) {2353return false;2354}2355if (p_option.begins_with("compression/") && p_option != "compression/enabled" && !bool(p_options["compression/enabled"])) {2356return false;2357}2358} break;2359case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {2360const bool use_retarget = Object::cast_to<BoneMap>(p_options["retarget/bone_map"].get_validated_object()) != nullptr;2361if (!use_retarget && p_option != "retarget/bone_map" && p_option.begins_with("retarget/")) {2362return false;2363}2364int rest_warning = 0;2365if (p_option.begins_with("rest_pose/")) {2366if (!p_options.has("rest_pose/load_pose") || int(p_options["rest_pose/load_pose"]) == 0) {2367if (p_option != "rest_pose/load_pose") {2368return false;2369}2370} else if (int(p_options["rest_pose/load_pose"]) == 1) {2371if (p_option == "rest_pose/external_animation_library") {2372return false;2373}2374} else if (int(p_options["rest_pose/load_pose"]) == 2) {2375Object *res = p_options["rest_pose/external_animation_library"];2376Ref<Animation> anim(res);2377if (anim.is_valid() && p_option == "rest_pose/selected_animation") {2378return false;2379}2380Ref<AnimationLibrary> library(res);2381String selected_animation_name = p_options["rest_pose/selected_animation"];2382if (library.is_valid()) {2383List<StringName> anim_list;2384library->get_animation_list(&anim_list);2385if (anim_list.size() == 1) {2386selected_animation_name = String(anim_list.front()->get());2387}2388if (library->has_animation(selected_animation_name)) {2389anim = library->get_animation(selected_animation_name);2390}2391}2392int found_bone_count = 0;2393Ref<BoneMap> bone_map;2394Ref<SkeletonProfile> prof;2395if (p_options.has("retarget/bone_map")) {2396bone_map = p_options["retarget/bone_map"];2397}2398if (bone_map.is_valid()) {2399prof = bone_map->get_profile();2400}2401if (anim.is_valid()) {2402HashSet<StringName> target_bones;2403if (bone_map.is_valid() && prof.is_valid()) {2404for (int target_i = 0; target_i < prof->get_bone_size(); target_i++) {2405StringName skeleton_bone_name = bone_map->get_skeleton_bone_name(prof->get_bone_name(target_i));2406if (skeleton_bone_name) {2407target_bones.insert(skeleton_bone_name);2408}2409}2410}2411for (int track_i = 0; track_i < anim->get_track_count(); track_i++) {2412if (anim->track_get_type(track_i) != Animation::TYPE_POSITION_3D && anim->track_get_type(track_i) != Animation::TYPE_ROTATION_3D) {2413continue;2414}2415NodePath path = anim->track_get_path(track_i);2416StringName node_path = path.get_concatenated_names();2417StringName skeleton_bone = path.get_concatenated_subnames();2418if (skeleton_bone) {2419if (String(node_path).begins_with("%")) {2420rest_warning = 1;2421}2422if (target_bones.has(skeleton_bone)) {2423target_bones.erase(skeleton_bone);2424}2425found_bone_count++;2426}2427}2428if ((found_bone_count < 15 || !target_bones.is_empty()) && rest_warning != 1) {2429rest_warning = 2; // heuristic: animation targeted too few bones.2430}2431} else {2432rest_warning = 3;2433}2434}2435if (p_option.begins_with("rest_pose/") && p_option.ends_with("profile_must_not_be_retargeted")) {2436return rest_warning == 1;2437}2438if (p_option.begins_with("rest_pose/") && p_option.ends_with("mismatched_or_empty_profile")) {2439return rest_warning == 2;2440}2441if (p_option.begins_with("rest_pose/") && p_option.ends_with("no_animation_chosen")) {2442return rest_warning == 3;2443}2444}2445} break;2446default: {2447}2448}24492450// TODO: If there are more than 2 or equal get_internal_option_visibility method, visibility state is broken.2451for (int i = 0; i < post_importer_plugins.size(); i++) {2452Variant ret = post_importer_plugins.write[i]->get_internal_option_visibility(EditorScenePostImportPlugin::InternalImportCategory(p_category), _scene_import_type, p_option, p_options);2453if (ret.get_type() == Variant::BOOL) {2454return ret;2455}2456}24572458return true;2459}24602461bool ResourceImporterScene::get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const {2462switch (p_category) {2463case INTERNAL_IMPORT_CATEGORY_NODE: {2464} break;2465case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {2466if (2467p_option == "generate/physics" ||2468p_option == "physics/shape_type" ||2469p_option.contains("decomposition/") ||2470p_option.contains("primitive/")) {2471return true;2472}2473} break;2474case INTERNAL_IMPORT_CATEGORY_MESH: {2475} break;2476case INTERNAL_IMPORT_CATEGORY_MATERIAL: {2477} break;2478case INTERNAL_IMPORT_CATEGORY_ANIMATION: {2479} break;2480case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {2481} break;2482case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {2483} break;2484default: {2485}2486}24872488for (int i = 0; i < post_importer_plugins.size(); i++) {2489Variant ret = post_importer_plugins.write[i]->get_internal_option_update_view_required(EditorScenePostImportPlugin::InternalImportCategory(p_category), p_option, p_options);2490if (ret.get_type() == Variant::BOOL) {2491return ret;2492}2493}24942495return false;2496}24972498void ResourceImporterScene::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {2499r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));2500r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_name"), ""));2501r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "nodes/root_script", PROPERTY_HINT_RESOURCE_TYPE, "Script"), Variant()));25022503List<String> script_extensions;2504ResourceLoader::get_recognized_extensions_for_type("Script", &script_extensions);25052506String script_ext_hint;25072508for (const String &E : script_extensions) {2509if (!script_ext_hint.is_empty()) {2510script_ext_hint += ",";2511}2512script_ext_hint += "*." + E;2513}2514bool trimming_defaults_on = p_path.get_extension().to_lower() == "fbx";25152516r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true));2517r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0));2518r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/import_as_skeleton_bones"), false));2519r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/use_name_suffixes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));2520r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/use_node_type_suffixes"), true));2521r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));2522r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true));2523r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));2524r_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));2525r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2));2526r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/force_disable_compression"), false));2527r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));2528r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));2529r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));2530r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), trimming_defaults_on));2531r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true));2532r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import_rest_as_RESET"), false));2533r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));2534r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/extract", PROPERTY_HINT_ENUM, "Keep Internal,Extract Once,Extract and Overwrite"), 0));2535r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/extract_format", PROPERTY_HINT_ENUM, "Text (*.tres),Binary (*.res),Material (*.material)"), 0));2536r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "materials/extract_path", PROPERTY_HINT_DIR, ""), ""));25372538r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Dictionary()));25392540for (int i = 0; i < post_importer_plugins.size(); i++) {2541post_importer_plugins.write[i]->get_import_options(p_path, r_options);2542}25432544for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {2545importer_elem->get_import_options(p_path, r_options);2546}2547}25482549void ResourceImporterScene::handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {2550for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {2551importer_elem->handle_compatibility_options(p_import_params);2552}2553}25542555void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner) {2556if (p_node != p_new_owner && p_node->get_owner() == p_scene) {2557p_node->set_owner(p_new_owner);2558}25592560for (int i = 0; i < p_node->get_child_count(); i++) {2561Node *n = p_node->get_child(i);2562_replace_owner(n, p_scene, p_new_owner);2563}2564}25652566Array ResourceImporterScene::_get_skinned_pose_transforms(ImporterMeshInstance3D *p_src_mesh_node) {2567Array skin_pose_transform_array;25682569const Ref<Skin> skin = p_src_mesh_node->get_skin();2570if (skin.is_valid()) {2571NodePath skeleton_path = p_src_mesh_node->get_skeleton_path();2572const Node *node = p_src_mesh_node->get_node_or_null(skeleton_path);2573const Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);2574if (skeleton) {2575int bind_count = skin->get_bind_count();25762577for (int i = 0; i < bind_count; i++) {2578Transform3D bind_pose = skin->get_bind_pose(i);2579String bind_name = skin->get_bind_name(i);25802581int bone_idx = bind_name.is_empty() ? skin->get_bind_bone(i) : skeleton->find_bone(bind_name);2582ERR_FAIL_COND_V(bone_idx >= skeleton->get_bone_count(), Array());25832584Transform3D bp_global_rest;2585if (bone_idx >= 0) {2586bp_global_rest = skeleton->get_bone_global_pose(bone_idx);2587} else {2588bp_global_rest = skeleton->get_bone_global_pose(i);2589}25902591skin_pose_transform_array.push_back(bp_global_rest * bind_pose);2592}2593}2594}25952596return skin_pose_transform_array;2597}25982599Node *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) {2600ImporterMeshInstance3D *src_mesh_node = Object::cast_to<ImporterMeshInstance3D>(p_node);2601if (src_mesh_node) {2602//is mesh2603MeshInstance3D *mesh_node = memnew(MeshInstance3D);2604mesh_node->set_name(src_mesh_node->get_name());2605mesh_node->set_transform(src_mesh_node->get_transform());2606mesh_node->set_skin(src_mesh_node->get_skin());2607mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());2608mesh_node->merge_meta_from(src_mesh_node);26092610Ref<ImporterMesh> importer_mesh = src_mesh_node->get_mesh();2611if (importer_mesh.is_valid()) {2612Ref<ArrayMesh> mesh;2613if (!importer_mesh->has_mesh()) {2614//do mesh processing26152616bool generate_lods = p_generate_lods;2617float merge_angle = 20.0f;2618bool create_shadow_meshes = p_create_shadow_meshes;2619bool bake_lightmaps = p_light_bake_mode == LIGHT_BAKE_STATIC_LIGHTMAPS;2620String save_to_file;26212622String mesh_id = importer_mesh->get_meta("import_id", importer_mesh->get_name());26232624if (!mesh_id.is_empty() && p_mesh_data.has(mesh_id)) {2625Dictionary mesh_settings = p_mesh_data[mesh_id];2626{2627//fill node settings for this node with default values2628List<ImportOption> iopts;2629get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MESH, &iopts);2630for (const ImportOption &E : iopts) {2631if (!mesh_settings.has(E.option.name)) {2632mesh_settings[E.option.name] = E.default_value;2633}2634}2635}26362637if (mesh_settings.has("generate/shadow_meshes")) {2638int shadow_meshes = mesh_settings["generate/shadow_meshes"];2639if (shadow_meshes == MESH_OVERRIDE_ENABLE) {2640create_shadow_meshes = true;2641} else if (shadow_meshes == MESH_OVERRIDE_DISABLE) {2642create_shadow_meshes = false;2643}2644}26452646if (mesh_settings.has("generate/lightmap_uv")) {2647int lightmap_uv = mesh_settings["generate/lightmap_uv"];2648if (lightmap_uv == MESH_OVERRIDE_ENABLE) {2649bake_lightmaps = true;2650} else if (lightmap_uv == MESH_OVERRIDE_DISABLE) {2651bake_lightmaps = false;2652}2653}26542655if (mesh_settings.has("generate/lods")) {2656int lods = mesh_settings["generate/lods"];2657if (lods == MESH_OVERRIDE_ENABLE) {2658generate_lods = true;2659} else if (lods == MESH_OVERRIDE_DISABLE) {2660generate_lods = false;2661}2662}26632664if (mesh_settings.has("lods/normal_merge_angle")) {2665merge_angle = mesh_settings["lods/normal_merge_angle"];2666}26672668if (bool(mesh_settings.get("save_to_file/enabled", false))) {2669save_to_file = mesh_settings.get("save_to_file/path", String());2670if (!ResourceUID::ensure_path(save_to_file).is_resource_file()) {2671save_to_file = "";2672}2673}26742675for (int i = 0; i < post_importer_plugins.size(); i++) {2676post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH, nullptr, src_mesh_node, importer_mesh, mesh_settings);2677}2678}26792680if (bake_lightmaps) {2681Transform3D xf;2682Node3D *n = src_mesh_node;2683while (n) {2684xf = n->get_transform() * xf;2685n = n->get_parent_node_3d();2686}26872688Vector<uint8_t> lightmap_cache;2689importer_mesh->lightmap_unwrap_cached(xf, p_lightmap_texel_size, p_src_lightmap_cache, lightmap_cache);26902691if (!lightmap_cache.is_empty()) {2692if (r_lightmap_caches.is_empty()) {2693r_lightmap_caches.push_back(lightmap_cache);2694} else {2695String new_md5 = String::md5(lightmap_cache.ptr()); // MD5 is stored at the beginning of the cache data26962697for (int i = 0; i < r_lightmap_caches.size(); i++) {2698String md5 = String::md5(r_lightmap_caches[i].ptr());2699if (new_md5 < md5) {2700r_lightmap_caches.insert(i, lightmap_cache);2701break;2702}27032704if (new_md5 == md5) {2705break;2706}2707}2708}2709}2710}27112712if (generate_lods) {2713Array skin_pose_transform_array = _get_skinned_pose_transforms(src_mesh_node);2714importer_mesh->generate_lods(merge_angle, skin_pose_transform_array);2715}27162717if (create_shadow_meshes) {2718importer_mesh->create_shadow_mesh();2719}27202721importer_mesh->optimize_indices();27222723if (!save_to_file.is_empty()) {2724String save_res_path = ResourceUID::ensure_path(save_to_file);2725Ref<Mesh> existing = ResourceCache::get_ref(save_res_path);2726if (existing.is_valid()) {2727//if somehow an existing one is useful, create2728existing->reset_state();2729}2730mesh = importer_mesh->get_mesh(existing);27312732Error err = ResourceSaver::save(mesh, save_res_path); //override2733if (err != OK) {2734WARN_PRINT(vformat("Failed to save mesh %s to '%s'.", mesh->get_name(), save_res_path));2735}2736if (err == OK && save_to_file.begins_with("uid://")) {2737// slow2738ResourceSaver::set_uid(save_res_path, ResourceUID::get_singleton()->text_to_id(save_to_file));2739}27402741mesh->set_path(save_res_path, true); //takeover existing, if needed27422743} else {2744mesh = importer_mesh->get_mesh();2745}2746} else {2747mesh = importer_mesh->get_mesh();2748}27492750if (mesh.is_valid()) {2751_copy_meta(importer_mesh.ptr(), mesh.ptr());2752mesh_node->set_mesh(mesh);2753for (int i = 0; i < mesh->get_surface_count(); i++) {2754mesh_node->set_surface_override_material(i, src_mesh_node->get_surface_material(i));2755}2756mesh->merge_meta_from(*importer_mesh);2757}2758}27592760switch (p_light_bake_mode) {2761case LIGHT_BAKE_DISABLED: {2762mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_DISABLED);2763} break;2764case LIGHT_BAKE_DYNAMIC: {2765mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_DYNAMIC);2766} break;2767case LIGHT_BAKE_STATIC:2768case LIGHT_BAKE_STATIC_LIGHTMAPS: {2769mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_STATIC);2770} break;2771}27722773mesh_node->set_layer_mask(src_mesh_node->get_layer_mask());2774mesh_node->set_cast_shadows_setting(src_mesh_node->get_cast_shadows_setting());2775mesh_node->set_visible(src_mesh_node->is_visible());2776mesh_node->set_visibility_range_begin(src_mesh_node->get_visibility_range_begin());2777mesh_node->set_visibility_range_begin_margin(src_mesh_node->get_visibility_range_begin_margin());2778mesh_node->set_visibility_range_end(src_mesh_node->get_visibility_range_end());2779mesh_node->set_visibility_range_end_margin(src_mesh_node->get_visibility_range_end_margin());2780mesh_node->set_visibility_range_fade_mode(src_mesh_node->get_visibility_range_fade_mode());27812782_copy_meta(p_node, mesh_node);27832784p_node->replace_by(mesh_node);2785p_node->set_owner(nullptr);2786memdelete(p_node);2787p_node = mesh_node;2788}27892790for (int i = 0; i < p_node->get_child_count(); i++) {2791_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);2792}27932794return p_node;2795}27962797void ResourceImporterScene::_add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes) {2798for (const Ref<Shape3D> &E : p_shapes) {2799CollisionShape3D *cshape = memnew(CollisionShape3D);2800cshape->set_shape(E);2801p_node->add_child(cshape, true);28022803cshape->set_owner(p_node->get_owner());2804}2805}28062807void ResourceImporterScene::_copy_meta(Object *p_src_object, Object *p_dst_object) {2808List<StringName> meta_list;2809p_src_object->get_meta_list(&meta_list);2810for (const StringName &meta_key : meta_list) {2811Variant meta_value = p_src_object->get_meta(meta_key);2812p_dst_object->set_meta(meta_key, meta_value);2813}2814}28152816void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions) {2817List<StringName> anims;2818p_player->get_animation_list(&anims);2819Node *parent = p_player->get_parent();2820ERR_FAIL_NULL(parent);2821HashMap<NodePath, uint32_t> used_tracks[TRACK_CHANNEL_MAX];2822bool tracks_to_add = false;2823static const Animation::TrackType track_types[TRACK_CHANNEL_MAX] = { Animation::TYPE_POSITION_3D, Animation::TYPE_ROTATION_3D, Animation::TYPE_SCALE_3D, Animation::TYPE_BLEND_SHAPE };2824for (const StringName &I : anims) {2825Ref<Animation> anim = p_player->get_animation(I);2826for (int i = 0; i < anim->get_track_count(); i++) {2827for (int j = 0; j < TRACK_CHANNEL_MAX; j++) {2828if (anim->track_get_type(i) != track_types[j]) {2829continue;2830}2831switch (p_track_actions[j]) {2832case ANIMATION_IMPORT_TRACKS_IF_PRESENT: {2833// Do Nothing.2834} break;2835case ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL: {2836used_tracks[j].insert(anim->track_get_path(i), 0);2837tracks_to_add = true;2838} break;2839case ANIMATION_IMPORT_TRACKS_NEVER: {2840anim->remove_track(i);2841i--;2842} break;2843}2844}2845}2846}28472848if (!tracks_to_add) {2849return;2850}28512852uint32_t pass = 0;2853for (const StringName &I : anims) {2854Ref<Animation> anim = p_player->get_animation(I);2855for (int j = 0; j < TRACK_CHANNEL_MAX; j++) {2856if (p_track_actions[j] != ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {2857continue;2858}28592860pass++;28612862for (int i = 0; i < anim->get_track_count(); i++) {2863if (anim->track_get_type(i) != track_types[j]) {2864continue;2865}28662867NodePath path = anim->track_get_path(i);28682869ERR_CONTINUE(!used_tracks[j].has(path)); // Should never happen.28702871used_tracks[j][path] = pass;2872}28732874for (const KeyValue<NodePath, uint32_t> &J : used_tracks[j]) {2875if (J.value == pass) {2876continue;2877}28782879NodePath path = J.key;2880Node *n = parent->get_node(path);28812882if (j == TRACK_CHANNEL_BLEND_SHAPE) {2883MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(n);2884if (mi && path.get_subname_count() > 0) {2885StringName bs = path.get_subname(0);2886bool valid;2887float value = mi->get(bs, &valid);2888if (valid) {2889int track_idx = anim->add_track(track_types[j]);2890anim->track_set_path(track_idx, path);2891anim->track_set_imported(track_idx, true);2892anim->blend_shape_track_insert_key(track_idx, 0, value);2893}2894}28952896} else {2897Skeleton3D *skel = Object::cast_to<Skeleton3D>(n);2898Node3D *n3d = Object::cast_to<Node3D>(n);2899Vector3 loc;2900Quaternion rot;2901Vector3 scale;2902if (skel && path.get_subname_count() > 0) {2903StringName bone = path.get_subname(0);2904int bone_idx = skel->find_bone(bone);2905if (bone_idx == -1) {2906continue;2907}2908// Note that this is using get_bone_pose to update the bone pose cache.2909_ALLOW_DISCARD_ skel->get_bone_pose(bone_idx);2910loc = skel->get_bone_pose_position(bone_idx);2911rot = skel->get_bone_pose_rotation(bone_idx);2912scale = skel->get_bone_pose_scale(bone_idx);2913} else if (n3d) {2914loc = n3d->get_position();2915rot = n3d->get_transform().basis.get_rotation_quaternion();2916scale = n3d->get_scale();2917} else {2918continue;2919}29202921// Ensure insertion keeps tracks together and ordered by type (loc/rot/scale)2922int insert_at_pos = -1;2923for (int k = 0; k < anim->get_track_count(); k++) {2924NodePath tpath = anim->track_get_path(k);29252926if (path == tpath) {2927Animation::TrackType ttype = anim->track_get_type(k);2928if (insert_at_pos == -1) {2929// First insert, determine whether replacing or kicking back2930if (track_types[j] < ttype) {2931insert_at_pos = k;2932break; // No point in continuing.2933} else {2934insert_at_pos = k + 1;2935}2936} else if (ttype < track_types[j]) {2937// Kick back.2938insert_at_pos = k + 1;2939}2940} else if (insert_at_pos >= 0) {2941break;2942}2943}2944int track_idx = anim->add_track(track_types[j], insert_at_pos);29452946anim->track_set_path(track_idx, path);2947anim->track_set_imported(track_idx, true);2948switch (j) {2949case TRACK_CHANNEL_POSITION: {2950anim->position_track_insert_key(track_idx, 0, loc);2951} break;2952case TRACK_CHANNEL_ROTATION: {2953anim->rotation_track_insert_key(track_idx, 0, rot);2954} break;2955case TRACK_CHANNEL_SCALE: {2956anim->scale_track_insert_key(track_idx, 0, scale);2957} break;2958default: {2959}2960}2961}2962}2963}2964}2965}29662967Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashMap<StringName, Variant> &p_options) {2968Ref<EditorSceneFormatImporter> importer;2969String ext = p_source_file.get_extension().to_lower();29702971// TRANSLATORS: This is an editor progress label.2972EditorProgress progress("pre-import", TTR("Pre-Import Scene"), 0);2973progress.step(TTR("Importing Scene..."), 0);29742975for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {2976List<String> extensions;2977importer_elem->get_extensions(&extensions);29782979for (const String &F : extensions) {2980if (F.to_lower() == ext) {2981importer = importer_elem;2982break;2983}2984}29852986if (importer.is_valid()) {2987break;2988}2989}29902991ERR_FAIL_COND_V(importer.is_null(), nullptr);2992ERR_FAIL_COND_V(p_options.is_empty(), nullptr);29932994Error err = OK;29952996Node *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);2997if (!scene || err != OK) {2998return nullptr;2999}30003001_pre_fix_global(scene, p_options);30023003HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;3004List<Pair<NodePath, Node *>> node_renames;3005_pre_fix_node(scene, scene, collision_map, nullptr, node_renames, p_options);30063007return scene;3008}30093010static 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) {3011const String &raw_save_path = p_settings[p_path_key];3012String save_path = ResourceUID::ensure_path(raw_save_path);3013if (raw_save_path.begins_with("uid://")) {3014if (save_path.is_empty() || !DirAccess::exists(save_path.get_base_dir())) {3015if (p_settings.has(p_fallback_path_key)) {3016String fallback_save_path = p_settings[p_fallback_path_key];3017if (!fallback_save_path.is_empty() && DirAccess::exists(fallback_save_path.get_base_dir())) {3018save_path = fallback_save_path;3019ResourceUID::get_singleton()->add_id(ResourceUID::get_singleton()->text_to_id(raw_save_path), save_path);3020}3021}3022} else {3023p_settings[p_fallback_path_key] = save_path;3024}3025}3026ERR_FAIL_COND_V(!save_path.is_empty() && !DirAccess::exists(save_path.get_base_dir()), ERR_FILE_BAD_PATH);3027if (!save_path.is_empty() && !raw_save_path.begins_with("uid://")) {3028const ResourceUID::ID id = ResourceLoader::get_resource_uid(save_path);3029if (id != ResourceUID::INVALID_ID) {3030p_settings[p_path_key] = ResourceUID::get_singleton()->id_to_text(id);3031} else {3032ResourceUID::ID save_id = hash64_murmur3_64(p_hash_str.hash64(), p_source_id) & 0x7FFFFFFFFFFFFFFF;3033if (ResourceUID::get_singleton()->has_id(save_id)) {3034if (save_path != ResourceUID::get_singleton()->get_id_path(save_id)) {3035// The user has specified a path which does not match the default UID.3036save_id = ResourceUID::get_singleton()->create_id_for_path(save_path);3037}3038}3039p_settings[p_path_key] = ResourceUID::get_singleton()->id_to_text(save_id);3040ResourceUID::get_singleton()->add_id(save_id, save_path);3041}3042p_settings[p_fallback_path_key] = save_path;3043}3044return OK;3045}30463047Error ResourceImporterScene::_check_resource_save_paths(ResourceUID::ID p_source_id, const String &p_hash_suffix, const Dictionary &p_data) {3048for (const KeyValue<Variant, Variant> &kv : p_data) {3049Dictionary settings = kv.value;30503051if (bool(settings.get("save_to_file/enabled", false)) && settings.has("save_to_file/path")) {3052String to_hash = kv.key.operator String() + p_hash_suffix;3053Error ret = convert_path_to_uid(p_source_id, to_hash, settings, "save_to_file/path", "save_to_file/fallback_path");3054ERR_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")));3055}30563057if (settings.has("slices/amount")) {3058int slice_count = settings["slices/amount"];3059for (int si = 0; si < slice_count; si++) {3060if (bool(settings.get("slice_" + itos(si + 1) + "/save_to_file/enabled", false)) &&3061settings.has("slice_" + itos(si + 1) + "/save_to_file/path")) {3062String to_hash = kv.key.operator String() + p_hash_suffix + itos(si + 1);3063Error ret = convert_path_to_uid(p_source_id, to_hash, settings,3064"slice_" + itos(si + 1) + "/save_to_file/path",3065"slice_" + itos(si + 1) + "/save_to_file/fallback_path");3066ERR_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")));3067}3068}3069}3070}30713072return OK;3073}30743075Error 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) {3076const String &src_path = p_source_file;30773078Ref<EditorSceneFormatImporter> importer;3079String ext = src_path.get_extension().to_lower();30803081EditorProgress progress("import", TTR("Import Scene"), 104);3082progress.step(TTR("Importing Scene..."), 0);30833084for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {3085List<String> extensions;3086importer_elem->get_extensions(&extensions);30873088for (const String &F : extensions) {3089if (F.to_lower() == ext) {3090importer = importer_elem;3091break;3092}3093}30943095if (importer.is_valid()) {3096break;3097}3098}30993100ERR_FAIL_COND_V(importer.is_null(), ERR_FILE_UNRECOGNIZED);3101ERR_FAIL_COND_V(p_options.is_empty(), ERR_BUG);31023103int import_flags = 0;31043105if (_scene_import_type == "AnimationLibrary") {3106import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION;3107import_flags |= EditorSceneFormatImporter::IMPORT_DISCARD_MESHES_AND_MATERIALS;3108} else if (bool(p_options["animation/import"])) {3109import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION;3110}31113112if (bool(p_options["skins/use_named_skins"])) {3113import_flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;3114}31153116bool ensure_tangents = p_options["meshes/ensure_tangents"];3117if (ensure_tangents) {3118import_flags |= EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS;3119}31203121bool force_disable_compression = p_options["meshes/force_disable_compression"];3122if (force_disable_compression) {3123import_flags |= EditorSceneFormatImporter::IMPORT_FORCE_DISABLE_MESH_COMPRESSION;3124}31253126Dictionary subresources = p_options["_subresources"];31273128Error err = OK;31293130// Check whether any of the meshes or animations have nonexistent save paths3131// and if they do, fail the import immediately.3132if (subresources.has("meshes")) {3133err = _check_resource_save_paths(p_source_id, "m", subresources["meshes"]);3134if (err != OK) {3135return err;3136}3137}31383139if (subresources.has("animations")) {3140err = _check_resource_save_paths(p_source_id, "a", subresources["animations"]);3141if (err != OK) {3142return err;3143}3144}31453146List<String> missing_deps; // for now, not much will be done with this3147Node *scene = importer->import_scene(src_path, import_flags, p_options, &missing_deps, &err);3148if (!scene || err != OK) {3149return err;3150}31513152bool apply_root = true;3153if (p_options.has("nodes/apply_root_scale")) {3154apply_root = p_options["nodes/apply_root_scale"];3155}3156real_t root_scale = 1;3157if (p_options.has("nodes/root_scale")) {3158root_scale = p_options["nodes/root_scale"];3159}3160if (Object::cast_to<Node3D>(scene)) {3161Node3D *scene_3d = Object::cast_to<Node3D>(scene);3162Vector3 scale = Vector3(root_scale, root_scale, root_scale);3163if (apply_root) {3164_apply_permanent_scale_to_descendants(scene, scale);3165} else {3166scene_3d->scale(scale);3167}3168}31693170_pre_fix_global(scene, p_options);31713172HashSet<Ref<ImporterMesh>> scanned_meshes;3173HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;3174Pair<PackedVector3Array, PackedInt32Array> occluder_arrays;3175List<Pair<NodePath, Node *>> node_renames;31763177_pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames, p_options);31783179for (int i = 0; i < post_importer_plugins.size(); i++) {3180post_importer_plugins.write[i]->pre_process(scene, p_options);3181}31823183// data in _subresources may be modified by pre_process(), so wait until now to check.3184Dictionary node_data;3185if (subresources.has("nodes")) {3186node_data = subresources["nodes"];3187}31883189Dictionary material_data;3190if (subresources.has("materials")) {3191material_data = subresources["materials"];3192}31933194Dictionary animation_data;3195if (subresources.has("animations")) {3196animation_data = subresources["animations"];3197}31983199Dictionary mesh_data;3200if (subresources.has("meshes")) {3201mesh_data = subresources["meshes"];3202}32033204float fps = 30;3205if (p_options.has(SNAME("animation/fps"))) {3206fps = (float)p_options[SNAME("animation/fps")];3207}3208bool remove_immutable_tracks = p_options.has("animation/remove_immutable_tracks") ? (bool)p_options["animation/remove_immutable_tracks"] : true;3209_pre_fix_animations(scene, scene, node_data, animation_data, fps);3210_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);3211_post_fix_animations(scene, scene, node_data, animation_data, fps, remove_immutable_tracks);32123213String root_type = p_options["nodes/root_type"];3214Ref<Script> root_script = p_options["nodes/root_script"];3215scene = _replace_node_with_type_and_script(scene, root_type, root_script);32163217String root_name = p_options["nodes/root_name"];3218if (!root_name.is_empty() && root_name != "Scene Root") {3219// TODO: Remove `&& root_name != "Scene Root"` for Godot 5.0.3220// For backwards compatibility with existing .import files,3221// treat "Scene Root" as having no root name override.3222scene->set_name(root_name);3223} else if (String(scene->get_name()).is_empty()) {3224scene->set_name(p_save_path.get_file().get_basename());3225}32263227if (!occluder_arrays.first.is_empty() && !occluder_arrays.second.is_empty()) {3228Ref<ArrayOccluder3D> occ = memnew(ArrayOccluder3D);3229occ->set_arrays(occluder_arrays.first, occluder_arrays.second);3230OccluderInstance3D *occluder_instance = memnew(OccluderInstance3D);3231occluder_instance->set_occluder(occ);3232scene->add_child(occluder_instance, true);3233occluder_instance->set_owner(scene);3234}32353236bool gen_lods = bool(p_options["meshes/generate_lods"]);3237bool create_shadow_meshes = bool(p_options["meshes/create_shadow_meshes"]);3238int light_bake_mode = p_options["meshes/light_baking"];3239float texel_size = p_options["meshes/lightmap_texel_size"];3240float lightmap_texel_size = MAX(0.001, texel_size);32413242Vector<uint8_t> src_lightmap_cache;3243Vector<Vector<uint8_t>> mesh_lightmap_caches;32443245{3246src_lightmap_cache = FileAccess::get_file_as_bytes(p_source_file + ".unwrap_cache", &err);3247if (err != OK) {3248src_lightmap_cache.clear();3249}3250}32513252scene = _generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, mesh_lightmap_caches);32533254if (mesh_lightmap_caches.size()) {3255Ref<FileAccess> f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE);3256if (f.is_valid()) {3257f->store_32(mesh_lightmap_caches.size());3258for (int i = 0; i < mesh_lightmap_caches.size(); i++) {3259String md5 = String::md5(mesh_lightmap_caches[i].ptr());3260f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size());3261}3262}3263}3264err = OK;32653266progress.step(TTR("Running Custom Script..."), 2);32673268String post_import_script_path = p_options["import_script/path"];32693270Ref<EditorScenePostImport> post_import_script;32713272if (!post_import_script_path.is_empty()) {3273if (post_import_script_path.is_relative_path()) {3274post_import_script_path = p_source_file.get_base_dir().path_join(post_import_script_path);3275}3276Ref<Script> scr = ResourceLoader::load(post_import_script_path);3277if (scr.is_null()) {3278EditorNode::add_io_error(TTR("Couldn't load post-import script:") + " " + post_import_script_path);3279} else if (scr->get_instance_base_type() != "EditorScenePostImport") {3280EditorNode::add_io_error(TTR("Script is not a subtype of EditorScenePostImport:") + " " + post_import_script_path);3281} else {3282post_import_script.instantiate();3283post_import_script->set_script(scr);3284if (!post_import_script->get_script_instance()) {3285EditorNode::add_io_error(TTR("Invalid/broken script for post-import (check console):") + " " + post_import_script_path);3286post_import_script.unref();3287return ERR_CANT_CREATE;3288}3289}3290}32913292// Apply RESET animation before serializing.3293if (_scene_import_type == "PackedScene") {3294int scene_child_count = scene->get_child_count();3295for (int i = 0; i < scene_child_count; i++) {3296AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(scene->get_child(i));3297if (ap) {3298if (ap->can_apply_reset()) {3299ap->apply_reset();3300}3301}3302}3303}33043305if (post_import_script.is_valid()) {3306post_import_script->init(p_source_file);3307scene = post_import_script->post_import(scene);3308if (!scene) {3309EditorNode::add_io_error(3310TTR("Error running post-import script:") + " " + post_import_script_path + "\n" +3311TTR("Did you return a Node-derived object in the `_post_import()` method?"));3312return err;3313}3314}33153316for (int i = 0; i < post_importer_plugins.size(); i++) {3317post_importer_plugins.write[i]->post_process(scene, p_options);3318}33193320progress.step(TTR("Saving..."), 104);33213322int flags = 0;3323if (EditorSettings::get_singleton() && EDITOR_GET("filesystem/on_save/compress_binary_resources")) {3324flags |= ResourceSaver::FLAG_COMPRESS;3325}33263327if (_scene_import_type == "AnimationLibrary") {3328Ref<AnimationLibrary> library;3329for (int i = 0; i < scene->get_child_count(); i++) {3330AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(scene->get_child(i));3331if (ap) {3332List<StringName> libs;3333ap->get_animation_library_list(&libs);3334if (libs.size()) {3335library = ap->get_animation_library(libs.front()->get());3336break;3337}3338}3339}33403341if (library.is_null()) {3342library.instantiate(); // Will be empty3343}33443345print_verbose("Saving animation to: " + p_save_path + ".res");3346err = ResourceSaver::save(library, p_save_path + ".res", flags); //do not take over, let the changed files reload themselves3347ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save animation to file '" + p_save_path + ".res'.");3348} else if (_scene_import_type == "PackedScene") {3349Ref<PackedScene> packer = memnew(PackedScene);3350packer->pack(scene);3351print_verbose("Saving scene to: " + p_save_path + ".scn");3352err = ResourceSaver::save(packer, p_save_path + ".scn", flags); //do not take over, let the changed files reload themselves3353ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save scene to file '" + p_save_path + ".scn'.");3354EditorInterface::get_singleton()->make_scene_preview(p_source_file, scene, 1024);3355} else {3356ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown scene import type: " + _scene_import_type);3357}33583359memdelete(scene);33603361//this is not the time to reimport, wait until import process is done, import file is saved, etc.3362//EditorNode::get_singleton()->reload_scene(p_source_file);33633364return OK;3365}33663367ResourceImporterScene *ResourceImporterScene::scene_singleton = nullptr;3368ResourceImporterScene *ResourceImporterScene::animation_singleton = nullptr;33693370Vector<Ref<EditorSceneFormatImporter>> ResourceImporterScene::scene_importers;3371Vector<Ref<EditorScenePostImportPlugin>> ResourceImporterScene::post_importer_plugins;33723373bool ResourceImporterScene::has_advanced_options() const {3374return true;3375}33763377void ResourceImporterScene::show_advanced_options(const String &p_path) {3378SceneImportSettingsDialog::get_singleton()->open_settings(p_path, _scene_import_type);3379}33803381ResourceImporterScene::ResourceImporterScene(const String &p_scene_import_type, bool p_singleton) {3382// This should only be set through the EditorNode.3383if (p_singleton) {3384if (p_scene_import_type == "AnimationLibrary") {3385animation_singleton = this;3386} else if (p_scene_import_type == "PackedScene") {3387scene_singleton = this;3388}3389}33903391_scene_import_type = p_scene_import_type;3392}33933394ResourceImporterScene::~ResourceImporterScene() {3395if (animation_singleton == this) {3396animation_singleton = nullptr;3397}3398if (scene_singleton == this) {3399scene_singleton = nullptr;3400}3401}34023403void ResourceImporterScene::add_scene_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority) {3404ERR_FAIL_COND(p_importer.is_null());3405if (p_first_priority) {3406scene_importers.insert(0, p_importer);3407} else {3408scene_importers.push_back(p_importer);3409}3410}34113412void ResourceImporterScene::remove_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) {3413post_importer_plugins.erase(p_plugin);3414}34153416void ResourceImporterScene::add_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority) {3417ERR_FAIL_COND(p_plugin.is_null());3418if (p_first_priority) {3419post_importer_plugins.insert(0, p_plugin);3420} else {3421post_importer_plugins.push_back(p_plugin);3422}3423}34243425void ResourceImporterScene::remove_scene_importer(Ref<EditorSceneFormatImporter> p_importer) {3426scene_importers.erase(p_importer);3427}34283429void ResourceImporterScene::clean_up_importer_plugins() {3430scene_importers.clear();3431post_importer_plugins.clear();3432}34333434void ResourceImporterScene::get_scene_importer_extensions(List<String> *p_extensions) {3435for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {3436importer_elem->get_extensions(p_extensions);3437}3438}34393440///////////////////////////////////////34413442void EditorSceneFormatImporterESCN::get_extensions(List<String> *r_extensions) const {3443r_extensions->push_back("escn");3444}34453446Node *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) {3447Error error;3448Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error);3449ERR_FAIL_COND_V_MSG(ps.is_null(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'.");3450Node *scene = ps->instantiate();3451TypedArray<Node> nodes = scene->find_children("*", "MeshInstance3D");3452for (int32_t node_i = 0; node_i < nodes.size(); node_i++) {3453MeshInstance3D *mesh_3d = cast_to<MeshInstance3D>(nodes[node_i]);3454Ref<ImporterMesh> mesh;3455mesh.instantiate();3456// Ignore the aabb, it will be recomputed.3457ImporterMeshInstance3D *importer_mesh_3d = memnew(ImporterMeshInstance3D);3458importer_mesh_3d->set_name(mesh_3d->get_name());3459importer_mesh_3d->set_transform(mesh_3d->get_relative_transform(mesh_3d->get_parent()));3460importer_mesh_3d->set_skin(mesh_3d->get_skin());3461importer_mesh_3d->set_skeleton_path(mesh_3d->get_skeleton_path());3462Ref<ArrayMesh> array_mesh_3d_mesh = mesh_3d->get_mesh();3463if (array_mesh_3d_mesh.is_valid()) {3464// For the MeshInstance3D nodes, we need to convert the ArrayMesh to an ImporterMesh specially.3465mesh->set_name(array_mesh_3d_mesh->get_name());3466for (int32_t blend_i = 0; blend_i < array_mesh_3d_mesh->get_blend_shape_count(); blend_i++) {3467mesh->add_blend_shape(array_mesh_3d_mesh->get_blend_shape_name(blend_i));3468}3469for (int32_t surface_i = 0; surface_i < array_mesh_3d_mesh->get_surface_count(); surface_i++) {3470mesh->add_surface(array_mesh_3d_mesh->surface_get_primitive_type(surface_i),3471array_mesh_3d_mesh->surface_get_arrays(surface_i),3472array_mesh_3d_mesh->surface_get_blend_shape_arrays(surface_i),3473array_mesh_3d_mesh->surface_get_lods(surface_i),3474array_mesh_3d_mesh->surface_get_material(surface_i),3475array_mesh_3d_mesh->surface_get_name(surface_i),3476array_mesh_3d_mesh->surface_get_format(surface_i));3477}3478mesh->set_blend_shape_mode(array_mesh_3d_mesh->get_blend_shape_mode());3479importer_mesh_3d->set_mesh(mesh);3480mesh_3d->replace_by(importer_mesh_3d);3481continue;3482}3483Ref<Mesh> mesh_3d_mesh = mesh_3d->get_mesh();3484if (mesh_3d_mesh.is_valid()) {3485// For the MeshInstance3D nodes, we need to convert the Mesh to an ImporterMesh specially.3486mesh->set_name(mesh_3d_mesh->get_name());3487for (int32_t surface_i = 0; surface_i < mesh_3d_mesh->get_surface_count(); surface_i++) {3488mesh->add_surface(mesh_3d_mesh->surface_get_primitive_type(surface_i),3489mesh_3d_mesh->surface_get_arrays(surface_i),3490Array(),3491mesh_3d_mesh->surface_get_lods(surface_i),3492mesh_3d_mesh->surface_get_material(surface_i),3493mesh_3d_mesh->surface_get_material(surface_i).is_valid() ? mesh_3d_mesh->surface_get_material(surface_i)->get_name() : String(),3494mesh_3d_mesh->surface_get_format(surface_i));3495}3496importer_mesh_3d->set_mesh(mesh);3497mesh_3d->replace_by(importer_mesh_3d);3498continue;3499}3500}35013502ERR_FAIL_NULL_V(scene, nullptr);35033504return scene;3505}350635073508