Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/import/3d/resource_importer_scene.cpp
21025 views
1
/**************************************************************************/
2
/* resource_importer_scene.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "resource_importer_scene.h"
32
33
#include "core/error/error_macros.h"
34
#include "core/io/dir_access.h"
35
#include "core/io/resource_saver.h"
36
#include "core/object/script_language.h"
37
#include "editor/editor_interface.h"
38
#include "editor/editor_node.h"
39
#include "editor/import/3d/scene_import_settings.h"
40
#include "editor/settings/editor_settings.h"
41
#include "scene/3d/importer_mesh_instance_3d.h"
42
#include "scene/3d/mesh_instance_3d.h"
43
#include "scene/3d/navigation/navigation_region_3d.h"
44
#include "scene/3d/occluder_instance_3d.h"
45
#include "scene/3d/physics/area_3d.h"
46
#include "scene/3d/physics/collision_shape_3d.h"
47
#include "scene/3d/physics/static_body_3d.h"
48
#include "scene/3d/physics/vehicle_body_3d.h"
49
#include "scene/animation/animation_player.h"
50
#include "scene/resources/3d/box_shape_3d.h"
51
#include "scene/resources/3d/importer_mesh.h"
52
#include "scene/resources/3d/separation_ray_shape_3d.h"
53
#include "scene/resources/3d/sphere_shape_3d.h"
54
#include "scene/resources/3d/world_boundary_shape_3d.h"
55
#include "scene/resources/animation.h"
56
#include "scene/resources/bone_map.h"
57
#include "scene/resources/packed_scene.h"
58
#include "scene/resources/resource_format_text.h"
59
60
void EditorSceneFormatImporter::get_extensions(List<String> *r_extensions) const {
61
Vector<String> arr;
62
GDVIRTUAL_CALL(_get_extensions, arr);
63
for (int i = 0; i < arr.size(); i++) {
64
r_extensions->push_back(arr[i]);
65
}
66
}
67
68
Node *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) {
69
Dictionary options_dict;
70
for (const KeyValue<StringName, Variant> &elem : p_options) {
71
options_dict[elem.key] = elem.value;
72
}
73
Object *ret = nullptr;
74
GDVIRTUAL_CALL(_import_scene, p_path, p_flags, options_dict, ret);
75
return Object::cast_to<Node>(ret);
76
}
77
78
void EditorSceneFormatImporter::add_import_option(const String &p_name, const Variant &p_default_value) {
79
ERR_FAIL_NULL_MSG(current_option_list, "add_import_option() can only be called from get_import_options().");
80
add_import_option_advanced(p_default_value.get_type(), p_name, p_default_value);
81
}
82
83
void 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) {
84
ERR_FAIL_NULL_MSG(current_option_list, "add_import_option_advanced() can only be called from get_import_options().");
85
current_option_list->push_back(ResourceImporter::ImportOption(PropertyInfo(p_type, p_name, p_hint, p_hint_string, p_usage_flags), p_default_value));
86
}
87
88
void EditorSceneFormatImporter::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) {
89
current_option_list = r_options;
90
GDVIRTUAL_CALL(_get_import_options, p_path);
91
current_option_list = nullptr;
92
}
93
94
Variant EditorSceneFormatImporter::get_option_visibility(const String &p_path, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) {
95
Variant ret;
96
// For compatibility with the old API, pass the import type as a boolean.
97
GDVIRTUAL_CALL(_get_option_visibility, p_path, p_scene_import_type == "AnimationLibrary", p_option, ret);
98
return ret;
99
}
100
101
void EditorSceneFormatImporter::_bind_methods() {
102
ClassDB::bind_method(D_METHOD("add_import_option", "name", "value"), &EditorSceneFormatImporter::add_import_option);
103
ClassDB::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));
104
105
GDVIRTUAL_BIND(_get_extensions);
106
GDVIRTUAL_BIND(_import_scene, "path", "flags", "options");
107
GDVIRTUAL_BIND(_get_import_options, "path");
108
GDVIRTUAL_BIND(_get_option_visibility, "path", "for_animation", "option");
109
110
BIND_CONSTANT(IMPORT_SCENE);
111
BIND_CONSTANT(IMPORT_ANIMATION);
112
BIND_CONSTANT(IMPORT_FAIL_ON_MISSING_DEPENDENCIES);
113
BIND_CONSTANT(IMPORT_GENERATE_TANGENT_ARRAYS);
114
BIND_CONSTANT(IMPORT_USE_NAMED_SKIN_BINDS);
115
BIND_CONSTANT(IMPORT_DISCARD_MESHES_AND_MATERIALS);
116
BIND_CONSTANT(IMPORT_FORCE_DISABLE_MESH_COMPRESSION);
117
}
118
119
/////////////////////////////////
120
void EditorScenePostImport::_bind_methods() {
121
GDVIRTUAL_BIND(_post_import, "scene")
122
ClassDB::bind_method(D_METHOD("get_source_file"), &EditorScenePostImport::get_source_file);
123
}
124
125
Node *EditorScenePostImport::post_import(Node *p_scene) {
126
Object *ret;
127
if (GDVIRTUAL_CALL(_post_import, p_scene, ret)) {
128
return Object::cast_to<Node>(ret);
129
}
130
131
return p_scene;
132
}
133
134
String EditorScenePostImport::get_source_file() const {
135
return source_file;
136
}
137
138
void EditorScenePostImport::init(const String &p_source_file) {
139
source_file = p_source_file;
140
}
141
142
///////////////////////////////////////////////////////
143
144
Variant EditorScenePostImportPlugin::get_option_value(const StringName &p_name) const {
145
ERR_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.");
146
ERR_FAIL_COND_V_MSG(current_options && !current_options->has(p_name), Variant(), "get_option_value called with unexisting option argument: " + String(p_name));
147
ERR_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));
148
if (current_options && current_options->has(p_name)) {
149
return (*current_options)[p_name];
150
}
151
if (current_options_dict && current_options_dict->has(p_name)) {
152
return (*current_options_dict)[p_name];
153
}
154
return Variant();
155
}
156
void EditorScenePostImportPlugin::add_import_option(const String &p_name, const Variant &p_default_value) {
157
ERR_FAIL_NULL_MSG(current_option_list, "add_import_option() can only be called from get_import_options().");
158
add_import_option_advanced(p_default_value.get_type(), p_name, p_default_value);
159
}
160
void 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) {
161
ERR_FAIL_NULL_MSG(current_option_list, "add_import_option_advanced() can only be called from get_import_options().");
162
current_option_list->push_back(ResourceImporter::ImportOption(PropertyInfo(p_type, p_name, p_hint, p_hint_string, p_usage_flags), p_default_value));
163
}
164
165
void EditorScenePostImportPlugin::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {
166
current_option_list = r_options;
167
GDVIRTUAL_CALL(_get_internal_import_options, p_category);
168
current_option_list = nullptr;
169
}
170
171
Variant 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 {
172
current_options = &p_options;
173
Variant ret;
174
// For compatibility with the old API, pass the import type as a boolean.
175
GDVIRTUAL_CALL(_get_internal_option_visibility, p_category, p_scene_import_type == "AnimationLibrary", p_option, ret);
176
current_options = nullptr;
177
return ret;
178
}
179
180
Variant EditorScenePostImportPlugin::get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
181
current_options = &p_options;
182
Variant ret;
183
GDVIRTUAL_CALL(_get_internal_option_update_view_required, p_category, p_option, ret);
184
current_options = nullptr;
185
return ret;
186
}
187
188
void EditorScenePostImportPlugin::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) {
189
current_options_dict = &p_options;
190
GDVIRTUAL_CALL(_internal_process, p_category, p_base_scene, p_node, p_resource);
191
current_options_dict = nullptr;
192
}
193
194
void EditorScenePostImportPlugin::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) {
195
current_option_list = r_options;
196
GDVIRTUAL_CALL(_get_import_options, p_path);
197
current_option_list = nullptr;
198
}
199
Variant 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 {
200
current_options = &p_options;
201
Variant ret;
202
GDVIRTUAL_CALL(_get_option_visibility, p_path, p_scene_import_type == "AnimationLibrary", p_option, ret);
203
current_options = nullptr;
204
return ret;
205
}
206
207
void EditorScenePostImportPlugin::pre_process(Node *p_scene, const HashMap<StringName, Variant> &p_options) {
208
current_options = &p_options;
209
GDVIRTUAL_CALL(_pre_process, p_scene);
210
current_options = nullptr;
211
}
212
void EditorScenePostImportPlugin::post_process(Node *p_scene, const HashMap<StringName, Variant> &p_options) {
213
current_options = &p_options;
214
GDVIRTUAL_CALL(_post_process, p_scene);
215
current_options = nullptr;
216
}
217
218
void EditorScenePostImportPlugin::_bind_methods() {
219
ClassDB::bind_method(D_METHOD("get_option_value", "name"), &EditorScenePostImportPlugin::get_option_value);
220
221
ClassDB::bind_method(D_METHOD("add_import_option", "name", "value"), &EditorScenePostImportPlugin::add_import_option);
222
ClassDB::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));
223
224
GDVIRTUAL_BIND(_get_internal_import_options, "category");
225
GDVIRTUAL_BIND(_get_internal_option_visibility, "category", "for_animation", "option");
226
GDVIRTUAL_BIND(_get_internal_option_update_view_required, "category", "option");
227
GDVIRTUAL_BIND(_internal_process, "category", "base_node", "node", "resource");
228
GDVIRTUAL_BIND(_get_import_options, "path");
229
GDVIRTUAL_BIND(_get_option_visibility, "path", "for_animation", "option");
230
GDVIRTUAL_BIND(_pre_process, "scene");
231
GDVIRTUAL_BIND(_post_process, "scene");
232
233
BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_NODE);
234
BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE);
235
BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MESH);
236
BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MATERIAL);
237
BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION);
238
BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE);
239
BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE);
240
BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MAX);
241
}
242
243
/////////////////////////////////////////////////////////
244
245
const String ResourceImporterScene::material_extension[3] = { ".tres", ".res", ".material" };
246
247
String ResourceImporterScene::get_importer_name() const {
248
// For compatibility with 4.2 and earlier we need to keep the "scene" and "animation_library" names.
249
// However this is arbitrary so for new import types we can use any string.
250
if (_scene_import_type == "PackedScene") {
251
return "scene";
252
} else if (_scene_import_type == "AnimationLibrary") {
253
return "animation_library";
254
}
255
return _scene_import_type;
256
}
257
258
String ResourceImporterScene::get_visible_name() const {
259
// This is displayed on the UI. Friendly names here are nice but not vital, so fall back to the type.
260
if (_scene_import_type == "PackedScene") {
261
return "Scene";
262
}
263
return _scene_import_type.capitalize();
264
}
265
266
void ResourceImporterScene::get_recognized_extensions(List<String> *p_extensions) const {
267
get_scene_importer_extensions(p_extensions);
268
}
269
270
String ResourceImporterScene::get_save_extension() const {
271
if (_scene_import_type == "PackedScene") {
272
return "scn";
273
}
274
return "res";
275
}
276
277
String ResourceImporterScene::get_resource_type() const {
278
return _scene_import_type;
279
}
280
281
int ResourceImporterScene::get_format_version() const {
282
return 1;
283
}
284
285
bool ResourceImporterScene::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
286
if (_scene_import_type == "PackedScene") {
287
if (p_option.begins_with("animation/")) {
288
if (p_option != "animation/import" && !bool(p_options["animation/import"])) {
289
return false;
290
}
291
}
292
} else if (_scene_import_type == "AnimationLibrary") {
293
if (p_option == "animation/import") { // Option ignored, animation always imported.
294
return false;
295
}
296
if (p_option == "nodes/root_type" || p_option == "nodes/root_name" || p_option.begins_with("meshes/") || p_option.begins_with("skins/")) {
297
return false; // Nothing to do here for animations.
298
}
299
}
300
301
if (p_option == "nodes/use_node_type_suffixes" && p_options.has("nodes/use_name_suffixes")) {
302
return p_options["nodes/use_name_suffixes"];
303
}
304
if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) != 2) {
305
// Only display the lightmap texel size import option when using the Static Lightmaps light baking mode.
306
return false;
307
}
308
309
for (int i = 0; i < post_importer_plugins.size(); i++) {
310
Variant ret = post_importer_plugins.write[i]->get_option_visibility(p_path, _scene_import_type, p_option, p_options);
311
if (ret.get_type() == Variant::BOOL) {
312
if (!ret) {
313
return false;
314
}
315
}
316
}
317
318
for (Ref<EditorSceneFormatImporter> importer : scene_importers) {
319
Variant ret = importer->get_option_visibility(p_path, _scene_import_type, p_option, p_options);
320
if (ret.get_type() == Variant::BOOL) {
321
if (!ret) {
322
return false;
323
}
324
}
325
}
326
327
return true;
328
}
329
330
int ResourceImporterScene::get_preset_count() const {
331
return 0;
332
}
333
334
String ResourceImporterScene::get_preset_name(int p_idx) const {
335
return String();
336
}
337
338
void ResourceImporterScene::_pre_fix_global(Node *p_scene, const HashMap<StringName, Variant> &p_options) const {
339
if (p_options.has("animation/import_rest_as_RESET") && (bool)p_options["animation/import_rest_as_RESET"]) {
340
TypedArray<Node> anim_players = p_scene->find_children("*", "AnimationPlayer");
341
if (anim_players.is_empty()) {
342
AnimationPlayer *anim_player = memnew(AnimationPlayer);
343
anim_player->set_name("AnimationPlayer");
344
p_scene->add_child(anim_player);
345
anim_player->set_owner(p_scene);
346
anim_players.append(anim_player);
347
}
348
Ref<Animation> reset_anim;
349
for (int i = 0; i < anim_players.size(); i++) {
350
AnimationPlayer *player = cast_to<AnimationPlayer>(anim_players[i]);
351
if (player->has_animation(SceneStringName(RESET))) {
352
reset_anim = player->get_animation(SceneStringName(RESET));
353
break;
354
}
355
}
356
if (reset_anim.is_null()) {
357
AnimationPlayer *anim_player = cast_to<AnimationPlayer>(anim_players[0]);
358
reset_anim.instantiate();
359
Ref<AnimationLibrary> anim_library;
360
if (anim_player->has_animation_library(StringName())) {
361
anim_library = anim_player->get_animation_library(StringName());
362
} else {
363
anim_library.instantiate();
364
anim_player->add_animation_library(StringName(), anim_library);
365
}
366
anim_library->add_animation(SceneStringName(RESET), reset_anim);
367
}
368
TypedArray<Node> skeletons = p_scene->find_children("*", "Skeleton3D");
369
for (int i = 0; i < skeletons.size(); i++) {
370
Skeleton3D *skeleton = cast_to<Skeleton3D>(skeletons[i]);
371
NodePath skeleton_path = p_scene->get_path_to(skeleton);
372
373
HashSet<NodePath> existing_pos_tracks;
374
HashSet<NodePath> existing_rot_tracks;
375
for (int trk_i = 0; trk_i < reset_anim->get_track_count(); trk_i++) {
376
NodePath np = reset_anim->track_get_path(trk_i);
377
if (reset_anim->track_get_type(trk_i) == Animation::TYPE_POSITION_3D) {
378
existing_pos_tracks.insert(np);
379
}
380
if (reset_anim->track_get_type(trk_i) == Animation::TYPE_ROTATION_3D) {
381
existing_rot_tracks.insert(np);
382
}
383
}
384
for (int bone_i = 0; bone_i < skeleton->get_bone_count(); bone_i++) {
385
NodePath bone_path(skeleton_path.get_names(), Vector<StringName>{ skeleton->get_bone_name(bone_i) }, false);
386
if (!existing_pos_tracks.has(bone_path)) {
387
int pos_t = reset_anim->add_track(Animation::TYPE_POSITION_3D);
388
reset_anim->track_set_path(pos_t, bone_path);
389
reset_anim->position_track_insert_key(pos_t, 0.0, skeleton->get_bone_rest(bone_i).origin);
390
reset_anim->track_set_imported(pos_t, true);
391
}
392
if (!existing_rot_tracks.has(bone_path)) {
393
int rot_t = reset_anim->add_track(Animation::TYPE_ROTATION_3D);
394
reset_anim->track_set_path(rot_t, bone_path);
395
reset_anim->rotation_track_insert_key(rot_t, 0.0, skeleton->get_bone_rest(bone_i).basis.get_rotation_quaternion());
396
reset_anim->track_set_imported(rot_t, true);
397
}
398
}
399
}
400
}
401
}
402
403
static bool _teststr(const String &p_what, const String &p_str) {
404
String what = p_what;
405
406
// Remove trailing spaces and numbers, some apps like blender add ".number" to duplicates
407
// (dot is replaced with _ as invalid character) so also compensate for this.
408
while (what.length() && (is_digit(what[what.length() - 1]) || what[what.length() - 1] <= 32 || what[what.length() - 1] == '_')) {
409
what = what.substr(0, what.length() - 1);
410
}
411
412
if (what.containsn("$" + p_str)) { // Blender and other stuff.
413
return true;
414
}
415
if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters
416
return true;
417
}
418
if (what.to_lower().ends_with("_" + p_str)) { //collada only supports "_" and "-" besides letters
419
return true;
420
}
421
return false;
422
}
423
424
static String _fixstr(const String &p_what, const String &p_str) {
425
String what = p_what;
426
427
// Remove trailing spaces and numbers, some apps like blender add ".number" to duplicates
428
// (dot is replaced with _ as invalid character) so also compensate for this.
429
while (what.length() && (is_digit(what[what.length() - 1]) || what[what.length() - 1] <= 32 || what[what.length() - 1] == '_')) {
430
what = what.substr(0, what.length() - 1);
431
}
432
433
String end = p_what.substr(what.length());
434
435
if (what.containsn("$" + p_str)) { // Blender and other stuff.
436
return what.replace("$" + p_str, "") + end;
437
}
438
if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters
439
return what.substr(0, what.length() - (p_str.length() + 1)) + end;
440
}
441
if (what.to_lower().ends_with("_" + p_str)) { //collada only supports "_" and "-" besides letters
442
return what.substr(0, what.length() - (p_str.length() + 1)) + end;
443
}
444
return what;
445
}
446
447
static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r_shape_list, bool p_convex) {
448
ERR_FAIL_COND_MSG(mesh.is_null(), "Cannot generate shape list with null mesh value.");
449
if (!p_convex) {
450
Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape();
451
r_shape_list.push_back(shape);
452
} else {
453
Vector<Ref<Shape3D>> cd;
454
cd.push_back(mesh->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false));
455
if (cd.size()) {
456
for (int i = 0; i < cd.size(); i++) {
457
r_shape_list.push_back(cd[i]);
458
}
459
}
460
}
461
}
462
463
struct ScalableNodeCollection {
464
HashSet<Node3D *> node_3ds;
465
HashSet<Ref<ImporterMesh>> importer_meshes;
466
HashSet<Ref<Skin>> skins;
467
HashSet<Ref<Animation>> animations;
468
};
469
470
void _rescale_importer_mesh(Vector3 p_scale, Ref<ImporterMesh> p_mesh, bool is_shadow = false) {
471
// MESH and SKIN data divide, to compensate for object position multiplying.
472
473
const int surf_count = p_mesh->get_surface_count();
474
const int blendshape_count = p_mesh->get_blend_shape_count();
475
struct LocalSurfData {
476
Mesh::PrimitiveType prim = {};
477
Array arr;
478
Array bsarr;
479
Dictionary lods;
480
String name;
481
Ref<Material> mat;
482
uint64_t fmt_compress_flags = 0;
483
};
484
485
Vector<LocalSurfData> surf_data_by_mesh;
486
487
Vector<String> blendshape_names;
488
for (int bsidx = 0; bsidx < blendshape_count; bsidx++) {
489
blendshape_names.append(p_mesh->get_blend_shape_name(bsidx));
490
}
491
492
for (int surf_idx = 0; surf_idx < surf_count; surf_idx++) {
493
Mesh::PrimitiveType prim = p_mesh->get_surface_primitive_type(surf_idx);
494
const uint64_t fmt_compress_flags = p_mesh->get_surface_format(surf_idx);
495
Array arr = p_mesh->get_surface_arrays(surf_idx);
496
String name = p_mesh->get_surface_name(surf_idx);
497
Dictionary lods;
498
Ref<Material> mat = p_mesh->get_surface_material(surf_idx);
499
{
500
Vector<Vector3> vertex_array = arr[ArrayMesh::ARRAY_VERTEX];
501
for (int vert_arr_i = 0; vert_arr_i < vertex_array.size(); vert_arr_i++) {
502
vertex_array.write[vert_arr_i] = vertex_array[vert_arr_i] * p_scale;
503
}
504
arr[ArrayMesh::ARRAY_VERTEX] = vertex_array;
505
}
506
Array blendshapes;
507
for (int bsidx = 0; bsidx < blendshape_count; bsidx++) {
508
Array current_bsarr = p_mesh->get_surface_blend_shape_arrays(surf_idx, bsidx);
509
Vector<Vector3> current_bs_vertex_array = current_bsarr[ArrayMesh::ARRAY_VERTEX];
510
int current_bs_vert_arr_len = current_bs_vertex_array.size();
511
for (int32_t bs_vert_arr_i = 0; bs_vert_arr_i < current_bs_vert_arr_len; bs_vert_arr_i++) {
512
current_bs_vertex_array.write[bs_vert_arr_i] = current_bs_vertex_array[bs_vert_arr_i] * p_scale;
513
}
514
current_bsarr[ArrayMesh::ARRAY_VERTEX] = current_bs_vertex_array;
515
blendshapes.push_back(current_bsarr);
516
}
517
518
LocalSurfData surf_data_dictionary = LocalSurfData();
519
surf_data_dictionary.prim = prim;
520
surf_data_dictionary.arr = arr;
521
surf_data_dictionary.bsarr = blendshapes;
522
surf_data_dictionary.lods = lods;
523
surf_data_dictionary.fmt_compress_flags = fmt_compress_flags;
524
surf_data_dictionary.name = name;
525
surf_data_dictionary.mat = mat;
526
527
surf_data_by_mesh.push_back(surf_data_dictionary);
528
}
529
530
p_mesh->clear();
531
532
for (int bsidx = 0; bsidx < blendshape_count; bsidx++) {
533
p_mesh->add_blend_shape(blendshape_names[bsidx]);
534
}
535
536
for (int surf_idx = 0; surf_idx < surf_count; surf_idx++) {
537
const Mesh::PrimitiveType prim = surf_data_by_mesh[surf_idx].prim;
538
const Array arr = surf_data_by_mesh[surf_idx].arr;
539
const Array bsarr = surf_data_by_mesh[surf_idx].bsarr;
540
const Dictionary lods = surf_data_by_mesh[surf_idx].lods;
541
const uint64_t fmt_compress_flags = surf_data_by_mesh[surf_idx].fmt_compress_flags;
542
const String name = surf_data_by_mesh[surf_idx].name;
543
const Ref<Material> mat = surf_data_by_mesh[surf_idx].mat;
544
545
p_mesh->add_surface(prim, arr, bsarr, lods, mat, name, fmt_compress_flags);
546
}
547
548
if (!is_shadow && p_mesh->get_shadow_mesh() != p_mesh && p_mesh->get_shadow_mesh().is_valid()) {
549
_rescale_importer_mesh(p_scale, p_mesh->get_shadow_mesh(), true);
550
}
551
}
552
553
void _rescale_skin(Vector3 p_scale, Ref<Skin> p_skin) {
554
// MESH and SKIN data divide, to compensate for object position multiplying.
555
for (int i = 0; i < p_skin->get_bind_count(); i++) {
556
Transform3D transform = p_skin->get_bind_pose(i);
557
p_skin->set_bind_pose(i, Transform3D(transform.basis, p_scale * transform.origin));
558
}
559
}
560
561
void _rescale_animation(Vector3 p_scale, Ref<Animation> p_animation) {
562
for (int track_idx = 0; track_idx < p_animation->get_track_count(); track_idx++) {
563
if (p_animation->track_get_type(track_idx) == Animation::TYPE_POSITION_3D) {
564
for (int key_idx = 0; key_idx < p_animation->track_get_key_count(track_idx); key_idx++) {
565
Vector3 value = p_animation->track_get_key_value(track_idx, key_idx);
566
value = p_scale * value;
567
p_animation->track_set_key_value(track_idx, key_idx, value);
568
}
569
}
570
}
571
}
572
573
void _apply_scale_to_scalable_node_collection(ScalableNodeCollection &p_collection, Vector3 p_scale) {
574
for (Node3D *node_3d : p_collection.node_3ds) {
575
node_3d->set_position(p_scale * node_3d->get_position());
576
Skeleton3D *skeleton_3d = Object::cast_to<Skeleton3D>(node_3d);
577
if (skeleton_3d) {
578
for (int i = 0; i < skeleton_3d->get_bone_count(); i++) {
579
Transform3D rest = skeleton_3d->get_bone_rest(i);
580
Vector3 position = skeleton_3d->get_bone_pose_position(i);
581
skeleton_3d->set_bone_rest(i, Transform3D(rest.basis, p_scale * rest.origin));
582
skeleton_3d->set_bone_pose_position(i, p_scale * position);
583
}
584
}
585
}
586
for (Ref<ImporterMesh> mesh : p_collection.importer_meshes) {
587
_rescale_importer_mesh(p_scale, mesh, false);
588
}
589
for (Ref<Skin> skin : p_collection.skins) {
590
_rescale_skin(p_scale, skin);
591
}
592
for (Ref<Animation> animation : p_collection.animations) {
593
_rescale_animation(p_scale, animation);
594
}
595
}
596
597
void _populate_scalable_nodes_collection(Node *p_node, ScalableNodeCollection &p_collection) {
598
if (!p_node) {
599
return;
600
}
601
Node3D *node_3d = Object::cast_to<Node3D>(p_node);
602
if (node_3d) {
603
p_collection.node_3ds.insert(node_3d);
604
ImporterMeshInstance3D *mesh_instance_3d = Object::cast_to<ImporterMeshInstance3D>(p_node);
605
if (mesh_instance_3d) {
606
Ref<ImporterMesh> mesh = mesh_instance_3d->get_mesh();
607
if (mesh.is_valid()) {
608
p_collection.importer_meshes.insert(mesh);
609
}
610
Ref<Skin> skin = mesh_instance_3d->get_skin();
611
if (skin.is_valid()) {
612
p_collection.skins.insert(skin);
613
}
614
}
615
}
616
AnimationPlayer *animation_player = Object::cast_to<AnimationPlayer>(p_node);
617
if (animation_player) {
618
List<StringName> animation_list;
619
animation_player->get_animation_list(&animation_list);
620
621
for (const StringName &E : animation_list) {
622
Ref<Animation> animation = animation_player->get_animation(E);
623
p_collection.animations.insert(animation);
624
}
625
}
626
627
for (int i = 0; i < p_node->get_child_count(); i++) {
628
Node *child = p_node->get_child(i);
629
_populate_scalable_nodes_collection(child, p_collection);
630
}
631
}
632
633
void _apply_permanent_scale_to_descendants(Node *p_root_node, Vector3 p_scale) {
634
ScalableNodeCollection scalable_node_collection;
635
_populate_scalable_nodes_collection(p_root_node, scalable_node_collection);
636
_apply_scale_to_scalable_node_collection(scalable_node_collection, p_scale);
637
}
638
639
Node *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) {
640
bool use_name_suffixes = true;
641
if (p_options.has("nodes/use_name_suffixes")) {
642
use_name_suffixes = p_options["nodes/use_name_suffixes"];
643
}
644
if (!use_name_suffixes) {
645
return p_node;
646
}
647
648
// Children first.
649
for (int i = 0; i < p_node->get_child_count(); i++) {
650
Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames, p_options);
651
if (!r) {
652
i--; // Was erased.
653
}
654
}
655
656
String name = p_node->get_name();
657
NodePath original_path = p_root->get_path_to(p_node); // Used to detect renames due to import hints.
658
659
Ref<Resource> original_meta = memnew(Resource); // Create temp resource to hold original meta
660
original_meta->merge_meta_from(p_node);
661
662
bool isroot = p_node == p_root;
663
664
if (!isroot && _teststr(name, "noimp")) {
665
p_node->set_owner(nullptr);
666
memdelete(p_node);
667
return nullptr;
668
}
669
670
if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
671
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
672
673
Ref<ImporterMesh> m = mi->get_mesh();
674
675
if (m.is_valid()) {
676
for (int i = 0; i < m->get_surface_count(); i++) {
677
Ref<BaseMaterial3D> mat = m->get_surface_material(i);
678
if (mat.is_null()) {
679
continue;
680
}
681
682
if (_teststr(mat->get_name(), "alpha")) {
683
mat->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA);
684
mat->set_name(_fixstr(mat->get_name(), "alpha"));
685
}
686
if (_teststr(mat->get_name(), "vcol")) {
687
mat->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
688
mat->set_flag(BaseMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
689
mat->set_name(_fixstr(mat->get_name(), "vcol"));
690
}
691
}
692
}
693
}
694
695
if (Object::cast_to<AnimationPlayer>(p_node)) {
696
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
697
698
// Node paths in animation tracks are relative to the following path (this is used to fix node paths below).
699
Node *ap_root = ap->get_node(ap->get_root_node());
700
NodePath path_prefix = p_root->get_path_to(ap_root);
701
702
bool nodes_were_renamed = r_node_renames.size() != 0;
703
704
List<StringName> anims;
705
ap->get_animation_list(&anims);
706
for (const StringName &E : anims) {
707
Ref<Animation> anim = ap->get_animation(E);
708
ERR_CONTINUE(anim.is_null());
709
710
// Remove animation tracks referencing non-importable nodes.
711
for (int i = 0; i < anim->get_track_count(); i++) {
712
NodePath path = anim->track_get_path(i);
713
714
for (int j = 0; j < path.get_name_count(); j++) {
715
String node = path.get_name(j);
716
if (_teststr(node, "noimp")) {
717
anim->remove_track(i);
718
i--;
719
break;
720
}
721
}
722
}
723
724
// Fix node paths in animations, in case nodes were renamed earlier due to import hints.
725
if (nodes_were_renamed) {
726
for (int i = 0; i < anim->get_track_count(); i++) {
727
NodePath path = anim->track_get_path(i);
728
// Convert track path to absolute node path without subnames (some manual work because we are not in the scene tree).
729
Vector<StringName> absolute_path_names = path_prefix.get_names();
730
absolute_path_names.append_array(path.get_names());
731
NodePath absolute_path(absolute_path_names, false);
732
absolute_path.simplify();
733
// Fix paths to renamed nodes.
734
for (const Pair<NodePath, Node *> &F : r_node_renames) {
735
if (F.first == absolute_path) {
736
NodePath new_path(ap_root->get_path_to(F.second).get_names(), path.get_subnames(), false);
737
print_verbose(vformat("Fix: Correcting node path in animation track: %s should be %s", path, new_path));
738
anim->track_set_path(i, new_path);
739
break; // Only one match is possible.
740
}
741
}
742
}
743
}
744
745
String animname = E;
746
const int loop_string_count = 3;
747
static const char *loop_strings[loop_string_count] = { "loop_mode", "loop", "cycle" };
748
for (int i = 0; i < loop_string_count; i++) {
749
if (_teststr(animname, loop_strings[i])) {
750
anim->set_loop_mode(Animation::LOOP_LINEAR);
751
animname = _fixstr(animname, loop_strings[i]);
752
753
Ref<AnimationLibrary> library = ap->get_animation_library(ap->find_animation_library(anim));
754
library->rename_animation(E, animname);
755
}
756
}
757
}
758
}
759
760
bool use_node_type_suffixes = true;
761
if (p_options.has("nodes/use_node_type_suffixes")) {
762
use_node_type_suffixes = p_options["nodes/use_node_type_suffixes"];
763
}
764
if (!use_node_type_suffixes) {
765
return p_node;
766
}
767
768
if (_teststr(name, "colonly") || _teststr(name, "convcolonly")) {
769
if (isroot) {
770
return p_node;
771
}
772
773
String fixed_name;
774
if (_teststr(name, "colonly")) {
775
fixed_name = _fixstr(name, "colonly");
776
} else if (_teststr(name, "convcolonly")) {
777
fixed_name = _fixstr(name, "convcolonly");
778
}
779
780
if (fixed_name.is_empty()) {
781
p_node->set_owner(nullptr);
782
memdelete(p_node);
783
ERR_FAIL_V_MSG(nullptr, vformat("Skipped node `%s` because its name is empty after removing the suffix.", name));
784
}
785
786
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
787
if (mi) {
788
Ref<ImporterMesh> mesh = mi->get_mesh();
789
790
if (mesh.is_valid()) {
791
Vector<Ref<Shape3D>> shapes;
792
if (r_collision_map.has(mesh)) {
793
shapes = r_collision_map[mesh];
794
} else if (_teststr(name, "colonly")) {
795
_pre_gen_shape_list(mesh, shapes, false);
796
r_collision_map[mesh] = shapes;
797
} else if (_teststr(name, "convcolonly")) {
798
_pre_gen_shape_list(mesh, shapes, true);
799
r_collision_map[mesh] = shapes;
800
}
801
802
if (shapes.size()) {
803
StaticBody3D *col = memnew(StaticBody3D);
804
col->set_transform(mi->get_transform());
805
col->set_name(fixed_name);
806
_copy_meta(p_node, col);
807
p_node->replace_by(col);
808
p_node->set_owner(nullptr);
809
memdelete(p_node);
810
p_node = col;
811
812
_add_shapes(col, shapes);
813
}
814
}
815
816
} else if (p_node->has_meta("empty_draw_type")) {
817
String empty_draw_type = String(p_node->get_meta("empty_draw_type"));
818
StaticBody3D *sb = memnew(StaticBody3D);
819
sb->set_name(fixed_name);
820
Object::cast_to<Node3D>(sb)->set_transform(Object::cast_to<Node3D>(p_node)->get_transform());
821
_copy_meta(p_node, sb);
822
p_node->replace_by(sb);
823
p_node->set_owner(nullptr);
824
memdelete(p_node);
825
p_node = sb;
826
CollisionShape3D *colshape = memnew(CollisionShape3D);
827
if (empty_draw_type == "CUBE") {
828
BoxShape3D *boxShape = memnew(BoxShape3D);
829
boxShape->set_size(Vector3(2, 2, 2));
830
colshape->set_shape(boxShape);
831
} else if (empty_draw_type == "SINGLE_ARROW") {
832
SeparationRayShape3D *rayShape = memnew(SeparationRayShape3D);
833
rayShape->set_length(1);
834
colshape->set_shape(rayShape);
835
Object::cast_to<Node3D>(sb)->rotate_x(Math::PI / 2);
836
} else if (empty_draw_type == "IMAGE") {
837
WorldBoundaryShape3D *world_boundary_shape = memnew(WorldBoundaryShape3D);
838
colshape->set_shape(world_boundary_shape);
839
} else {
840
SphereShape3D *sphereShape = memnew(SphereShape3D);
841
sphereShape->set_radius(1);
842
colshape->set_shape(sphereShape);
843
}
844
sb->add_child(colshape, true);
845
colshape->set_owner(sb->get_owner());
846
}
847
848
} else if (_teststr(name, "rigid") && Object::cast_to<ImporterMeshInstance3D>(p_node)) {
849
if (isroot) {
850
return p_node;
851
}
852
853
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
854
Ref<ImporterMesh> mesh = mi->get_mesh();
855
856
if (mesh.is_valid()) {
857
Vector<Ref<Shape3D>> shapes;
858
if (r_collision_map.has(mesh)) {
859
shapes = r_collision_map[mesh];
860
} else {
861
_pre_gen_shape_list(mesh, shapes, true);
862
}
863
864
RigidBody3D *rigid_body = memnew(RigidBody3D);
865
rigid_body->set_name(_fixstr(name, "rigid_body"));
866
_copy_meta(p_node, rigid_body);
867
p_node->replace_by(rigid_body);
868
rigid_body->set_transform(mi->get_transform());
869
p_node = rigid_body;
870
mi->set_transform(Transform3D());
871
rigid_body->add_child(mi, true);
872
mi->set_owner(rigid_body->get_owner());
873
874
_add_shapes(rigid_body, shapes);
875
}
876
877
} else if ((_teststr(name, "col") || (_teststr(name, "convcol"))) && Object::cast_to<ImporterMeshInstance3D>(p_node)) {
878
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
879
880
Ref<ImporterMesh> mesh = mi->get_mesh();
881
882
if (mesh.is_valid()) {
883
Vector<Ref<Shape3D>> shapes;
884
String fixed_name;
885
if (r_collision_map.has(mesh)) {
886
shapes = r_collision_map[mesh];
887
} else if (_teststr(name, "col")) {
888
_pre_gen_shape_list(mesh, shapes, false);
889
r_collision_map[mesh] = shapes;
890
} else if (_teststr(name, "convcol")) {
891
_pre_gen_shape_list(mesh, shapes, true);
892
r_collision_map[mesh] = shapes;
893
}
894
895
if (_teststr(name, "col")) {
896
fixed_name = _fixstr(name, "col");
897
} else if (_teststr(name, "convcol")) {
898
fixed_name = _fixstr(name, "convcol");
899
}
900
901
if (!fixed_name.is_empty()) {
902
if (mi->get_parent() && !mi->get_parent()->has_node(fixed_name)) {
903
mi->set_name(fixed_name);
904
}
905
}
906
907
if (shapes.size()) {
908
StaticBody3D *col = memnew(StaticBody3D);
909
mi->add_child(col, true);
910
col->set_owner(mi->get_owner());
911
912
_add_shapes(col, shapes);
913
}
914
}
915
916
} else if (_teststr(name, "navmesh") && Object::cast_to<ImporterMeshInstance3D>(p_node)) {
917
if (isroot) {
918
return p_node;
919
}
920
921
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
922
923
Ref<ImporterMesh> mesh = mi->get_mesh();
924
ERR_FAIL_COND_V(mesh.is_null(), nullptr);
925
NavigationRegion3D *nmi = memnew(NavigationRegion3D);
926
927
nmi->set_name(_fixstr(name, "navmesh"));
928
Ref<NavigationMesh> nmesh = mesh->create_navigation_mesh();
929
nmi->set_navigation_mesh(nmesh);
930
Object::cast_to<Node3D>(nmi)->set_transform(mi->get_transform());
931
_copy_meta(p_node, nmi);
932
p_node->replace_by(nmi);
933
p_node->set_owner(nullptr);
934
memdelete(p_node);
935
p_node = nmi;
936
} else if (_teststr(name, "occ") || _teststr(name, "occonly")) {
937
if (isroot) {
938
return p_node;
939
}
940
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
941
if (mi) {
942
Ref<ImporterMesh> mesh = mi->get_mesh();
943
944
if (mesh.is_valid()) {
945
if (r_occluder_arrays) {
946
OccluderInstance3D::bake_single_node(mi, 0.0f, r_occluder_arrays->first, r_occluder_arrays->second);
947
}
948
if (_teststr(name, "occ")) {
949
String fixed_name = _fixstr(name, "occ");
950
if (!fixed_name.is_empty()) {
951
if (mi->get_parent() && !mi->get_parent()->has_node(fixed_name)) {
952
mi->set_name(fixed_name);
953
}
954
}
955
} else {
956
p_node->set_owner(nullptr);
957
memdelete(p_node);
958
p_node = nullptr;
959
}
960
}
961
}
962
} else if (_teststr(name, "vehicle")) {
963
if (isroot) {
964
return p_node;
965
}
966
967
Node *owner = p_node->get_owner();
968
Node3D *s = Object::cast_to<Node3D>(p_node);
969
VehicleBody3D *bv = memnew(VehicleBody3D);
970
String n = _fixstr(p_node->get_name(), "vehicle");
971
bv->set_name(n);
972
_copy_meta(p_node, bv);
973
p_node->replace_by(bv);
974
p_node->set_name(n);
975
bv->add_child(p_node);
976
bv->set_owner(owner);
977
p_node->set_owner(owner);
978
bv->set_transform(s->get_transform());
979
s->set_transform(Transform3D());
980
981
p_node = bv;
982
} else if (_teststr(name, "wheel")) {
983
if (isroot) {
984
return p_node;
985
}
986
987
Node *owner = p_node->get_owner();
988
Node3D *s = Object::cast_to<Node3D>(p_node);
989
VehicleWheel3D *bv = memnew(VehicleWheel3D);
990
String n = _fixstr(p_node->get_name(), "wheel");
991
bv->set_name(n);
992
_copy_meta(p_node, bv);
993
p_node->replace_by(bv);
994
p_node->set_name(n);
995
bv->add_child(p_node);
996
bv->set_owner(owner);
997
p_node->set_owner(owner);
998
bv->set_transform(s->get_transform());
999
s->set_transform(Transform3D());
1000
1001
p_node = bv;
1002
} else if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
1003
//last attempt, maybe collision inside the mesh data
1004
1005
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
1006
1007
Ref<ImporterMesh> mesh = mi->get_mesh();
1008
if (mesh.is_valid()) {
1009
Vector<Ref<Shape3D>> shapes;
1010
if (r_collision_map.has(mesh)) {
1011
shapes = r_collision_map[mesh];
1012
} else if (_teststr(mesh->get_name(), "col")) {
1013
_pre_gen_shape_list(mesh, shapes, false);
1014
r_collision_map[mesh] = shapes;
1015
mesh->set_name(_fixstr(mesh->get_name(), "col"));
1016
} else if (_teststr(mesh->get_name(), "convcol")) {
1017
_pre_gen_shape_list(mesh, shapes, true);
1018
r_collision_map[mesh] = shapes;
1019
mesh->set_name(_fixstr(mesh->get_name(), "convcol"));
1020
} else if (_teststr(mesh->get_name(), "occ")) {
1021
if (r_occluder_arrays) {
1022
OccluderInstance3D::bake_single_node(mi, 0.0f, r_occluder_arrays->first, r_occluder_arrays->second);
1023
}
1024
mesh->set_name(_fixstr(mesh->get_name(), "occ"));
1025
}
1026
1027
if (shapes.size()) {
1028
StaticBody3D *col = memnew(StaticBody3D);
1029
p_node->add_child(col, true);
1030
col->set_owner(p_node->get_owner());
1031
1032
_add_shapes(col, shapes);
1033
}
1034
}
1035
}
1036
1037
if (p_node) {
1038
NodePath new_path = p_root->get_path_to(p_node);
1039
if (new_path != original_path) {
1040
print_verbose(vformat("Fix: Renamed %s to %s", original_path, new_path));
1041
r_node_renames.push_back({ original_path, p_node });
1042
}
1043
// If we created new node instead, merge meta values from the original node.
1044
p_node->merge_meta_from(*original_meta);
1045
}
1046
1047
return p_node;
1048
}
1049
1050
Node *ResourceImporterScene::_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps) {
1051
// children first
1052
for (int i = 0; i < p_node->get_child_count(); i++) {
1053
Node *r = _pre_fix_animations(p_node->get_child(i), p_root, p_node_data, p_animation_data, p_animation_fps);
1054
if (!r) {
1055
i--; //was erased
1056
}
1057
}
1058
1059
String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));
1060
1061
Dictionary node_settings;
1062
if (p_node_data.has(import_id)) {
1063
node_settings = p_node_data[import_id];
1064
}
1065
1066
{
1067
//make sure this is unique
1068
node_settings = node_settings.duplicate(true);
1069
//fill node settings for this node with default values
1070
List<ImportOption> iopts;
1071
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);
1072
for (const ImportOption &E : iopts) {
1073
if (!node_settings.has(E.option.name)) {
1074
node_settings[E.option.name] = E.default_value;
1075
}
1076
}
1077
}
1078
1079
if (Object::cast_to<AnimationPlayer>(p_node)) {
1080
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
1081
List<StringName> anims;
1082
ap->get_animation_list(&anims);
1083
1084
AnimationImportTracks import_tracks_mode[TRACK_CHANNEL_MAX] = {
1085
AnimationImportTracks(int(node_settings["import_tracks/position"])),
1086
AnimationImportTracks(int(node_settings["import_tracks/rotation"])),
1087
AnimationImportTracks(int(node_settings["import_tracks/scale"]))
1088
};
1089
1090
if (!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)) {
1091
_optimize_track_usage(ap, import_tracks_mode);
1092
}
1093
}
1094
1095
return p_node;
1096
}
1097
1098
Node *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) {
1099
// children first
1100
for (int i = 0; i < p_node->get_child_count(); i++) {
1101
Node *r = _post_fix_animations(p_node->get_child(i), p_root, p_node_data, p_animation_data, p_animation_fps, p_remove_immutable_tracks);
1102
if (!r) {
1103
i--; //was erased
1104
}
1105
}
1106
1107
String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));
1108
1109
Dictionary node_settings;
1110
if (p_node_data.has(import_id)) {
1111
node_settings = p_node_data[import_id];
1112
}
1113
1114
{
1115
//make sure this is unique
1116
node_settings = node_settings.duplicate(true);
1117
//fill node settings for this node with default values
1118
List<ImportOption> iopts;
1119
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);
1120
for (const ImportOption &E : iopts) {
1121
if (!node_settings.has(E.option.name)) {
1122
node_settings[E.option.name] = E.default_value;
1123
}
1124
}
1125
}
1126
1127
if (Object::cast_to<AnimationPlayer>(p_node)) {
1128
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
1129
List<StringName> anims;
1130
ap->get_animation_list(&anims);
1131
1132
if (p_remove_immutable_tracks) {
1133
AnimationImportTracks import_tracks_mode[TRACK_CHANNEL_MAX] = {
1134
AnimationImportTracks(int(node_settings["import_tracks/position"])),
1135
AnimationImportTracks(int(node_settings["import_tracks/rotation"])),
1136
AnimationImportTracks(int(node_settings["import_tracks/scale"]))
1137
};
1138
HashMap<NodePath, bool> used_tracks[TRACK_CHANNEL_MAX];
1139
1140
for (const StringName &name : anims) {
1141
Ref<Animation> anim = ap->get_animation(name);
1142
int track_count = anim->get_track_count();
1143
LocalVector<int> tracks_to_keep;
1144
for (int track_i = 0; track_i < track_count; track_i++) {
1145
tracks_to_keep.push_back(track_i);
1146
int track_channel_type = 0;
1147
switch (anim->track_get_type(track_i)) {
1148
case Animation::TYPE_POSITION_3D:
1149
track_channel_type = TRACK_CHANNEL_POSITION;
1150
break;
1151
case Animation::TYPE_ROTATION_3D:
1152
track_channel_type = TRACK_CHANNEL_ROTATION;
1153
break;
1154
case Animation::TYPE_SCALE_3D:
1155
track_channel_type = TRACK_CHANNEL_SCALE;
1156
break;
1157
default:
1158
continue;
1159
}
1160
AnimationImportTracks track_mode = import_tracks_mode[track_channel_type];
1161
NodePath path = anim->track_get_path(track_i);
1162
Node *n = p_root->get_node(path);
1163
Node3D *n3d = Object::cast_to<Node3D>(n);
1164
Skeleton3D *skel = Object::cast_to<Skeleton3D>(n);
1165
bool keep_track = false;
1166
Vector3 loc;
1167
Quaternion rot;
1168
Vector3 scale;
1169
if (skel && path.get_subname_count() > 0) {
1170
StringName bone = path.get_subname(0);
1171
int bone_idx = skel->find_bone(bone);
1172
if (bone_idx == -1) {
1173
continue;
1174
}
1175
// Note that this is using get_bone_pose to update the bone pose cache.
1176
Transform3D bone_rest = skel->get_bone_rest(bone_idx);
1177
loc = bone_rest.origin / skel->get_motion_scale();
1178
rot = bone_rest.basis.get_rotation_quaternion();
1179
scale = bone_rest.basis.get_scale();
1180
} else if (n3d) {
1181
loc = n3d->get_position();
1182
rot = n3d->get_transform().basis.get_rotation_quaternion();
1183
scale = n3d->get_scale();
1184
} else {
1185
continue;
1186
}
1187
1188
if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {
1189
if (used_tracks[track_channel_type].has(path)) {
1190
if (used_tracks[track_channel_type][path]) {
1191
continue;
1192
}
1193
} else {
1194
used_tracks[track_channel_type].insert(path, false);
1195
}
1196
}
1197
1198
for (int key_i = 0; key_i < anim->track_get_key_count(track_i) && !keep_track; key_i++) {
1199
switch (track_channel_type) {
1200
case TRACK_CHANNEL_POSITION: {
1201
Vector3 key_pos;
1202
anim->position_track_get_key(track_i, key_i, &key_pos);
1203
if (!key_pos.is_equal_approx(loc)) {
1204
keep_track = true;
1205
if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {
1206
used_tracks[track_channel_type][path] = true;
1207
}
1208
}
1209
} break;
1210
case TRACK_CHANNEL_ROTATION: {
1211
Quaternion key_rot;
1212
anim->rotation_track_get_key(track_i, key_i, &key_rot);
1213
if (!key_rot.is_equal_approx(rot)) {
1214
keep_track = true;
1215
if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {
1216
used_tracks[track_channel_type][path] = true;
1217
}
1218
}
1219
} break;
1220
case TRACK_CHANNEL_SCALE: {
1221
Vector3 key_scl;
1222
anim->scale_track_get_key(track_i, key_i, &key_scl);
1223
if (!key_scl.is_equal_approx(scale)) {
1224
keep_track = true;
1225
if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {
1226
used_tracks[track_channel_type][path] = true;
1227
}
1228
}
1229
} break;
1230
default:
1231
break;
1232
}
1233
}
1234
if (track_mode != ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL && !keep_track) {
1235
tracks_to_keep.remove_at(tracks_to_keep.size() - 1);
1236
}
1237
}
1238
for (int dst_track_i = 0; dst_track_i < (int)tracks_to_keep.size(); dst_track_i++) {
1239
int src_track_i = tracks_to_keep[dst_track_i];
1240
if (src_track_i != dst_track_i) {
1241
anim->track_swap(src_track_i, dst_track_i);
1242
}
1243
}
1244
for (int track_i = track_count - 1; track_i >= (int)tracks_to_keep.size(); track_i--) {
1245
anim->remove_track(track_i);
1246
}
1247
}
1248
for (const StringName &name : anims) {
1249
Ref<Animation> anim = ap->get_animation(name);
1250
int track_count = anim->get_track_count();
1251
LocalVector<int> tracks_to_keep;
1252
for (int track_i = 0; track_i < track_count; track_i++) {
1253
tracks_to_keep.push_back(track_i);
1254
int track_channel_type = 0;
1255
switch (anim->track_get_type(track_i)) {
1256
case Animation::TYPE_POSITION_3D:
1257
track_channel_type = TRACK_CHANNEL_POSITION;
1258
break;
1259
case Animation::TYPE_ROTATION_3D:
1260
track_channel_type = TRACK_CHANNEL_ROTATION;
1261
break;
1262
case Animation::TYPE_SCALE_3D:
1263
track_channel_type = TRACK_CHANNEL_SCALE;
1264
break;
1265
default:
1266
continue;
1267
}
1268
AnimationImportTracks track_mode = import_tracks_mode[track_channel_type];
1269
if (track_mode == ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {
1270
NodePath path = anim->track_get_path(track_i);
1271
if (used_tracks[track_channel_type].has(path) && !used_tracks[track_channel_type][path]) {
1272
tracks_to_keep.remove_at(tracks_to_keep.size() - 1);
1273
}
1274
}
1275
}
1276
for (int dst_track_i = 0; dst_track_i < (int)tracks_to_keep.size(); dst_track_i++) {
1277
int src_track_i = tracks_to_keep[dst_track_i];
1278
if (src_track_i != dst_track_i) {
1279
anim->track_swap(src_track_i, dst_track_i);
1280
}
1281
}
1282
for (int track_i = track_count - 1; track_i >= (int)tracks_to_keep.size(); track_i--) {
1283
anim->remove_track(track_i);
1284
}
1285
}
1286
}
1287
1288
bool use_optimizer = node_settings["optimizer/enabled"];
1289
float anim_optimizer_linerr = node_settings["optimizer/max_velocity_error"];
1290
float anim_optimizer_angerr = node_settings["optimizer/max_angular_error"];
1291
int anim_optimizer_preerr = node_settings["optimizer/max_precision_error"];
1292
1293
if (use_optimizer) {
1294
_optimize_animations(ap, anim_optimizer_linerr, anim_optimizer_angerr, anim_optimizer_preerr);
1295
}
1296
1297
bool use_compression = node_settings["compression/enabled"];
1298
int anim_compression_page_size = node_settings["compression/page_size"];
1299
1300
if (use_compression) {
1301
_compress_animations(ap, anim_compression_page_size);
1302
}
1303
1304
for (const StringName &name : anims) {
1305
Ref<Animation> anim = ap->get_animation(name);
1306
Array animation_slices;
1307
1308
if (p_animation_data.has(name)) {
1309
Dictionary anim_settings = p_animation_data[name];
1310
1311
{
1312
int slice_count = anim_settings["slices/amount"];
1313
1314
for (int i = 0; i < slice_count; i++) {
1315
String slice_name = anim_settings["slice_" + itos(i + 1) + "/name"];
1316
int from_frame = anim_settings["slice_" + itos(i + 1) + "/start_frame"];
1317
int end_frame = anim_settings["slice_" + itos(i + 1) + "/end_frame"];
1318
Animation::LoopMode loop_mode = static_cast<Animation::LoopMode>((int)anim_settings["slice_" + itos(i + 1) + "/loop_mode"]);
1319
bool save_to_file = anim_settings["slice_" + itos(i + 1) + "/save_to_file/enabled"];
1320
String save_to_path = anim_settings["slice_" + itos(i + 1) + "/save_to_file/path"];
1321
bool save_to_file_keep_custom = anim_settings["slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"];
1322
1323
animation_slices.push_back(slice_name);
1324
animation_slices.push_back(from_frame / p_animation_fps);
1325
animation_slices.push_back(end_frame / p_animation_fps);
1326
animation_slices.push_back(loop_mode);
1327
animation_slices.push_back(save_to_file);
1328
animation_slices.push_back(save_to_path);
1329
animation_slices.push_back(save_to_file_keep_custom);
1330
}
1331
1332
if (animation_slices.size() > 0) {
1333
_create_slices(ap, anim, animation_slices, true);
1334
}
1335
}
1336
{
1337
//fill with default values
1338
List<ImportOption> iopts;
1339
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION, &iopts);
1340
for (const ImportOption &F : iopts) {
1341
if (!anim_settings.has(F.option.name)) {
1342
anim_settings[F.option.name] = F.default_value;
1343
}
1344
}
1345
}
1346
1347
anim->set_loop_mode(static_cast<Animation::LoopMode>((int)anim_settings["settings/loop_mode"]));
1348
bool save = anim_settings["save_to_file/enabled"];
1349
String path = anim_settings["save_to_file/path"];
1350
bool keep_custom = anim_settings["save_to_file/keep_custom_tracks"];
1351
1352
Ref<Animation> saved_anim = _save_animation_to_file(anim, save, path, keep_custom);
1353
1354
if (saved_anim != anim) {
1355
Ref<AnimationLibrary> al = ap->get_animation_library(ap->find_animation_library(anim));
1356
al->add_animation(name, saved_anim); //replace
1357
}
1358
}
1359
}
1360
}
1361
1362
return p_node;
1363
}
1364
1365
Node *ResourceImporterScene::_replace_node_with_type_and_script(Node *p_node, String p_node_type, Ref<Script> p_script) {
1366
p_node_type = p_node_type.get_slicec(' ', 0); // Full root_type is "ClassName (filename.gd)" for a script global class.
1367
if (p_script.is_valid()) {
1368
// Ensure the node type supports the script, or pick one that does.
1369
String script_base_type = p_script->get_instance_base_type();
1370
if (ClassDB::is_parent_class(script_base_type, "Node")) {
1371
if (p_node_type.is_empty() || !ClassDB::is_parent_class(p_node_type, script_base_type)) {
1372
p_node_type = script_base_type;
1373
}
1374
}
1375
}
1376
if (!p_node_type.is_empty() && ScriptServer::is_global_class(p_node_type)) {
1377
// If the user specified a script class, we need to get the base node type.
1378
if (p_script.is_null()) {
1379
p_script = ResourceLoader::load(ScriptServer::get_global_class_path(p_node_type));
1380
}
1381
p_node_type = ScriptServer::get_global_class_base(p_node_type);
1382
while (!p_node_type.is_empty()) {
1383
if (ScriptServer::is_global_class(p_node_type)) {
1384
p_node_type = ScriptServer::get_global_class_base(p_node_type);
1385
} else {
1386
break;
1387
}
1388
}
1389
}
1390
if (!p_node_type.is_empty() && p_node->get_class_name() != p_node_type) {
1391
// If the user specified a Godot node type that does not match
1392
// what the scene import gave us, replace the root node.
1393
Node *new_base_node = Object::cast_to<Node>(ClassDB::instantiate(p_node_type));
1394
if (new_base_node) {
1395
List<PropertyInfo> old_properties;
1396
p_node->get_property_list(&old_properties);
1397
for (const PropertyInfo &prop : old_properties) {
1398
if (!(prop.usage & PROPERTY_USAGE_STORAGE)) {
1399
continue;
1400
}
1401
new_base_node->set(prop.name, p_node->get(prop.name));
1402
}
1403
new_base_node->set_name(p_node->get_name());
1404
_copy_meta(p_node, new_base_node);
1405
p_node->replace_by(new_base_node);
1406
p_node->set_owner(nullptr);
1407
memdelete(p_node);
1408
p_node = new_base_node;
1409
}
1410
}
1411
if (p_script.is_valid()) {
1412
p_node->set_script(Variant(p_script));
1413
}
1414
return p_node;
1415
}
1416
1417
Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale, const String &p_source_file, const HashMap<StringName, Variant> &p_options) {
1418
// children first
1419
for (int i = 0; i < p_node->get_child_count(); i++) {
1420
Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_occluder_arrays, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps, p_applied_root_scale, p_source_file, p_options);
1421
if (!r) {
1422
i--; //was erased
1423
}
1424
}
1425
1426
int extract_mat = 0;
1427
if (p_options.has("materials/extract")) {
1428
extract_mat = p_options["materials/extract"];
1429
}
1430
1431
String spath = p_source_file.get_base_dir();
1432
if (p_options.has("materials/extract_path")) {
1433
String extpath = p_options["materials/extract_path"];
1434
if (!extpath.is_empty()) {
1435
spath = extpath;
1436
}
1437
}
1438
1439
bool isroot = p_node == p_root;
1440
1441
String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));
1442
1443
Dictionary node_settings;
1444
if (p_node_data.has(import_id)) {
1445
node_settings = p_node_data[import_id];
1446
}
1447
1448
if (!isroot && (node_settings.has("import/skip_import") && bool(node_settings["import/skip_import"]))) {
1449
p_node->set_owner(nullptr);
1450
memdelete(p_node);
1451
return nullptr;
1452
}
1453
1454
{
1455
//make sure this is unique
1456
node_settings = node_settings.duplicate(true);
1457
//fill node settings for this node with default values
1458
List<ImportOption> iopts;
1459
if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
1460
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE, &iopts);
1461
} else if (Object::cast_to<AnimationPlayer>(p_node)) {
1462
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);
1463
} else if (Object::cast_to<Skeleton3D>(p_node)) {
1464
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, &iopts);
1465
} else {
1466
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_NODE, &iopts);
1467
}
1468
for (const ImportOption &E : iopts) {
1469
if (!node_settings.has(E.option.name)) {
1470
node_settings[E.option.name] = E.default_value;
1471
}
1472
}
1473
}
1474
1475
{
1476
ObjectID node_id = p_node->get_instance_id();
1477
for (int i = 0; i < post_importer_plugins.size(); i++) {
1478
post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_NODE, p_root, p_node, Ref<Resource>(), node_settings);
1479
if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue
1480
break;
1481
}
1482
}
1483
}
1484
1485
if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
1486
ObjectID node_id = p_node->get_instance_id();
1487
for (int i = 0; i < post_importer_plugins.size(); i++) {
1488
post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE, p_root, p_node, Ref<Resource>(), node_settings);
1489
if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue
1490
break;
1491
}
1492
}
1493
}
1494
1495
if (Object::cast_to<Skeleton3D>(p_node)) {
1496
Ref<Animation> rest_animation;
1497
float rest_animation_timestamp = 0.0;
1498
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);
1499
if (skeleton != nullptr && int(node_settings.get("rest_pose/load_pose", 0)) != 0) {
1500
String selected_animation_name = node_settings.get("rest_pose/selected_animation", String());
1501
if (int(node_settings["rest_pose/load_pose"]) == 1) {
1502
TypedArray<Node> children = p_root->find_children("*", "AnimationPlayer", true, false);
1503
for (int node_i = 0; node_i < children.size(); node_i++) {
1504
AnimationPlayer *anim_player = cast_to<AnimationPlayer>(children[node_i]);
1505
ERR_CONTINUE(anim_player == nullptr);
1506
List<StringName> anim_list;
1507
anim_player->get_animation_list(&anim_list);
1508
if (anim_list.size() == 1) {
1509
selected_animation_name = anim_list.front()->get();
1510
}
1511
rest_animation = anim_player->get_animation(selected_animation_name);
1512
if (rest_animation.is_valid()) {
1513
break;
1514
}
1515
}
1516
} else if (int(node_settings["rest_pose/load_pose"]) == 2) {
1517
Object *external_object = node_settings.get("rest_pose/external_animation_library", Variant());
1518
rest_animation = external_object;
1519
if (rest_animation.is_null()) {
1520
Ref<AnimationLibrary> library(external_object);
1521
if (library.is_valid()) {
1522
List<StringName> anim_list;
1523
library->get_animation_list(&anim_list);
1524
if (anim_list.size() == 1) {
1525
selected_animation_name = String(anim_list.front()->get());
1526
}
1527
rest_animation = library->get_animation(selected_animation_name);
1528
}
1529
}
1530
}
1531
rest_animation_timestamp = double(node_settings.get("rest_pose/selected_timestamp", 0.0));
1532
if (rest_animation.is_valid()) {
1533
for (int track_i = 0; track_i < rest_animation->get_track_count(); track_i++) {
1534
NodePath path = rest_animation->track_get_path(track_i);
1535
StringName node_path = path.get_concatenated_names();
1536
if (String(node_path).begins_with("%")) {
1537
continue; // Unique node names are commonly used with retargeted animations, which we do not want to use.
1538
}
1539
StringName skeleton_bone = path.get_concatenated_subnames();
1540
if (skeleton_bone == StringName()) {
1541
continue;
1542
}
1543
int bone_idx = skeleton->find_bone(skeleton_bone);
1544
if (bone_idx == -1) {
1545
continue;
1546
}
1547
switch (rest_animation->track_get_type(track_i)) {
1548
case Animation::TYPE_POSITION_3D: {
1549
Vector3 bone_position = rest_animation->position_track_interpolate(track_i, rest_animation_timestamp);
1550
skeleton->set_bone_rest(bone_idx, Transform3D(skeleton->get_bone_rest(bone_idx).basis, bone_position));
1551
} break;
1552
case Animation::TYPE_ROTATION_3D: {
1553
Quaternion bone_rotation = rest_animation->rotation_track_interpolate(track_i, rest_animation_timestamp);
1554
Transform3D current_rest = skeleton->get_bone_rest(bone_idx);
1555
skeleton->set_bone_rest(bone_idx, Transform3D(Basis(bone_rotation).scaled(current_rest.basis.get_scale()), current_rest.origin));
1556
} break;
1557
default:
1558
break;
1559
}
1560
}
1561
}
1562
}
1563
1564
ObjectID node_id = p_node->get_instance_id();
1565
for (int i = 0; i < post_importer_plugins.size(); i++) {
1566
post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, p_root, p_node, Ref<Resource>(), node_settings);
1567
if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue
1568
break;
1569
}
1570
}
1571
}
1572
1573
if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
1574
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
1575
1576
Ref<ImporterMesh> m = mi->get_mesh();
1577
1578
if (m.is_valid()) {
1579
if (!r_scanned_meshes.has(m)) {
1580
for (int i = 0; i < m->get_surface_count(); i++) {
1581
Ref<Material> mat = m->get_surface_material(i);
1582
if (mat.is_valid()) {
1583
String mat_id = mat->get_meta("import_id", mat->get_name());
1584
if (!mat_id.is_empty() && p_material_data.has(mat_id)) {
1585
Dictionary matdata = p_material_data[mat_id];
1586
{
1587
//fill node settings for this node with default values
1588
List<ImportOption> iopts;
1589
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MATERIAL, &iopts);
1590
for (const ImportOption &E : iopts) {
1591
if (!matdata.has(E.option.name)) {
1592
matdata[E.option.name] = E.default_value;
1593
}
1594
}
1595
}
1596
1597
for (int j = 0; j < post_importer_plugins.size(); j++) {
1598
post_importer_plugins.write[j]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL, p_root, p_node, mat, matdata);
1599
}
1600
}
1601
if (!mat_id.is_empty() && extract_mat != 0) {
1602
String ext = material_extension[p_options.has("materials/extract_format") ? (int)p_options["materials/extract_format"] : 0];
1603
String path = spath.path_join(mat_id.validate_filename() + ext);
1604
String uid_path = ResourceUID::path_to_uid(path);
1605
1606
Dictionary matdata = p_material_data[mat_id];
1607
matdata["use_external/enabled"] = true;
1608
matdata["use_external/path"] = uid_path;
1609
matdata["use_external/fallback_path"] = path;
1610
if (!FileAccess::exists(path) || extract_mat == 2 /*overwrite*/) {
1611
ResourceSaver::save(mat, path);
1612
}
1613
1614
Ref<Material> external_mat = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE);
1615
if (external_mat.is_valid()) {
1616
m->set_surface_material(i, external_mat);
1617
}
1618
}
1619
if (!mat_id.is_empty() && p_material_data.has(mat_id)) {
1620
Dictionary matdata = p_material_data[mat_id];
1621
if (matdata.has("use_external/enabled") && bool(matdata["use_external/enabled"]) && matdata.has("use_external/path")) {
1622
String path = matdata["use_external/path"];
1623
Ref<Material> external_mat = ResourceLoader::load(path);
1624
if (external_mat.is_null()) {
1625
if (matdata.has("use_external/fallback_path")) {
1626
String fallback_save_path = matdata["use_external/fallback_path"];
1627
if (!fallback_save_path.is_empty()) {
1628
external_mat = ResourceLoader::load(fallback_save_path);
1629
if (external_mat.is_valid()) {
1630
path = fallback_save_path;
1631
}
1632
}
1633
}
1634
}
1635
if (external_mat.is_valid()) {
1636
m->set_surface_material(i, external_mat);
1637
if (!path.begins_with("uid://")) {
1638
const ResourceUID::ID id = ResourceLoader::get_resource_uid(path);
1639
if (id != ResourceUID::INVALID_ID) {
1640
matdata["use_external/path"] = ResourceUID::get_singleton()->id_to_text(id);
1641
}
1642
}
1643
matdata["use_external/fallback_path"] = external_mat->get_path();
1644
}
1645
}
1646
}
1647
}
1648
}
1649
1650
r_scanned_meshes.insert(m);
1651
}
1652
1653
if (node_settings.has("generate/physics")) {
1654
int mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_DISABLED;
1655
1656
const bool generate_collider = node_settings["generate/physics"];
1657
if (generate_collider) {
1658
mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_MESH_AND_STATIC_COLLIDER;
1659
if (node_settings.has("physics/body_type")) {
1660
const BodyType body_type = (BodyType)node_settings["physics/body_type"].operator int();
1661
switch (body_type) {
1662
case BODY_TYPE_STATIC:
1663
mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_MESH_AND_STATIC_COLLIDER;
1664
break;
1665
case BODY_TYPE_DYNAMIC:
1666
mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_RIGID_BODY_AND_MESH;
1667
break;
1668
case BODY_TYPE_AREA:
1669
mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_AREA_ONLY;
1670
break;
1671
}
1672
}
1673
}
1674
1675
if (mesh_physics_mode != MeshPhysicsMode::MESH_PHYSICS_DISABLED) {
1676
Vector<Ref<Shape3D>> shapes;
1677
if (collision_map.has(m)) {
1678
shapes = collision_map[m];
1679
} else {
1680
shapes = get_collision_shapes(
1681
m,
1682
node_settings,
1683
p_applied_root_scale);
1684
}
1685
1686
if (shapes.size()) {
1687
CollisionObject3D *base = nullptr;
1688
switch (mesh_physics_mode) {
1689
case MESH_PHYSICS_MESH_AND_STATIC_COLLIDER: {
1690
StaticBody3D *col = memnew(StaticBody3D);
1691
p_node->add_child(col, true);
1692
col->set_owner(p_node->get_owner());
1693
col->set_transform(get_collision_shapes_transform(node_settings));
1694
col->set_position(p_applied_root_scale * col->get_position());
1695
const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
1696
if (pmo.is_valid()) {
1697
col->set_physics_material_override(pmo);
1698
}
1699
base = col;
1700
} break;
1701
case MESH_PHYSICS_RIGID_BODY_AND_MESH: {
1702
NodePath skeleton_path = mi->get_skeleton_path();
1703
Skeleton3D *skeleton = nullptr;
1704
if (!skeleton_path.is_empty()) {
1705
skeleton = Object::cast_to<Skeleton3D>(mi->get_node_or_null(skeleton_path));
1706
}
1707
RigidBody3D *rigid_body = memnew(RigidBody3D);
1708
rigid_body->set_name(p_node->get_name());
1709
_copy_meta(p_node, rigid_body);
1710
p_node->replace_by(rigid_body);
1711
rigid_body->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));
1712
rigid_body->set_position(p_applied_root_scale * rigid_body->get_position());
1713
p_node = rigid_body;
1714
mi->set_transform(Transform3D());
1715
rigid_body->add_child(mi, true);
1716
mi->set_owner(rigid_body->get_owner());
1717
const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
1718
if (pmo.is_valid()) {
1719
rigid_body->set_physics_material_override(pmo);
1720
}
1721
base = rigid_body;
1722
if (skeleton) {
1723
skeleton_path = mi->get_path_to(skeleton);
1724
mi->set_skeleton_path(skeleton_path);
1725
}
1726
} break;
1727
case MESH_PHYSICS_STATIC_COLLIDER_ONLY: {
1728
StaticBody3D *col = memnew(StaticBody3D);
1729
col->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));
1730
col->set_position(p_applied_root_scale * col->get_position());
1731
col->set_name(p_node->get_name());
1732
_copy_meta(p_node, col);
1733
p_node->replace_by(col);
1734
p_node->set_owner(nullptr);
1735
memdelete(p_node);
1736
p_node = col;
1737
const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
1738
if (pmo.is_valid()) {
1739
col->set_physics_material_override(pmo);
1740
}
1741
base = col;
1742
} break;
1743
case MESH_PHYSICS_AREA_ONLY: {
1744
Area3D *area = memnew(Area3D);
1745
area->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));
1746
area->set_position(p_applied_root_scale * area->get_position());
1747
area->set_name(p_node->get_name());
1748
_copy_meta(p_node, area);
1749
p_node->replace_by(area);
1750
p_node->set_owner(nullptr);
1751
memdelete(p_node);
1752
p_node = area;
1753
base = area;
1754
1755
} break;
1756
}
1757
1758
base->set_collision_layer(node_settings["physics/layer"]);
1759
base->set_collision_mask(node_settings["physics/mask"]);
1760
1761
for (const Ref<Shape3D> &E : shapes) {
1762
CollisionShape3D *cshape = memnew(CollisionShape3D);
1763
cshape->set_shape(E);
1764
base->add_child(cshape, true);
1765
1766
cshape->set_owner(base->get_owner());
1767
}
1768
}
1769
}
1770
}
1771
}
1772
}
1773
1774
//navmesh (node may have changed type above)
1775
if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
1776
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
1777
1778
Ref<ImporterMesh> m = mi->get_mesh();
1779
1780
if (m.is_valid()) {
1781
if (node_settings.has("generate/navmesh")) {
1782
int navmesh_mode = node_settings["generate/navmesh"];
1783
1784
if (navmesh_mode != NAVMESH_DISABLED) {
1785
NavigationRegion3D *nmi = memnew(NavigationRegion3D);
1786
1787
Ref<NavigationMesh> nmesh = m->create_navigation_mesh();
1788
nmi->set_navigation_mesh(nmesh);
1789
1790
if (navmesh_mode == NAVMESH_NAVMESH_ONLY) {
1791
nmi->set_transform(mi->get_transform());
1792
_copy_meta(p_node, nmi);
1793
p_node->replace_by(nmi);
1794
p_node->set_owner(nullptr);
1795
memdelete(p_node);
1796
p_node = nmi;
1797
} else {
1798
mi->add_child(nmi, true);
1799
nmi->set_owner(mi->get_owner());
1800
}
1801
}
1802
}
1803
}
1804
}
1805
1806
if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
1807
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
1808
1809
Ref<ImporterMesh> m = mi->get_mesh();
1810
1811
if (m.is_valid()) {
1812
if (node_settings.has("generate/occluder")) {
1813
int occluder_mode = node_settings["generate/occluder"];
1814
1815
if (occluder_mode != OCCLUDER_DISABLED) {
1816
float simplification_dist = 0.0f;
1817
if (node_settings.has("occluder/simplification_distance")) {
1818
simplification_dist = node_settings["occluder/simplification_distance"];
1819
}
1820
1821
OccluderInstance3D::bake_single_node(mi, simplification_dist, r_occluder_arrays.first, r_occluder_arrays.second);
1822
1823
if (occluder_mode == OCCLUDER_OCCLUDER_ONLY) {
1824
p_node->set_owner(nullptr);
1825
memdelete(p_node);
1826
p_node = nullptr;
1827
}
1828
}
1829
}
1830
}
1831
}
1832
1833
if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
1834
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
1835
1836
if (node_settings.has("mesh_instance/layers")) {
1837
mi->set_layer_mask(node_settings["mesh_instance/layers"]);
1838
}
1839
1840
if (node_settings.has("mesh_instance/visibility_range_begin")) {
1841
mi->set_visibility_range_begin(node_settings["mesh_instance/visibility_range_begin"]);
1842
}
1843
1844
if (node_settings.has("mesh_instance/visibility_range_begin_margin")) {
1845
mi->set_visibility_range_begin_margin(node_settings["mesh_instance/visibility_range_begin_margin"]);
1846
}
1847
1848
if (node_settings.has("mesh_instance/visibility_range_end")) {
1849
mi->set_visibility_range_end(node_settings["mesh_instance/visibility_range_end"]);
1850
}
1851
1852
if (node_settings.has("mesh_instance/visibility_range_end_margin")) {
1853
mi->set_visibility_range_end_margin(node_settings["mesh_instance/visibility_range_end_margin"]);
1854
}
1855
1856
if (node_settings.has("mesh_instance/visibility_range_fade_mode")) {
1857
const GeometryInstance3D::VisibilityRangeFadeMode range_fade_mode = (GeometryInstance3D::VisibilityRangeFadeMode)node_settings["mesh_instance/visibility_range_fade_mode"].operator int();
1858
mi->set_visibility_range_fade_mode(range_fade_mode);
1859
}
1860
1861
if (node_settings.has("mesh_instance/cast_shadow")) {
1862
const GeometryInstance3D::ShadowCastingSetting cast_shadows = (GeometryInstance3D::ShadowCastingSetting)node_settings["mesh_instance/cast_shadow"].operator int();
1863
mi->set_cast_shadows_setting(cast_shadows);
1864
}
1865
}
1866
1867
if (Object::cast_to<AnimationPlayer>(p_node)) {
1868
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
1869
1870
for (int i = 0; i < post_importer_plugins.size(); i++) {
1871
post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, p_root, p_node, Ref<Resource>(), node_settings);
1872
}
1873
1874
if (post_importer_plugins.size()) {
1875
List<StringName> anims;
1876
ap->get_animation_list(&anims);
1877
for (const StringName &name : anims) {
1878
if (p_animation_data.has(name)) {
1879
Ref<Animation> anim = ap->get_animation(name);
1880
Dictionary anim_settings = p_animation_data[name];
1881
{
1882
//fill with default values
1883
List<ImportOption> iopts;
1884
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION, &iopts);
1885
for (const ImportOption &F : iopts) {
1886
if (!anim_settings.has(F.option.name)) {
1887
anim_settings[F.option.name] = F.default_value;
1888
}
1889
}
1890
}
1891
1892
for (int i = 0; i < post_importer_plugins.size(); i++) {
1893
post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION, p_root, p_node, anim, anim_settings);
1894
}
1895
}
1896
}
1897
}
1898
}
1899
1900
String node_type = node_settings.get("node/node_type", "");
1901
Ref<Script> node_script = node_settings.get("node/script", Ref<Script>());
1902
p_node = _replace_node_with_type_and_script(p_node, node_type, node_script);
1903
1904
return p_node;
1905
}
1906
1907
Ref<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) {
1908
String res_path = ResourceUID::ensure_path(p_save_to_path);
1909
if (!p_save_to_file || !res_path.is_resource_file()) {
1910
return anim;
1911
}
1912
1913
if (FileAccess::exists(res_path) && p_keep_custom_tracks) {
1914
// Copy custom animation tracks from previously imported files.
1915
Ref<Animation> old_anim = ResourceLoader::load(res_path, "Animation", ResourceFormatLoader::CACHE_MODE_IGNORE);
1916
if (old_anim.is_valid()) {
1917
for (int i = 0; i < old_anim->get_track_count(); i++) {
1918
if (!old_anim->track_is_imported(i)) {
1919
old_anim->copy_track(i, anim);
1920
}
1921
}
1922
anim->set_loop_mode(old_anim->get_loop_mode());
1923
}
1924
}
1925
1926
if (ResourceCache::has(res_path)) {
1927
Ref<Animation> old_anim = ResourceCache::get_ref(res_path);
1928
if (old_anim.is_valid()) {
1929
old_anim->copy_from(anim);
1930
anim = old_anim;
1931
}
1932
}
1933
anim->set_path(res_path, true); // Set path to save externally.
1934
Error err = ResourceSaver::save(anim, res_path, ResourceSaver::FLAG_CHANGE_PATH);
1935
1936
ERR_FAIL_COND_V_MSG(err != OK, anim, "Saving of animation failed: " + res_path);
1937
if (p_save_to_path.begins_with("uid://")) {
1938
// slow
1939
ResourceSaver::set_uid(res_path, ResourceUID::get_singleton()->text_to_id(p_save_to_path));
1940
}
1941
return anim;
1942
}
1943
1944
void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref<Animation> anim, const Array &p_slices, bool p_bake_all) {
1945
Ref<AnimationLibrary> al = ap->get_animation_library(ap->find_animation_library(anim));
1946
1947
for (int i = 0; i < p_slices.size(); i += 7) {
1948
String name = p_slices[i];
1949
float from = p_slices[i + 1];
1950
float to = p_slices[i + 2];
1951
Animation::LoopMode loop_mode = static_cast<Animation::LoopMode>((int)p_slices[i + 3]);
1952
bool save_to_file = p_slices[i + 4];
1953
String save_to_path = p_slices[i + 5];
1954
bool keep_current = p_slices[i + 6];
1955
if (from >= to) {
1956
continue;
1957
}
1958
1959
Ref<Animation> new_anim = memnew(Animation);
1960
1961
for (int j = 0; j < anim->get_track_count(); j++) {
1962
List<float> keys;
1963
int kc = anim->track_get_key_count(j);
1964
int dtrack = -1;
1965
for (int k = 0; k < kc; k++) {
1966
float kt = anim->track_get_key_time(j, k);
1967
if (kt >= from && kt < to) {
1968
//found a key within range, so create track
1969
if (dtrack == -1) {
1970
new_anim->add_track(anim->track_get_type(j));
1971
dtrack = new_anim->get_track_count() - 1;
1972
new_anim->track_set_path(dtrack, anim->track_get_path(j));
1973
new_anim->track_set_imported(dtrack, true);
1974
1975
if (kt > (from + 0.01) && k > 0) {
1976
if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
1977
Vector3 p;
1978
anim->try_position_track_interpolate(j, from, &p);
1979
new_anim->position_track_insert_key(dtrack, 0, p);
1980
} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
1981
Quaternion r;
1982
anim->try_rotation_track_interpolate(j, from, &r);
1983
new_anim->rotation_track_insert_key(dtrack, 0, r);
1984
} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
1985
Vector3 s;
1986
anim->try_scale_track_interpolate(j, from, &s);
1987
new_anim->scale_track_insert_key(dtrack, 0, s);
1988
} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {
1989
Variant var = anim->value_track_interpolate(j, from);
1990
new_anim->track_insert_key(dtrack, 0, var);
1991
} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
1992
float interp;
1993
anim->try_blend_shape_track_interpolate(j, from, &interp);
1994
new_anim->blend_shape_track_insert_key(dtrack, 0, interp);
1995
}
1996
}
1997
}
1998
1999
if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
2000
Vector3 p;
2001
anim->position_track_get_key(j, k, &p);
2002
new_anim->position_track_insert_key(dtrack, kt - from, p);
2003
} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
2004
Quaternion r;
2005
anim->rotation_track_get_key(j, k, &r);
2006
new_anim->rotation_track_insert_key(dtrack, kt - from, r);
2007
} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
2008
Vector3 s;
2009
anim->scale_track_get_key(j, k, &s);
2010
new_anim->scale_track_insert_key(dtrack, kt - from, s);
2011
} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {
2012
Variant var = anim->track_get_key_value(j, k);
2013
new_anim->track_insert_key(dtrack, kt - from, var);
2014
} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
2015
float interp;
2016
anim->blend_shape_track_get_key(j, k, &interp);
2017
new_anim->blend_shape_track_insert_key(dtrack, kt - from, interp);
2018
}
2019
}
2020
2021
if (dtrack != -1 && kt >= to) {
2022
if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
2023
Vector3 p;
2024
anim->try_position_track_interpolate(j, to, &p);
2025
new_anim->position_track_insert_key(dtrack, to - from, p);
2026
} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
2027
Quaternion r;
2028
anim->try_rotation_track_interpolate(j, to, &r);
2029
new_anim->rotation_track_insert_key(dtrack, to - from, r);
2030
} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
2031
Vector3 s;
2032
anim->try_scale_track_interpolate(j, to, &s);
2033
new_anim->scale_track_insert_key(dtrack, to - from, s);
2034
} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {
2035
Variant var = anim->value_track_interpolate(j, to);
2036
new_anim->track_insert_key(dtrack, to - from, var);
2037
} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
2038
float interp;
2039
anim->try_blend_shape_track_interpolate(j, to, &interp);
2040
new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);
2041
}
2042
}
2043
}
2044
2045
if (dtrack == -1 && p_bake_all) {
2046
new_anim->add_track(anim->track_get_type(j));
2047
dtrack = new_anim->get_track_count() - 1;
2048
new_anim->track_set_path(dtrack, anim->track_get_path(j));
2049
new_anim->track_set_imported(dtrack, true);
2050
if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
2051
Vector3 p;
2052
anim->try_position_track_interpolate(j, from, &p);
2053
new_anim->position_track_insert_key(dtrack, 0, p);
2054
anim->try_position_track_interpolate(j, to, &p);
2055
new_anim->position_track_insert_key(dtrack, to - from, p);
2056
} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
2057
Quaternion r;
2058
anim->try_rotation_track_interpolate(j, from, &r);
2059
new_anim->rotation_track_insert_key(dtrack, 0, r);
2060
anim->try_rotation_track_interpolate(j, to, &r);
2061
new_anim->rotation_track_insert_key(dtrack, to - from, r);
2062
} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
2063
Vector3 s;
2064
anim->try_scale_track_interpolate(j, from, &s);
2065
new_anim->scale_track_insert_key(dtrack, 0, s);
2066
anim->try_scale_track_interpolate(j, to, &s);
2067
new_anim->scale_track_insert_key(dtrack, to - from, s);
2068
} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {
2069
Variant var = anim->value_track_interpolate(j, from);
2070
new_anim->track_insert_key(dtrack, 0, var);
2071
Variant to_var = anim->value_track_interpolate(j, to);
2072
new_anim->track_insert_key(dtrack, to - from, to_var);
2073
} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
2074
float interp;
2075
anim->try_blend_shape_track_interpolate(j, from, &interp);
2076
new_anim->blend_shape_track_insert_key(dtrack, 0, interp);
2077
anim->try_blend_shape_track_interpolate(j, to, &interp);
2078
new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);
2079
}
2080
}
2081
}
2082
2083
new_anim->set_name(name);
2084
new_anim->set_loop_mode(loop_mode);
2085
new_anim->set_length(to - from);
2086
new_anim->set_step(anim->get_step());
2087
2088
al->add_animation(name, new_anim);
2089
2090
Ref<Animation> saved_anim = _save_animation_to_file(new_anim, save_to_file, save_to_path, keep_current);
2091
if (saved_anim != new_anim) {
2092
al->add_animation(name, saved_anim);
2093
}
2094
}
2095
2096
al->remove_animation(ap->find_animation(anim)); // Remove original animation (no longer needed).
2097
}
2098
2099
void ResourceImporterScene::_optimize_animations(AnimationPlayer *anim, float p_max_vel_error, float p_max_ang_error, int p_prc_error) {
2100
List<StringName> anim_names;
2101
anim->get_animation_list(&anim_names);
2102
for (const StringName &E : anim_names) {
2103
Ref<Animation> a = anim->get_animation(E);
2104
a->optimize(p_max_vel_error, p_max_ang_error, p_prc_error);
2105
}
2106
}
2107
2108
void ResourceImporterScene::_compress_animations(AnimationPlayer *anim, int p_page_size_kb) {
2109
List<StringName> anim_names;
2110
anim->get_animation_list(&anim_names);
2111
for (const StringName &E : anim_names) {
2112
Ref<Animation> a = anim->get_animation(E);
2113
a->compress(p_page_size_kb * 1024);
2114
}
2115
}
2116
2117
void ResourceImporterScene::get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const {
2118
switch (p_category) {
2119
case INTERNAL_IMPORT_CATEGORY_NODE: {
2120
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "node/node_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));
2121
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static()), Variant()));
2122
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
2123
} break;
2124
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
2125
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static()), Variant()));
2126
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
2127
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate/physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
2128
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));
2129
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/body_type", PROPERTY_HINT_ENUM, "StaticBody3D,RigidBody3D,Area3D"), 0));
2130
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/shape_type", PROPERTY_HINT_ENUM, "Decompose Convex (Slow),Single Convex (Average),Trimesh (Slow),Box (Fast),Sphere (Fast),Cylinder (Average),Capsule (Fast),Automatic", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 7));
2131
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "physics/physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, PhysicsMaterial::get_class_static()), Variant()));
2132
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1));
2133
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1));
2134
2135
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mesh_instance/layers", PROPERTY_HINT_LAYERS_3D_RENDER), 1));
2136
r_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));
2137
r_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));
2138
r_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));
2139
r_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));
2140
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mesh_instance/visibility_range_fade_mode", PROPERTY_HINT_ENUM, "Disabled,Self,Dependencies"), GeometryInstance3D::VISIBILITY_RANGE_FADE_DISABLED));
2141
r_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));
2142
2143
// Decomposition
2144
Ref<MeshConvexDecompositionSettings> decomposition_default = Ref<MeshConvexDecompositionSettings>();
2145
decomposition_default.instantiate();
2146
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/advanced", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
2147
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/precision", PROPERTY_HINT_RANGE, "1,10,1"), 5));
2148
r_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()));
2149
r_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()));
2150
r_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()));
2151
r_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()));
2152
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/resolution", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_resolution()));
2153
r_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()));
2154
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/plane_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_plane_downsampling()));
2155
r_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()));
2156
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/normalize_mesh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_normalize_mesh()));
2157
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/mode", PROPERTY_HINT_ENUM, "Voxel,Tetrahedron", PROPERTY_USAGE_DEFAULT), static_cast<int>(decomposition_default->get_mode())));
2158
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/convexhull_approximation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_convex_hull_approximation()));
2159
r_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()));
2160
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_project_hull_vertices()));
2161
2162
// Primitives: Box, Sphere, Cylinder, Capsule.
2163
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3(2.0, 2.0, 2.0)));
2164
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/height", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), 1.0));
2165
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/radius", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), 1.0));
2166
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3()));
2167
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3()));
2168
2169
r_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));
2170
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "occluder/simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01", PROPERTY_USAGE_DEFAULT), 0.1f));
2171
} break;
2172
case INTERNAL_IMPORT_CATEGORY_MESH: {
2173
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
2174
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.tres"), ""));
2175
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));
2176
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/shadow_meshes", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
2177
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lightmap_uv", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
2178
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
2179
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_merge_angle", PROPERTY_HINT_RANGE, "0,180,1,degrees"), 20.0f));
2180
} break;
2181
case INTERNAL_IMPORT_CATEGORY_MATERIAL: {
2182
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "use_external/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
2183
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "use_external/path", PROPERTY_HINT_FILE, "*.material,*.res,*.tres"), ""));
2184
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "use_external/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));
2185
} break;
2186
case INTERNAL_IMPORT_CATEGORY_ANIMATION: {
2187
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "settings/loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Pingpong"), 0));
2188
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
2189
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.anim,*.tres"), ""));
2190
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));
2191
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/keep_custom_tracks"), ""));
2192
r_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));
2193
2194
for (int i = 0; i < 256; i++) {
2195
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/name"), ""));
2196
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/start_frame"), 0));
2197
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/end_frame"), 0));
2198
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Pingpong"), 0));
2199
r_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));
2200
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.anim,*.tres"), ""));
2201
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/save_to_file/fallback_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), ""));
2202
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"), false));
2203
}
2204
} break;
2205
case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
2206
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static()), Variant()));
2207
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
2208
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
2209
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_velocity_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));
2210
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));
2211
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "optimizer/max_precision_error", PROPERTY_HINT_NONE, "1,6,1"), 3));
2212
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compression/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
2213
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compression/page_size", PROPERTY_HINT_RANGE, "4,512,1,suffix:kb"), 8));
2214
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
2215
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
2216
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
2217
} break;
2218
case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {
2219
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "node/script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static()), Variant()));
2220
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
2221
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "rest_pose/load_pose", PROPERTY_HINT_ENUM, "Default Pose,Use AnimationPlayer,Load External Animation", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
2222
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "rest_pose/external_animation_library", PROPERTY_HINT_RESOURCE_TYPE, "Animation,AnimationLibrary", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Variant()));
2223
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "rest_pose/selected_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), ""));
2224
r_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));
2225
String mismatched_or_empty_profile_warning = String(
2226
"The external rest animation is missing some bones. "
2227
"Consider disabling Remove Immutable Tracks on the other file."); // TODO: translate.
2228
r_options->push_back(ImportOption(
2229
PropertyInfo(
2230
Variant::STRING, U"rest_pose/\u26A0_validation_warning/mismatched_or_empty_profile",
2231
PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),
2232
Variant(mismatched_or_empty_profile_warning)));
2233
String profile_must_not_be_retargeted_warning = String(
2234
"This external rest animation appears to have been imported with a BoneMap. "
2235
"Disable the bone map when exporting a rest animation from the reference model."); // TODO: translate.
2236
r_options->push_back(ImportOption(
2237
PropertyInfo(
2238
Variant::STRING, U"rest_pose/\u26A0_validation_warning/profile_must_not_be_retargeted",
2239
PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),
2240
Variant(profile_must_not_be_retargeted_warning)));
2241
String no_animation_warning = String(
2242
"Select an animation: Find a FBX or glTF in a compatible rest pose "
2243
"and export a compatible animation from its import settings."); // TODO: translate.
2244
r_options->push_back(ImportOption(
2245
PropertyInfo(
2246
Variant::STRING, U"rest_pose//no_animation_chosen",
2247
PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),
2248
Variant(no_animation_warning)));
2249
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "retarget/bone_map", PROPERTY_HINT_RESOURCE_TYPE, BoneMap::get_class_static(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Variant()));
2250
} break;
2251
default: {
2252
}
2253
}
2254
2255
for (int i = 0; i < post_importer_plugins.size(); i++) {
2256
post_importer_plugins.write[i]->get_internal_import_options(EditorScenePostImportPlugin::InternalImportCategory(p_category), r_options);
2257
}
2258
}
2259
2260
bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
2261
if (p_options.has("import/skip_import") && p_option != "import/skip_import" && bool(p_options["import/skip_import"])) {
2262
return false; //if skip import
2263
}
2264
switch (p_category) {
2265
case INTERNAL_IMPORT_CATEGORY_NODE: {
2266
} break;
2267
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
2268
const bool generate_physics =
2269
p_options.has("generate/physics") &&
2270
p_options["generate/physics"].operator bool();
2271
2272
if (p_option.contains("physics/")) {
2273
// Show if need to generate collisions.
2274
return generate_physics;
2275
}
2276
2277
if (p_option.contains("decomposition/")) {
2278
// Show if need to generate collisions.
2279
if (generate_physics &&
2280
// Show if convex is enabled.
2281
p_options["physics/shape_type"] == Variant(SHAPE_TYPE_DECOMPOSE_CONVEX)) {
2282
if (p_option == "decomposition/advanced") {
2283
return true;
2284
}
2285
2286
const bool decomposition_advanced =
2287
p_options.has("decomposition/advanced") &&
2288
p_options["decomposition/advanced"].operator bool();
2289
2290
if (p_option == "decomposition/precision") {
2291
return !decomposition_advanced;
2292
} else {
2293
return decomposition_advanced;
2294
}
2295
}
2296
2297
return false;
2298
}
2299
2300
if (p_option == "primitive/position" || p_option == "primitive/rotation") {
2301
const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
2302
return generate_physics &&
2303
physics_shape >= SHAPE_TYPE_BOX;
2304
}
2305
2306
if (p_option == "primitive/size") {
2307
const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
2308
return generate_physics &&
2309
physics_shape == SHAPE_TYPE_BOX;
2310
}
2311
2312
if (p_option == "primitive/radius") {
2313
const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
2314
return generate_physics &&
2315
(physics_shape == SHAPE_TYPE_SPHERE ||
2316
physics_shape == SHAPE_TYPE_CYLINDER ||
2317
physics_shape == SHAPE_TYPE_CAPSULE);
2318
}
2319
2320
if (p_option == "primitive/height") {
2321
const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
2322
return generate_physics &&
2323
(physics_shape == SHAPE_TYPE_CYLINDER ||
2324
physics_shape == SHAPE_TYPE_CAPSULE);
2325
}
2326
2327
if (p_option == "occluder/simplification_distance") {
2328
// Show only if occluder generation is enabled
2329
return p_options.has("generate/occluder") && p_options["generate/occluder"].operator signed int() != OCCLUDER_DISABLED;
2330
}
2331
} break;
2332
case INTERNAL_IMPORT_CATEGORY_MESH: {
2333
if (p_option == "save_to_file/path") {
2334
return p_options["save_to_file/enabled"];
2335
}
2336
} break;
2337
case INTERNAL_IMPORT_CATEGORY_MATERIAL: {
2338
if (p_option == "use_external/path") {
2339
return p_options["use_external/enabled"];
2340
}
2341
} break;
2342
case INTERNAL_IMPORT_CATEGORY_ANIMATION: {
2343
if (p_option == "save_to_file/path" || p_option == "save_to_file/keep_custom_tracks") {
2344
return p_options["save_to_file/enabled"];
2345
}
2346
if (p_option.begins_with("slice_")) {
2347
int max_slice = p_options["slices/amount"];
2348
int slice = p_option.get_slicec('_', 1).to_int() - 1;
2349
if (slice >= max_slice) {
2350
return false;
2351
}
2352
}
2353
} break;
2354
case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
2355
if (p_option.begins_with("optimizer/") && p_option != "optimizer/enabled" && !bool(p_options["optimizer/enabled"])) {
2356
return false;
2357
}
2358
if (p_option.begins_with("compression/") && p_option != "compression/enabled" && !bool(p_options["compression/enabled"])) {
2359
return false;
2360
}
2361
} break;
2362
case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {
2363
const bool use_retarget = Object::cast_to<BoneMap>(p_options["retarget/bone_map"].get_validated_object()) != nullptr;
2364
if (!use_retarget && p_option != "retarget/bone_map" && p_option.begins_with("retarget/")) {
2365
return false;
2366
}
2367
int rest_warning = 0;
2368
if (p_option.begins_with("rest_pose/")) {
2369
if (!p_options.has("rest_pose/load_pose") || int(p_options["rest_pose/load_pose"]) == 0) {
2370
if (p_option != "rest_pose/load_pose") {
2371
return false;
2372
}
2373
} else if (int(p_options["rest_pose/load_pose"]) == 1) {
2374
if (p_option == "rest_pose/external_animation_library") {
2375
return false;
2376
}
2377
} else if (int(p_options["rest_pose/load_pose"]) == 2) {
2378
Object *res = p_options["rest_pose/external_animation_library"];
2379
Ref<Animation> anim(res);
2380
if (anim.is_valid() && p_option == "rest_pose/selected_animation") {
2381
return false;
2382
}
2383
Ref<AnimationLibrary> library(res);
2384
String selected_animation_name = p_options["rest_pose/selected_animation"];
2385
if (library.is_valid()) {
2386
List<StringName> anim_list;
2387
library->get_animation_list(&anim_list);
2388
if (anim_list.size() == 1) {
2389
selected_animation_name = String(anim_list.front()->get());
2390
}
2391
if (library->has_animation(selected_animation_name)) {
2392
anim = library->get_animation(selected_animation_name);
2393
}
2394
}
2395
int found_bone_count = 0;
2396
Ref<BoneMap> bone_map;
2397
Ref<SkeletonProfile> prof;
2398
if (p_options.has("retarget/bone_map")) {
2399
bone_map = p_options["retarget/bone_map"];
2400
}
2401
if (bone_map.is_valid()) {
2402
prof = bone_map->get_profile();
2403
}
2404
if (anim.is_valid()) {
2405
HashSet<StringName> target_bones;
2406
if (bone_map.is_valid() && prof.is_valid()) {
2407
for (int target_i = 0; target_i < prof->get_bone_size(); target_i++) {
2408
StringName skeleton_bone_name = bone_map->get_skeleton_bone_name(prof->get_bone_name(target_i));
2409
if (skeleton_bone_name) {
2410
target_bones.insert(skeleton_bone_name);
2411
}
2412
}
2413
}
2414
for (int track_i = 0; track_i < anim->get_track_count(); track_i++) {
2415
if (anim->track_get_type(track_i) != Animation::TYPE_POSITION_3D && anim->track_get_type(track_i) != Animation::TYPE_ROTATION_3D) {
2416
continue;
2417
}
2418
NodePath path = anim->track_get_path(track_i);
2419
StringName node_path = path.get_concatenated_names();
2420
StringName skeleton_bone = path.get_concatenated_subnames();
2421
if (skeleton_bone) {
2422
if (String(node_path).begins_with("%")) {
2423
rest_warning = 1;
2424
}
2425
if (target_bones.has(skeleton_bone)) {
2426
target_bones.erase(skeleton_bone);
2427
}
2428
found_bone_count++;
2429
}
2430
}
2431
if ((found_bone_count < 15 || !target_bones.is_empty()) && rest_warning != 1) {
2432
rest_warning = 2; // heuristic: animation targeted too few bones.
2433
}
2434
} else {
2435
rest_warning = 3;
2436
}
2437
}
2438
if (p_option.begins_with("rest_pose/") && p_option.ends_with("profile_must_not_be_retargeted")) {
2439
return rest_warning == 1;
2440
}
2441
if (p_option.begins_with("rest_pose/") && p_option.ends_with("mismatched_or_empty_profile")) {
2442
return rest_warning == 2;
2443
}
2444
if (p_option.begins_with("rest_pose/") && p_option.ends_with("no_animation_chosen")) {
2445
return rest_warning == 3;
2446
}
2447
}
2448
} break;
2449
default: {
2450
}
2451
}
2452
2453
// TODO: If there are more than 2 or equal get_internal_option_visibility method, visibility state is broken.
2454
for (int i = 0; i < post_importer_plugins.size(); i++) {
2455
Variant ret = post_importer_plugins.write[i]->get_internal_option_visibility(EditorScenePostImportPlugin::InternalImportCategory(p_category), _scene_import_type, p_option, p_options);
2456
if (ret.get_type() == Variant::BOOL) {
2457
return ret;
2458
}
2459
}
2460
2461
return true;
2462
}
2463
2464
bool ResourceImporterScene::get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
2465
switch (p_category) {
2466
case INTERNAL_IMPORT_CATEGORY_NODE: {
2467
} break;
2468
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
2469
if (
2470
p_option == "generate/physics" ||
2471
p_option == "physics/shape_type" ||
2472
p_option.contains("decomposition/") ||
2473
p_option.contains("primitive/")) {
2474
return true;
2475
}
2476
} break;
2477
case INTERNAL_IMPORT_CATEGORY_MESH: {
2478
} break;
2479
case INTERNAL_IMPORT_CATEGORY_MATERIAL: {
2480
} break;
2481
case INTERNAL_IMPORT_CATEGORY_ANIMATION: {
2482
} break;
2483
case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
2484
} break;
2485
case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {
2486
} break;
2487
default: {
2488
}
2489
}
2490
2491
for (int i = 0; i < post_importer_plugins.size(); i++) {
2492
Variant ret = post_importer_plugins.write[i]->get_internal_option_update_view_required(EditorScenePostImportPlugin::InternalImportCategory(p_category), p_option, p_options);
2493
if (ret.get_type() == Variant::BOOL) {
2494
return ret;
2495
}
2496
}
2497
2498
return false;
2499
}
2500
2501
void ResourceImporterScene::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
2502
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));
2503
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_name"), ""));
2504
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "nodes/root_script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static()), Variant()));
2505
2506
List<String> script_extensions;
2507
ResourceLoader::get_recognized_extensions_for_type("Script", &script_extensions);
2508
2509
String script_ext_hint;
2510
2511
for (const String &E : script_extensions) {
2512
if (!script_ext_hint.is_empty()) {
2513
script_ext_hint += ",";
2514
}
2515
script_ext_hint += "*." + E;
2516
}
2517
bool trimming_defaults_on = p_path.has_extension("fbx");
2518
2519
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true));
2520
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0));
2521
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/import_as_skeleton_bones"), false));
2522
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/use_name_suffixes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
2523
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/use_node_type_suffixes"), true));
2524
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));
2525
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true));
2526
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));
2527
r_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));
2528
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2));
2529
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/force_disable_compression"), false));
2530
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
2531
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
2532
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));
2533
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), trimming_defaults_on));
2534
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true));
2535
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import_rest_as_RESET"), false));
2536
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
2537
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/extract", PROPERTY_HINT_ENUM, "Keep Internal,Extract Once,Extract and Overwrite"), 0));
2538
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/extract_format", PROPERTY_HINT_ENUM, "Text (*.tres),Binary (*.res),Material (*.material)"), 0));
2539
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "materials/extract_path", PROPERTY_HINT_DIR, ""), ""));
2540
2541
r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Dictionary()));
2542
2543
for (int i = 0; i < post_importer_plugins.size(); i++) {
2544
post_importer_plugins.write[i]->get_import_options(p_path, r_options);
2545
}
2546
2547
for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {
2548
importer_elem->get_import_options(p_path, r_options);
2549
}
2550
}
2551
2552
void ResourceImporterScene::handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {
2553
for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {
2554
importer_elem->handle_compatibility_options(p_import_params);
2555
}
2556
}
2557
2558
void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner) {
2559
if (p_node != p_new_owner && p_node->get_owner() == p_scene) {
2560
p_node->set_owner(p_new_owner);
2561
}
2562
2563
for (int i = 0; i < p_node->get_child_count(); i++) {
2564
Node *n = p_node->get_child(i);
2565
_replace_owner(n, p_scene, p_new_owner);
2566
}
2567
}
2568
2569
Array ResourceImporterScene::_get_skinned_pose_transforms(ImporterMeshInstance3D *p_src_mesh_node) {
2570
Array skin_pose_transform_array;
2571
2572
const Ref<Skin> skin = p_src_mesh_node->get_skin();
2573
if (skin.is_valid()) {
2574
NodePath skeleton_path = p_src_mesh_node->get_skeleton_path();
2575
const Node *node = p_src_mesh_node->get_node_or_null(skeleton_path);
2576
const Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
2577
if (skeleton) {
2578
int bind_count = skin->get_bind_count();
2579
2580
for (int i = 0; i < bind_count; i++) {
2581
Transform3D bind_pose = skin->get_bind_pose(i);
2582
String bind_name = skin->get_bind_name(i);
2583
2584
int bone_idx = bind_name.is_empty() ? skin->get_bind_bone(i) : skeleton->find_bone(bind_name);
2585
ERR_FAIL_COND_V(bone_idx >= skeleton->get_bone_count(), Array());
2586
2587
Transform3D bp_global_rest;
2588
if (bone_idx >= 0) {
2589
bp_global_rest = skeleton->get_bone_global_pose(bone_idx);
2590
} else {
2591
bp_global_rest = skeleton->get_bone_global_pose(i);
2592
}
2593
2594
skin_pose_transform_array.push_back(bp_global_rest * bind_pose);
2595
}
2596
}
2597
}
2598
2599
return skin_pose_transform_array;
2600
}
2601
2602
Node *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) {
2603
ImporterMeshInstance3D *src_mesh_node = Object::cast_to<ImporterMeshInstance3D>(p_node);
2604
if (src_mesh_node) {
2605
//is mesh
2606
MeshInstance3D *mesh_node = memnew(MeshInstance3D);
2607
mesh_node->set_name(src_mesh_node->get_name());
2608
mesh_node->set_transform(src_mesh_node->get_transform());
2609
mesh_node->set_skin(src_mesh_node->get_skin());
2610
mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());
2611
mesh_node->merge_meta_from(src_mesh_node);
2612
2613
Ref<ImporterMesh> importer_mesh = src_mesh_node->get_mesh();
2614
if (importer_mesh.is_valid()) {
2615
Ref<ArrayMesh> mesh;
2616
if (!importer_mesh->has_mesh()) {
2617
//do mesh processing
2618
2619
bool generate_lods = p_generate_lods;
2620
float merge_angle = 20.0f;
2621
bool create_shadow_meshes = p_create_shadow_meshes;
2622
bool bake_lightmaps = p_light_bake_mode == LIGHT_BAKE_STATIC_LIGHTMAPS;
2623
String save_to_file;
2624
2625
String mesh_id = importer_mesh->get_meta("import_id", importer_mesh->get_name());
2626
2627
if (!mesh_id.is_empty() && p_mesh_data.has(mesh_id)) {
2628
Dictionary mesh_settings = p_mesh_data[mesh_id];
2629
{
2630
//fill node settings for this node with default values
2631
List<ImportOption> iopts;
2632
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MESH, &iopts);
2633
for (const ImportOption &E : iopts) {
2634
if (!mesh_settings.has(E.option.name)) {
2635
mesh_settings[E.option.name] = E.default_value;
2636
}
2637
}
2638
}
2639
2640
if (mesh_settings.has("generate/shadow_meshes")) {
2641
int shadow_meshes = mesh_settings["generate/shadow_meshes"];
2642
if (shadow_meshes == MESH_OVERRIDE_ENABLE) {
2643
create_shadow_meshes = true;
2644
} else if (shadow_meshes == MESH_OVERRIDE_DISABLE) {
2645
create_shadow_meshes = false;
2646
}
2647
}
2648
2649
if (mesh_settings.has("generate/lightmap_uv")) {
2650
int lightmap_uv = mesh_settings["generate/lightmap_uv"];
2651
if (lightmap_uv == MESH_OVERRIDE_ENABLE) {
2652
bake_lightmaps = true;
2653
} else if (lightmap_uv == MESH_OVERRIDE_DISABLE) {
2654
bake_lightmaps = false;
2655
}
2656
}
2657
2658
if (mesh_settings.has("generate/lods")) {
2659
int lods = mesh_settings["generate/lods"];
2660
if (lods == MESH_OVERRIDE_ENABLE) {
2661
generate_lods = true;
2662
} else if (lods == MESH_OVERRIDE_DISABLE) {
2663
generate_lods = false;
2664
}
2665
}
2666
2667
if (mesh_settings.has("lods/normal_merge_angle")) {
2668
merge_angle = mesh_settings["lods/normal_merge_angle"];
2669
}
2670
2671
if (bool(mesh_settings.get("save_to_file/enabled", false))) {
2672
save_to_file = mesh_settings.get("save_to_file/path", String());
2673
if (!ResourceUID::ensure_path(save_to_file).is_resource_file()) {
2674
save_to_file = "";
2675
}
2676
}
2677
2678
for (int i = 0; i < post_importer_plugins.size(); i++) {
2679
post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH, nullptr, src_mesh_node, importer_mesh, mesh_settings);
2680
}
2681
}
2682
2683
if (bake_lightmaps) {
2684
Transform3D xf;
2685
Node3D *n = src_mesh_node;
2686
while (n) {
2687
xf = n->get_transform() * xf;
2688
n = n->get_parent_node_3d();
2689
}
2690
2691
Vector<uint8_t> lightmap_cache;
2692
importer_mesh->lightmap_unwrap_cached(xf, p_lightmap_texel_size, p_src_lightmap_cache, lightmap_cache);
2693
2694
if (!lightmap_cache.is_empty()) {
2695
if (r_lightmap_caches.is_empty()) {
2696
r_lightmap_caches.push_back(lightmap_cache);
2697
} else {
2698
String new_md5 = String::md5(lightmap_cache.ptr()); // MD5 is stored at the beginning of the cache data
2699
2700
for (int i = 0; i < r_lightmap_caches.size(); i++) {
2701
String md5 = String::md5(r_lightmap_caches[i].ptr());
2702
if (new_md5 < md5) {
2703
r_lightmap_caches.insert(i, lightmap_cache);
2704
break;
2705
}
2706
2707
if (new_md5 == md5) {
2708
break;
2709
}
2710
}
2711
}
2712
}
2713
}
2714
2715
if (generate_lods) {
2716
Array skin_pose_transform_array = _get_skinned_pose_transforms(src_mesh_node);
2717
importer_mesh->generate_lods(merge_angle, skin_pose_transform_array);
2718
}
2719
2720
if (create_shadow_meshes) {
2721
importer_mesh->create_shadow_mesh();
2722
}
2723
2724
importer_mesh->optimize_indices();
2725
2726
if (!save_to_file.is_empty()) {
2727
String save_res_path = ResourceUID::ensure_path(save_to_file);
2728
Ref<Mesh> existing = ResourceCache::get_ref(save_res_path);
2729
if (existing.is_valid()) {
2730
//if somehow an existing one is useful, create
2731
existing->reset_state();
2732
}
2733
mesh = importer_mesh->get_mesh(existing);
2734
2735
Error err = ResourceSaver::save(mesh, save_res_path); //override
2736
if (err != OK) {
2737
WARN_PRINT(vformat("Failed to save mesh %s to '%s'.", mesh->get_name(), save_res_path));
2738
}
2739
if (err == OK && save_to_file.begins_with("uid://")) {
2740
// slow
2741
ResourceSaver::set_uid(save_res_path, ResourceUID::get_singleton()->text_to_id(save_to_file));
2742
}
2743
2744
mesh->set_path(save_res_path, true); //takeover existing, if needed
2745
2746
} else {
2747
mesh = importer_mesh->get_mesh();
2748
}
2749
} else {
2750
mesh = importer_mesh->get_mesh();
2751
}
2752
2753
if (mesh.is_valid()) {
2754
_copy_meta(importer_mesh.ptr(), mesh.ptr());
2755
mesh_node->set_mesh(mesh);
2756
for (int i = 0; i < mesh->get_surface_count(); i++) {
2757
mesh_node->set_surface_override_material(i, src_mesh_node->get_surface_material(i));
2758
}
2759
mesh->merge_meta_from(*importer_mesh);
2760
}
2761
}
2762
2763
switch (p_light_bake_mode) {
2764
case LIGHT_BAKE_DISABLED: {
2765
mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_DISABLED);
2766
} break;
2767
case LIGHT_BAKE_DYNAMIC: {
2768
mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_DYNAMIC);
2769
} break;
2770
case LIGHT_BAKE_STATIC:
2771
case LIGHT_BAKE_STATIC_LIGHTMAPS: {
2772
mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_STATIC);
2773
} break;
2774
}
2775
2776
mesh_node->set_layer_mask(src_mesh_node->get_layer_mask());
2777
mesh_node->set_cast_shadows_setting(src_mesh_node->get_cast_shadows_setting());
2778
mesh_node->set_visible(src_mesh_node->is_visible());
2779
mesh_node->set_visibility_range_begin(src_mesh_node->get_visibility_range_begin());
2780
mesh_node->set_visibility_range_begin_margin(src_mesh_node->get_visibility_range_begin_margin());
2781
mesh_node->set_visibility_range_end(src_mesh_node->get_visibility_range_end());
2782
mesh_node->set_visibility_range_end_margin(src_mesh_node->get_visibility_range_end_margin());
2783
mesh_node->set_visibility_range_fade_mode(src_mesh_node->get_visibility_range_fade_mode());
2784
2785
_copy_meta(p_node, mesh_node);
2786
2787
p_node->replace_by(mesh_node);
2788
p_node->set_owner(nullptr);
2789
memdelete(p_node);
2790
p_node = mesh_node;
2791
}
2792
2793
for (int i = 0; i < p_node->get_child_count(); i++) {
2794
_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);
2795
}
2796
2797
return p_node;
2798
}
2799
2800
void ResourceImporterScene::_add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes) {
2801
for (const Ref<Shape3D> &E : p_shapes) {
2802
CollisionShape3D *cshape = memnew(CollisionShape3D);
2803
cshape->set_shape(E);
2804
p_node->add_child(cshape, true);
2805
2806
cshape->set_owner(p_node->get_owner());
2807
}
2808
}
2809
2810
void ResourceImporterScene::_copy_meta(Object *p_src_object, Object *p_dst_object) {
2811
List<StringName> meta_list;
2812
p_src_object->get_meta_list(&meta_list);
2813
for (const StringName &meta_key : meta_list) {
2814
Variant meta_value = p_src_object->get_meta(meta_key);
2815
p_dst_object->set_meta(meta_key, meta_value);
2816
}
2817
}
2818
2819
void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions) {
2820
List<StringName> anims;
2821
p_player->get_animation_list(&anims);
2822
Node *parent = p_player->get_parent();
2823
ERR_FAIL_NULL(parent);
2824
HashMap<NodePath, uint32_t> used_tracks[TRACK_CHANNEL_MAX];
2825
bool tracks_to_add = false;
2826
static const Animation::TrackType track_types[TRACK_CHANNEL_MAX] = { Animation::TYPE_POSITION_3D, Animation::TYPE_ROTATION_3D, Animation::TYPE_SCALE_3D, Animation::TYPE_BLEND_SHAPE };
2827
for (const StringName &I : anims) {
2828
Ref<Animation> anim = p_player->get_animation(I);
2829
for (int i = 0; i < anim->get_track_count(); i++) {
2830
for (int j = 0; j < TRACK_CHANNEL_MAX; j++) {
2831
if (anim->track_get_type(i) != track_types[j]) {
2832
continue;
2833
}
2834
switch (p_track_actions[j]) {
2835
case ANIMATION_IMPORT_TRACKS_IF_PRESENT: {
2836
// Do Nothing.
2837
} break;
2838
case ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL: {
2839
used_tracks[j].insert(anim->track_get_path(i), 0);
2840
tracks_to_add = true;
2841
} break;
2842
case ANIMATION_IMPORT_TRACKS_NEVER: {
2843
anim->remove_track(i);
2844
i--;
2845
} break;
2846
}
2847
}
2848
}
2849
}
2850
2851
if (!tracks_to_add) {
2852
return;
2853
}
2854
2855
uint32_t pass = 0;
2856
for (const StringName &I : anims) {
2857
Ref<Animation> anim = p_player->get_animation(I);
2858
for (int j = 0; j < TRACK_CHANNEL_MAX; j++) {
2859
if (p_track_actions[j] != ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {
2860
continue;
2861
}
2862
2863
pass++;
2864
2865
for (int i = 0; i < anim->get_track_count(); i++) {
2866
if (anim->track_get_type(i) != track_types[j]) {
2867
continue;
2868
}
2869
2870
NodePath path = anim->track_get_path(i);
2871
2872
ERR_CONTINUE(!used_tracks[j].has(path)); // Should never happen.
2873
2874
used_tracks[j][path] = pass;
2875
}
2876
2877
for (const KeyValue<NodePath, uint32_t> &J : used_tracks[j]) {
2878
if (J.value == pass) {
2879
continue;
2880
}
2881
2882
NodePath path = J.key;
2883
Node *n = parent->get_node(path);
2884
2885
if (j == TRACK_CHANNEL_BLEND_SHAPE) {
2886
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(n);
2887
if (mi && path.get_subname_count() > 0) {
2888
StringName bs = path.get_subname(0);
2889
bool valid;
2890
float value = mi->get(bs, &valid);
2891
if (valid) {
2892
int track_idx = anim->add_track(track_types[j]);
2893
anim->track_set_path(track_idx, path);
2894
anim->track_set_imported(track_idx, true);
2895
anim->blend_shape_track_insert_key(track_idx, 0, value);
2896
}
2897
}
2898
2899
} else {
2900
Skeleton3D *skel = Object::cast_to<Skeleton3D>(n);
2901
Node3D *n3d = Object::cast_to<Node3D>(n);
2902
Vector3 loc;
2903
Quaternion rot;
2904
Vector3 scale;
2905
if (skel && path.get_subname_count() > 0) {
2906
StringName bone = path.get_subname(0);
2907
int bone_idx = skel->find_bone(bone);
2908
if (bone_idx == -1) {
2909
continue;
2910
}
2911
// Note that this is using get_bone_pose to update the bone pose cache.
2912
_ALLOW_DISCARD_ skel->get_bone_pose(bone_idx);
2913
loc = skel->get_bone_pose_position(bone_idx);
2914
rot = skel->get_bone_pose_rotation(bone_idx);
2915
scale = skel->get_bone_pose_scale(bone_idx);
2916
} else if (n3d) {
2917
loc = n3d->get_position();
2918
rot = n3d->get_transform().basis.get_rotation_quaternion();
2919
scale = n3d->get_scale();
2920
} else {
2921
continue;
2922
}
2923
2924
// Ensure insertion keeps tracks together and ordered by type (loc/rot/scale)
2925
int insert_at_pos = -1;
2926
for (int k = 0; k < anim->get_track_count(); k++) {
2927
NodePath tpath = anim->track_get_path(k);
2928
2929
if (path == tpath) {
2930
Animation::TrackType ttype = anim->track_get_type(k);
2931
if (insert_at_pos == -1) {
2932
// First insert, determine whether replacing or kicking back
2933
if (track_types[j] < ttype) {
2934
insert_at_pos = k;
2935
break; // No point in continuing.
2936
} else {
2937
insert_at_pos = k + 1;
2938
}
2939
} else if (ttype < track_types[j]) {
2940
// Kick back.
2941
insert_at_pos = k + 1;
2942
}
2943
} else if (insert_at_pos >= 0) {
2944
break;
2945
}
2946
}
2947
int track_idx = anim->add_track(track_types[j], insert_at_pos);
2948
2949
anim->track_set_path(track_idx, path);
2950
anim->track_set_imported(track_idx, true);
2951
switch (j) {
2952
case TRACK_CHANNEL_POSITION: {
2953
anim->position_track_insert_key(track_idx, 0, loc);
2954
} break;
2955
case TRACK_CHANNEL_ROTATION: {
2956
anim->rotation_track_insert_key(track_idx, 0, rot);
2957
} break;
2958
case TRACK_CHANNEL_SCALE: {
2959
anim->scale_track_insert_key(track_idx, 0, scale);
2960
} break;
2961
default: {
2962
}
2963
}
2964
}
2965
}
2966
}
2967
}
2968
}
2969
2970
Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashMap<StringName, Variant> &p_options) {
2971
Ref<EditorSceneFormatImporter> importer;
2972
String ext = p_source_file.get_extension().to_lower();
2973
2974
// TRANSLATORS: This is an editor progress label.
2975
EditorProgress progress("pre-import", TTR("Pre-Import Scene"), 0);
2976
progress.step(TTR("Importing Scene..."), 0);
2977
2978
for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {
2979
List<String> extensions;
2980
importer_elem->get_extensions(&extensions);
2981
2982
for (const String &F : extensions) {
2983
if (F.to_lower() == ext) {
2984
importer = importer_elem;
2985
break;
2986
}
2987
}
2988
2989
if (importer.is_valid()) {
2990
break;
2991
}
2992
}
2993
2994
ERR_FAIL_COND_V(importer.is_null(), nullptr);
2995
ERR_FAIL_COND_V(p_options.is_empty(), nullptr);
2996
2997
Error err = OK;
2998
2999
Node *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);
3000
if (!scene || err != OK) {
3001
return nullptr;
3002
}
3003
3004
_pre_fix_global(scene, p_options);
3005
3006
HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
3007
List<Pair<NodePath, Node *>> node_renames;
3008
_pre_fix_node(scene, scene, collision_map, nullptr, node_renames, p_options);
3009
3010
return scene;
3011
}
3012
3013
static 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) {
3014
const String &raw_save_path = p_settings[p_path_key];
3015
String save_path = ResourceUID::ensure_path(raw_save_path);
3016
if (raw_save_path.begins_with("uid://")) {
3017
if (save_path.is_empty() || !DirAccess::exists(save_path.get_base_dir())) {
3018
if (p_settings.has(p_fallback_path_key)) {
3019
String fallback_save_path = p_settings[p_fallback_path_key];
3020
if (!fallback_save_path.is_empty() && DirAccess::exists(fallback_save_path.get_base_dir())) {
3021
save_path = fallback_save_path;
3022
ResourceUID::get_singleton()->add_id(ResourceUID::get_singleton()->text_to_id(raw_save_path), save_path);
3023
}
3024
}
3025
} else {
3026
p_settings[p_fallback_path_key] = save_path;
3027
}
3028
}
3029
ERR_FAIL_COND_V(!save_path.is_empty() && !DirAccess::exists(save_path.get_base_dir()), ERR_FILE_BAD_PATH);
3030
if (!save_path.is_empty() && !raw_save_path.begins_with("uid://")) {
3031
const ResourceUID::ID id = ResourceLoader::get_resource_uid(save_path);
3032
if (id != ResourceUID::INVALID_ID) {
3033
p_settings[p_path_key] = ResourceUID::get_singleton()->id_to_text(id);
3034
} else {
3035
ResourceUID::ID save_id = hash64_murmur3_64(p_hash_str.hash64(), p_source_id) & 0x7FFFFFFFFFFFFFFF;
3036
if (ResourceUID::get_singleton()->has_id(save_id)) {
3037
if (save_path != ResourceUID::get_singleton()->get_id_path(save_id)) {
3038
// The user has specified a path which does not match the default UID.
3039
save_id = ResourceUID::get_singleton()->create_id_for_path(save_path);
3040
}
3041
}
3042
p_settings[p_path_key] = ResourceUID::get_singleton()->id_to_text(save_id);
3043
ResourceUID::get_singleton()->add_id(save_id, save_path);
3044
}
3045
p_settings[p_fallback_path_key] = save_path;
3046
}
3047
return OK;
3048
}
3049
3050
Error ResourceImporterScene::_check_resource_save_paths(ResourceUID::ID p_source_id, const String &p_hash_suffix, const Dictionary &p_data) {
3051
for (const KeyValue<Variant, Variant> &kv : p_data) {
3052
Dictionary settings = kv.value;
3053
3054
if (bool(settings.get("save_to_file/enabled", false)) && settings.has("save_to_file/path")) {
3055
String to_hash = kv.key.operator String() + p_hash_suffix;
3056
Error ret = convert_path_to_uid(p_source_id, to_hash, settings, "save_to_file/path", "save_to_file/fallback_path");
3057
ERR_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")));
3058
}
3059
3060
if (settings.has("slices/amount")) {
3061
int slice_count = settings["slices/amount"];
3062
for (int si = 0; si < slice_count; si++) {
3063
if (bool(settings.get("slice_" + itos(si + 1) + "/save_to_file/enabled", false)) &&
3064
settings.has("slice_" + itos(si + 1) + "/save_to_file/path")) {
3065
String to_hash = kv.key.operator String() + p_hash_suffix + itos(si + 1);
3066
Error ret = convert_path_to_uid(p_source_id, to_hash, settings,
3067
"slice_" + itos(si + 1) + "/save_to_file/path",
3068
"slice_" + itos(si + 1) + "/save_to_file/fallback_path");
3069
ERR_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")));
3070
}
3071
}
3072
}
3073
}
3074
3075
return OK;
3076
}
3077
3078
Error 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) {
3079
const String &src_path = p_source_file;
3080
3081
Ref<EditorSceneFormatImporter> importer;
3082
String ext = src_path.get_extension().to_lower();
3083
3084
EditorProgress progress("import", TTR("Import Scene"), 104);
3085
progress.step(TTR("Importing Scene..."), 0);
3086
3087
for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {
3088
List<String> extensions;
3089
importer_elem->get_extensions(&extensions);
3090
3091
for (const String &F : extensions) {
3092
if (F.to_lower() == ext) {
3093
importer = importer_elem;
3094
break;
3095
}
3096
}
3097
3098
if (importer.is_valid()) {
3099
break;
3100
}
3101
}
3102
3103
ERR_FAIL_COND_V(importer.is_null(), ERR_FILE_UNRECOGNIZED);
3104
ERR_FAIL_COND_V(p_options.is_empty(), ERR_BUG);
3105
3106
int import_flags = 0;
3107
3108
if (_scene_import_type == "AnimationLibrary") {
3109
import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION;
3110
import_flags |= EditorSceneFormatImporter::IMPORT_DISCARD_MESHES_AND_MATERIALS;
3111
} else if (bool(p_options["animation/import"])) {
3112
import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION;
3113
}
3114
3115
if (bool(p_options["skins/use_named_skins"])) {
3116
import_flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
3117
}
3118
3119
bool ensure_tangents = p_options["meshes/ensure_tangents"];
3120
if (ensure_tangents) {
3121
import_flags |= EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS;
3122
}
3123
3124
bool force_disable_compression = p_options["meshes/force_disable_compression"];
3125
if (force_disable_compression) {
3126
import_flags |= EditorSceneFormatImporter::IMPORT_FORCE_DISABLE_MESH_COMPRESSION;
3127
}
3128
3129
Dictionary subresources = p_options["_subresources"];
3130
3131
Error err = OK;
3132
3133
// Check whether any of the meshes or animations have nonexistent save paths
3134
// and if they do, fail the import immediately.
3135
if (subresources.has("meshes")) {
3136
err = _check_resource_save_paths(p_source_id, "m", subresources["meshes"]);
3137
if (err != OK) {
3138
return err;
3139
}
3140
}
3141
3142
if (subresources.has("animations")) {
3143
err = _check_resource_save_paths(p_source_id, "a", subresources["animations"]);
3144
if (err != OK) {
3145
return err;
3146
}
3147
}
3148
3149
List<String> missing_deps; // for now, not much will be done with this
3150
Node *scene = importer->import_scene(src_path, import_flags, p_options, &missing_deps, &err);
3151
if (!scene || err != OK) {
3152
return err;
3153
}
3154
3155
bool apply_root = true;
3156
if (p_options.has("nodes/apply_root_scale")) {
3157
apply_root = p_options["nodes/apply_root_scale"];
3158
}
3159
real_t root_scale = 1;
3160
if (p_options.has("nodes/root_scale")) {
3161
root_scale = p_options["nodes/root_scale"];
3162
}
3163
if (Object::cast_to<Node3D>(scene)) {
3164
Node3D *scene_3d = Object::cast_to<Node3D>(scene);
3165
Vector3 scale = Vector3(root_scale, root_scale, root_scale);
3166
if (apply_root) {
3167
_apply_permanent_scale_to_descendants(scene, scale);
3168
} else {
3169
scene_3d->scale(scale);
3170
}
3171
}
3172
3173
_pre_fix_global(scene, p_options);
3174
3175
HashSet<Ref<ImporterMesh>> scanned_meshes;
3176
HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
3177
Pair<PackedVector3Array, PackedInt32Array> occluder_arrays;
3178
List<Pair<NodePath, Node *>> node_renames;
3179
3180
_pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames, p_options);
3181
3182
for (int i = 0; i < post_importer_plugins.size(); i++) {
3183
post_importer_plugins.write[i]->pre_process(scene, p_options);
3184
}
3185
3186
// data in _subresources may be modified by pre_process(), so wait until now to check.
3187
Dictionary node_data;
3188
if (subresources.has("nodes")) {
3189
node_data = subresources["nodes"];
3190
}
3191
3192
Dictionary material_data;
3193
if (subresources.has("materials")) {
3194
material_data = subresources["materials"];
3195
}
3196
3197
Dictionary animation_data;
3198
if (subresources.has("animations")) {
3199
animation_data = subresources["animations"];
3200
}
3201
3202
Dictionary mesh_data;
3203
if (subresources.has("meshes")) {
3204
mesh_data = subresources["meshes"];
3205
}
3206
3207
float fps = 30;
3208
if (p_options.has(SNAME("animation/fps"))) {
3209
fps = (float)p_options[SNAME("animation/fps")];
3210
}
3211
bool remove_immutable_tracks = p_options.has("animation/remove_immutable_tracks") ? (bool)p_options["animation/remove_immutable_tracks"] : true;
3212
_pre_fix_animations(scene, scene, node_data, animation_data, fps);
3213
_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);
3214
_post_fix_animations(scene, scene, node_data, animation_data, fps, remove_immutable_tracks);
3215
3216
String root_type = p_options["nodes/root_type"];
3217
Ref<Script> root_script = p_options["nodes/root_script"];
3218
scene = _replace_node_with_type_and_script(scene, root_type, root_script);
3219
3220
String root_name = p_options["nodes/root_name"];
3221
if (!root_name.is_empty() && root_name != "Scene Root") {
3222
// TODO: Remove `&& root_name != "Scene Root"` for Godot 5.0.
3223
// For backwards compatibility with existing .import files,
3224
// treat "Scene Root" as having no root name override.
3225
scene->set_name(root_name);
3226
} else if (String(scene->get_name()).is_empty()) {
3227
scene->set_name(p_save_path.get_file().get_basename());
3228
}
3229
3230
if (!occluder_arrays.first.is_empty() && !occluder_arrays.second.is_empty()) {
3231
Ref<ArrayOccluder3D> occ = memnew(ArrayOccluder3D);
3232
occ->set_arrays(occluder_arrays.first, occluder_arrays.second);
3233
OccluderInstance3D *occluder_instance = memnew(OccluderInstance3D);
3234
occluder_instance->set_occluder(occ);
3235
scene->add_child(occluder_instance, true);
3236
occluder_instance->set_owner(scene);
3237
}
3238
3239
bool gen_lods = bool(p_options["meshes/generate_lods"]);
3240
bool create_shadow_meshes = bool(p_options["meshes/create_shadow_meshes"]);
3241
int light_bake_mode = p_options["meshes/light_baking"];
3242
float texel_size = p_options["meshes/lightmap_texel_size"];
3243
float lightmap_texel_size = MAX(0.001, texel_size);
3244
3245
Vector<uint8_t> src_lightmap_cache;
3246
Vector<Vector<uint8_t>> mesh_lightmap_caches;
3247
3248
{
3249
src_lightmap_cache = FileAccess::get_file_as_bytes(p_source_file + ".unwrap_cache", &err);
3250
if (err != OK) {
3251
src_lightmap_cache.clear();
3252
}
3253
}
3254
3255
scene = _generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, mesh_lightmap_caches);
3256
3257
if (mesh_lightmap_caches.size()) {
3258
Ref<FileAccess> f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE);
3259
if (f.is_valid()) {
3260
f->store_32(mesh_lightmap_caches.size());
3261
for (int i = 0; i < mesh_lightmap_caches.size(); i++) {
3262
String md5 = String::md5(mesh_lightmap_caches[i].ptr());
3263
f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size());
3264
}
3265
}
3266
}
3267
err = OK;
3268
3269
progress.step(TTR("Running Custom Script..."), 2);
3270
3271
String post_import_script_path = p_options["import_script/path"];
3272
3273
Ref<EditorScenePostImport> post_import_script;
3274
3275
if (!post_import_script_path.is_empty()) {
3276
if (post_import_script_path.is_relative_path()) {
3277
post_import_script_path = p_source_file.get_base_dir().path_join(post_import_script_path);
3278
}
3279
Ref<Script> scr = ResourceLoader::load(post_import_script_path);
3280
if (scr.is_null()) {
3281
EditorNode::add_io_error(TTR("Couldn't load post-import script:") + " " + post_import_script_path);
3282
} else if (scr->get_instance_base_type() != "EditorScenePostImport") {
3283
EditorNode::add_io_error(TTR("Script is not a subtype of EditorScenePostImport:") + " " + post_import_script_path);
3284
} else {
3285
post_import_script.instantiate();
3286
post_import_script->set_script(scr);
3287
if (!post_import_script->get_script_instance()) {
3288
EditorNode::add_io_error(TTR("Invalid/broken script for post-import (check console):") + " " + post_import_script_path);
3289
post_import_script.unref();
3290
return ERR_CANT_CREATE;
3291
}
3292
}
3293
}
3294
3295
// Apply RESET animation before serializing.
3296
if (_scene_import_type == "PackedScene") {
3297
int scene_child_count = scene->get_child_count();
3298
for (int i = 0; i < scene_child_count; i++) {
3299
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(scene->get_child(i));
3300
if (ap) {
3301
if (ap->can_apply_reset()) {
3302
ap->apply_reset();
3303
}
3304
}
3305
}
3306
}
3307
3308
if (post_import_script.is_valid()) {
3309
post_import_script->init(p_source_file);
3310
scene = post_import_script->post_import(scene);
3311
if (!scene) {
3312
EditorNode::add_io_error(
3313
TTR("Error running post-import script:") + " " + post_import_script_path + "\n" +
3314
TTR("Did you return a Node-derived object in the `_post_import()` method?"));
3315
return err;
3316
}
3317
}
3318
3319
for (int i = 0; i < post_importer_plugins.size(); i++) {
3320
post_importer_plugins.write[i]->post_process(scene, p_options);
3321
}
3322
3323
progress.step(TTR("Saving..."), 104);
3324
3325
int flags = 0;
3326
if (EditorSettings::get_singleton() && EDITOR_GET("filesystem/on_save/compress_binary_resources")) {
3327
flags |= ResourceSaver::FLAG_COMPRESS;
3328
}
3329
3330
if (_scene_import_type == "AnimationLibrary") {
3331
Ref<AnimationLibrary> library;
3332
for (int i = 0; i < scene->get_child_count(); i++) {
3333
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(scene->get_child(i));
3334
if (ap) {
3335
List<StringName> libs;
3336
ap->get_animation_library_list(&libs);
3337
if (libs.size()) {
3338
library = ap->get_animation_library(libs.front()->get());
3339
break;
3340
}
3341
}
3342
}
3343
3344
if (library.is_null()) {
3345
library.instantiate(); // Will be empty
3346
}
3347
3348
print_verbose("Saving animation to: " + p_save_path + ".res");
3349
err = ResourceSaver::save(library, p_save_path + ".res", flags); //do not take over, let the changed files reload themselves
3350
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save animation to file '" + p_save_path + ".res'.");
3351
} else if (_scene_import_type == "PackedScene") {
3352
Ref<PackedScene> packer = memnew(PackedScene);
3353
packer->pack(scene);
3354
print_verbose("Saving scene to: " + p_save_path + ".scn");
3355
err = ResourceSaver::save(packer, p_save_path + ".scn", flags); //do not take over, let the changed files reload themselves
3356
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save scene to file '" + p_save_path + ".scn'.");
3357
EditorInterface::get_singleton()->make_scene_preview(p_source_file, scene, 1024);
3358
} else {
3359
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown scene import type: " + _scene_import_type);
3360
}
3361
3362
memdelete(scene);
3363
3364
//this is not the time to reimport, wait until import process is done, import file is saved, etc.
3365
//EditorNode::get_singleton()->reload_scene(p_source_file);
3366
3367
return OK;
3368
}
3369
3370
Vector<Ref<EditorSceneFormatImporter>> ResourceImporterScene::scene_importers;
3371
Vector<Ref<EditorScenePostImportPlugin>> ResourceImporterScene::post_importer_plugins;
3372
3373
bool ResourceImporterScene::has_advanced_options() const {
3374
return true;
3375
}
3376
3377
void ResourceImporterScene::show_advanced_options(const String &p_path) {
3378
SceneImportSettingsDialog::get_singleton()->open_settings(p_path, _scene_import_type);
3379
}
3380
3381
ResourceImporterScene::ResourceImporterScene(const String &p_scene_import_type) {
3382
_scene_import_type = p_scene_import_type;
3383
}
3384
3385
void ResourceImporterScene::add_scene_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority) {
3386
ERR_FAIL_COND(p_importer.is_null());
3387
if (p_first_priority) {
3388
scene_importers.insert(0, p_importer);
3389
} else {
3390
scene_importers.push_back(p_importer);
3391
}
3392
}
3393
3394
void ResourceImporterScene::remove_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) {
3395
post_importer_plugins.erase(p_plugin);
3396
}
3397
3398
void ResourceImporterScene::add_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority) {
3399
ERR_FAIL_COND(p_plugin.is_null());
3400
if (p_first_priority) {
3401
post_importer_plugins.insert(0, p_plugin);
3402
} else {
3403
post_importer_plugins.push_back(p_plugin);
3404
}
3405
}
3406
3407
void ResourceImporterScene::remove_scene_importer(Ref<EditorSceneFormatImporter> p_importer) {
3408
scene_importers.erase(p_importer);
3409
}
3410
3411
void ResourceImporterScene::clean_up_importer_plugins() {
3412
scene_importers.clear();
3413
post_importer_plugins.clear();
3414
}
3415
3416
void ResourceImporterScene::get_scene_importer_extensions(List<String> *p_extensions) {
3417
for (Ref<EditorSceneFormatImporter> importer_elem : scene_importers) {
3418
importer_elem->get_extensions(p_extensions);
3419
}
3420
}
3421
3422
///////////////////////////////////////
3423
3424
void EditorSceneFormatImporterESCN::get_extensions(List<String> *r_extensions) const {
3425
r_extensions->push_back("escn");
3426
}
3427
3428
Node *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) {
3429
Error error;
3430
Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error);
3431
ERR_FAIL_COND_V_MSG(ps.is_null(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'.");
3432
Node *scene = ps->instantiate();
3433
TypedArray<Node> nodes = scene->find_children("*", "MeshInstance3D");
3434
for (int32_t node_i = 0; node_i < nodes.size(); node_i++) {
3435
MeshInstance3D *mesh_3d = cast_to<MeshInstance3D>(nodes[node_i]);
3436
Ref<ImporterMesh> mesh;
3437
mesh.instantiate();
3438
// Ignore the aabb, it will be recomputed.
3439
ImporterMeshInstance3D *importer_mesh_3d = memnew(ImporterMeshInstance3D);
3440
importer_mesh_3d->set_name(mesh_3d->get_name());
3441
importer_mesh_3d->set_transform(mesh_3d->get_relative_transform(mesh_3d->get_parent()));
3442
importer_mesh_3d->set_skin(mesh_3d->get_skin());
3443
importer_mesh_3d->set_skeleton_path(mesh_3d->get_skeleton_path());
3444
Ref<ArrayMesh> array_mesh_3d_mesh = mesh_3d->get_mesh();
3445
if (array_mesh_3d_mesh.is_valid()) {
3446
// For the MeshInstance3D nodes, we need to convert the ArrayMesh to an ImporterMesh specially.
3447
mesh->set_name(array_mesh_3d_mesh->get_name());
3448
for (int32_t blend_i = 0; blend_i < array_mesh_3d_mesh->get_blend_shape_count(); blend_i++) {
3449
mesh->add_blend_shape(array_mesh_3d_mesh->get_blend_shape_name(blend_i));
3450
}
3451
for (int32_t surface_i = 0; surface_i < array_mesh_3d_mesh->get_surface_count(); surface_i++) {
3452
mesh->add_surface(array_mesh_3d_mesh->surface_get_primitive_type(surface_i),
3453
array_mesh_3d_mesh->surface_get_arrays(surface_i),
3454
array_mesh_3d_mesh->surface_get_blend_shape_arrays(surface_i),
3455
array_mesh_3d_mesh->surface_get_lods(surface_i),
3456
array_mesh_3d_mesh->surface_get_material(surface_i),
3457
array_mesh_3d_mesh->surface_get_name(surface_i),
3458
array_mesh_3d_mesh->surface_get_format(surface_i));
3459
}
3460
mesh->set_blend_shape_mode(array_mesh_3d_mesh->get_blend_shape_mode());
3461
importer_mesh_3d->set_mesh(mesh);
3462
mesh_3d->replace_by(importer_mesh_3d);
3463
continue;
3464
}
3465
Ref<Mesh> mesh_3d_mesh = mesh_3d->get_mesh();
3466
if (mesh_3d_mesh.is_valid()) {
3467
// For the MeshInstance3D nodes, we need to convert the Mesh to an ImporterMesh specially.
3468
mesh->set_name(mesh_3d_mesh->get_name());
3469
for (int32_t surface_i = 0; surface_i < mesh_3d_mesh->get_surface_count(); surface_i++) {
3470
mesh->add_surface(mesh_3d_mesh->surface_get_primitive_type(surface_i),
3471
mesh_3d_mesh->surface_get_arrays(surface_i),
3472
Array(),
3473
mesh_3d_mesh->surface_get_lods(surface_i),
3474
mesh_3d_mesh->surface_get_material(surface_i),
3475
mesh_3d_mesh->surface_get_material(surface_i).is_valid() ? mesh_3d_mesh->surface_get_material(surface_i)->get_name() : String(),
3476
mesh_3d_mesh->surface_get_format(surface_i));
3477
}
3478
importer_mesh_3d->set_mesh(mesh);
3479
mesh_3d->replace_by(importer_mesh_3d);
3480
continue;
3481
}
3482
}
3483
3484
ERR_FAIL_NULL_V(scene, nullptr);
3485
3486
return scene;
3487
}
3488
3489