Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/import/3d/scene_import_settings.cpp
20878 views
1
/**************************************************************************/
2
/* scene_import_settings.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 "scene_import_settings.h"
32
33
#include "core/config/project_settings.h"
34
#include "editor/editor_node.h"
35
#include "editor/editor_string_names.h"
36
#include "editor/file_system/editor_file_system.h"
37
#include "editor/gui/editor_file_dialog.h"
38
#include "editor/inspector/editor_inspector.h"
39
#include "editor/scene/3d/skeleton_3d_editor_plugin.h"
40
#include "editor/settings/editor_settings.h"
41
#include "editor/themes/editor_scale.h"
42
#include "scene/3d/importer_mesh_instance_3d.h"
43
#include "scene/3d/multimesh_instance_3d.h"
44
#include "scene/animation/animation_player.h"
45
#include "scene/gui/subviewport_container.h"
46
#include "scene/main/timer.h"
47
#include "scene/resources/3d/importer_mesh.h"
48
#include "scene/resources/sky.h"
49
#include "scene/resources/surface_tool.h"
50
51
class SceneImportSettingsData : public Object {
52
GDCLASS(SceneImportSettingsData, Object)
53
friend class SceneImportSettingsDialog;
54
HashMap<StringName, Variant> *settings = nullptr;
55
HashMap<StringName, Variant> current;
56
HashMap<StringName, Variant> defaults;
57
List<ResourceImporter::ImportOption> options;
58
Vector<String> animation_list;
59
60
bool hide_options = false;
61
String path;
62
63
ResourceImporterScene::InternalImportCategory category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX;
64
65
bool _set(const StringName &p_name, const Variant &p_value) {
66
if (settings) {
67
if (defaults.has(p_name) && defaults[p_name] == p_value) {
68
settings->erase(p_name);
69
} else {
70
(*settings)[p_name] = p_value;
71
}
72
73
current[p_name] = p_value;
74
75
// SceneImportSettings must decide if a new collider should be generated or not.
76
if (category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE) {
77
SceneImportSettingsDialog::get_singleton()->request_generate_collider();
78
}
79
80
ResourceImporterScene *resource_importer_scene = SceneImportSettingsDialog::get_singleton()->get_resource_importer_scene();
81
if (category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) {
82
if (resource_importer_scene->get_option_visibility(path, p_name, current)) {
83
SceneImportSettingsDialog::get_singleton()->update_view();
84
}
85
} else {
86
if (resource_importer_scene->get_internal_option_update_view_required(category, p_name, current)) {
87
SceneImportSettingsDialog::get_singleton()->update_view();
88
}
89
}
90
91
return true;
92
}
93
return false;
94
}
95
96
bool _get(const StringName &p_name, Variant &r_ret) const {
97
if (settings) {
98
if (settings->has(p_name)) {
99
r_ret = (*settings)[p_name];
100
return true;
101
}
102
}
103
if (defaults.has(p_name)) {
104
r_ret = defaults[p_name];
105
return true;
106
}
107
return false;
108
}
109
110
void handle_special_properties(PropertyInfo &r_option) const {
111
ERR_FAIL_NULL(settings);
112
if (r_option.name == "rest_pose/load_pose") {
113
if (!settings->has("rest_pose/load_pose") || int((*settings)["rest_pose/load_pose"]) != 2) {
114
if (settings->has("rest_pose/external_animation_library")) {
115
(*settings)["rest_pose/external_animation_library"] = Variant();
116
}
117
}
118
}
119
if (r_option.name == "rest_pose/selected_animation") {
120
if (!settings->has("rest_pose/load_pose")) {
121
return;
122
}
123
String hint_string;
124
125
switch (int((*settings)["rest_pose/load_pose"])) {
126
case 1: {
127
hint_string = String(",").join(animation_list);
128
if (animation_list.size() == 1) {
129
(*settings)["rest_pose/selected_animation"] = animation_list[0];
130
}
131
} break;
132
case 2: {
133
Object *res = nullptr;
134
if (settings->has("rest_pose/external_animation_library")) {
135
res = (*settings)["rest_pose/external_animation_library"];
136
}
137
Ref<Animation> anim(res);
138
Ref<AnimationLibrary> library(res);
139
if (anim.is_valid()) {
140
hint_string = anim->get_name();
141
}
142
if (library.is_valid()) {
143
List<StringName> anim_names;
144
library->get_animation_list(&anim_names);
145
if (anim_names.size() == 1) {
146
(*settings)["rest_pose/selected_animation"] = String(anim_names.front()->get());
147
}
148
for (StringName anim_name : anim_names) {
149
hint_string += "," + anim_name; // Include preceding, as a catch-all.
150
}
151
}
152
} break;
153
default:
154
break;
155
}
156
r_option.hint = PROPERTY_HINT_ENUM;
157
r_option.hint_string = hint_string;
158
}
159
}
160
161
void _get_property_list(List<PropertyInfo> *r_list) const {
162
if (hide_options) {
163
return;
164
}
165
ResourceImporterScene *resource_importer_scene = SceneImportSettingsDialog::get_singleton()->get_resource_importer_scene();
166
for (const ResourceImporter::ImportOption &E : options) {
167
PropertyInfo option = E.option;
168
if (category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) {
169
if (resource_importer_scene->get_option_visibility(path, E.option.name, current)) {
170
handle_special_properties(option);
171
r_list->push_back(option);
172
}
173
} else {
174
if (resource_importer_scene->get_internal_option_visibility(category, E.option.name, current)) {
175
handle_special_properties(option);
176
r_list->push_back(option);
177
}
178
}
179
}
180
}
181
};
182
183
bool SceneImportSettingsDialog::_get_current(const StringName &p_name, Variant &r_ret) const {
184
if (scene_import_settings_data->_get(p_name, r_ret)) {
185
return true;
186
}
187
if (defaults.has(p_name)) {
188
r_ret = defaults[p_name];
189
return true;
190
}
191
return false;
192
}
193
194
void SceneImportSettingsDialog::_set_default(const StringName &p_name, const Variant &p_value) {
195
defaults[p_name] = p_value;
196
scene_import_settings_data->defaults[p_name] = p_value;
197
scene_import_settings_data->_set(p_name, p_value);
198
}
199
200
void SceneImportSettingsDialog::_fill_material(Tree *p_tree, const Ref<Material> &p_material, TreeItem *p_parent) {
201
String import_id;
202
bool has_import_id = false;
203
204
if (p_material->has_meta("import_id")) {
205
import_id = p_material->get_meta("import_id");
206
has_import_id = true;
207
} else if (!p_material->get_name().is_empty()) {
208
import_id = p_material->get_name();
209
has_import_id = true;
210
} else if (unnamed_material_name_map.has(p_material)) {
211
import_id = unnamed_material_name_map[p_material];
212
} else {
213
import_id = "@MATERIAL:" + itos(material_map.size());
214
unnamed_material_name_map[p_material] = import_id;
215
}
216
217
bool created = false;
218
if (!material_map.has(import_id)) {
219
MaterialData md;
220
created = true;
221
md.has_import_id = has_import_id;
222
md.material = p_material;
223
224
_load_default_subresource_settings(md.settings, "materials", import_id, ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MATERIAL);
225
226
material_map[import_id] = md;
227
}
228
229
MaterialData &material_data = material_map[import_id];
230
ERR_FAIL_COND(p_material != material_data.material);
231
232
Variant value;
233
if (_get_current("materials/extract", value) && (int)value != 0) {
234
String spath = base_path.get_base_dir();
235
if (_get_current("materials/extract_path", value)) {
236
String extpath = value;
237
if (!extpath.is_empty()) {
238
spath = extpath;
239
}
240
}
241
242
String ext = ResourceImporterScene::material_extension[_get_current("materials/extract_format", value) ? (int)value : 0];
243
String path = spath.path_join(import_id.validate_filename() + ext);
244
String uid_path = ResourceUID::path_to_uid(path);
245
material_data.settings["use_external/enabled"] = true;
246
material_data.settings["use_external/path"] = uid_path;
247
material_data.settings["use_external/fallback_path"] = path;
248
}
249
250
Ref<Texture2D> icon = get_editor_theme_icon(SNAME("StandardMaterial3D"));
251
252
TreeItem *item = p_tree->create_item(p_parent);
253
if (p_material->get_name().is_empty()) {
254
item->set_text(0, TTR("<Unnamed Material>"));
255
} else {
256
item->set_text(0, p_material->get_name());
257
}
258
item->set_icon(0, icon);
259
260
item->set_meta("type", "Material");
261
item->set_meta("import_id", import_id);
262
item->set_tooltip_text(0, vformat(TTR("Import ID: %s"), import_id));
263
item->set_selectable(0, true);
264
265
if (p_tree == scene_tree) {
266
material_data.scene_node = item;
267
} else if (p_tree == mesh_tree) {
268
material_data.mesh_node = item;
269
} else {
270
material_data.material_node = item;
271
}
272
273
if (created) {
274
_fill_material(material_tree, p_material, material_tree->get_root());
275
}
276
}
277
278
void SceneImportSettingsDialog::_fill_mesh(Tree *p_tree, const Ref<Mesh> &p_mesh, TreeItem *p_parent) {
279
String import_id;
280
281
bool has_import_id = false;
282
if (p_mesh->has_meta("import_id")) {
283
import_id = p_mesh->get_meta("import_id");
284
has_import_id = true;
285
} else if (!p_mesh->get_name().is_empty()) {
286
import_id = p_mesh->get_name();
287
has_import_id = true;
288
} else {
289
import_id = "@MESH:" + itos(mesh_set.size());
290
}
291
292
if (!mesh_map.has(import_id)) {
293
MeshData md;
294
md.has_import_id = has_import_id;
295
md.mesh = p_mesh;
296
297
_load_default_subresource_settings(md.settings, "meshes", import_id, ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH);
298
299
mesh_map[import_id] = md;
300
}
301
302
MeshData &mesh_data = mesh_map[import_id];
303
304
Ref<Texture2D> icon = get_editor_theme_icon(SNAME("MeshItem"));
305
306
TreeItem *item = p_tree->create_item(p_parent);
307
item->set_text(0, p_mesh->get_name());
308
item->set_icon(0, icon);
309
310
bool created = false;
311
if (!mesh_set.has(p_mesh)) {
312
mesh_set.insert(p_mesh);
313
created = true;
314
}
315
316
item->set_meta("type", "Mesh");
317
item->set_meta("import_id", import_id);
318
item->set_tooltip_text(0, vformat(TTR("Import ID: %s"), import_id));
319
320
item->set_selectable(0, true);
321
322
if (p_tree == scene_tree) {
323
mesh_data.scene_node = item;
324
} else {
325
mesh_data.mesh_node = item;
326
}
327
328
item->set_collapsed(true);
329
330
for (int i = 0; i < p_mesh->get_surface_count(); i++) {
331
Ref<Material> mat = p_mesh->surface_get_material(i);
332
if (mat.is_valid()) {
333
_fill_material(p_tree, mat, item);
334
}
335
}
336
337
if (created) {
338
_fill_mesh(mesh_tree, p_mesh, mesh_tree->get_root());
339
}
340
}
341
342
void SceneImportSettingsDialog::_fill_animation(Tree *p_tree, const Ref<Animation> &p_anim, const String &p_name, TreeItem *p_parent) {
343
if (!animation_map.has(p_name)) {
344
AnimationData ad;
345
ad.animation = p_anim;
346
347
_load_default_subresource_settings(ad.settings, "animations", p_name, ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION);
348
Animation::LoopMode loop_mode = p_anim->get_loop_mode();
349
if (!ad.settings.has("settings/loop_mode") && loop_mode != Animation::LoopMode::LOOP_NONE) {
350
// Update the loop mode to match detected mode (from import hints).
351
// This is necessary on the first import of a scene, otherwise the
352
// default (0/NONE) is set when filling out defaults.
353
ad.settings["settings/loop_mode"] = loop_mode;
354
}
355
356
animation_map[p_name] = ad;
357
}
358
359
AnimationData &animation_data = animation_map[p_name];
360
361
Ref<Texture2D> icon = get_editor_theme_icon(SNAME("Animation"));
362
363
TreeItem *item = p_tree->create_item(p_parent);
364
item->set_text(0, p_name);
365
item->set_icon(0, icon);
366
367
item->set_meta("type", "Animation");
368
item->set_meta("import_id", p_name);
369
370
item->set_selectable(0, true);
371
372
animation_data.scene_node = item;
373
}
374
375
void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
376
String import_id;
377
378
if (p_node->has_meta("import_id")) {
379
import_id = p_node->get_meta("import_id");
380
} else {
381
import_id = "PATH:" + String(scene->get_path_to(p_node));
382
p_node->set_meta("import_id", import_id);
383
}
384
385
ImporterMeshInstance3D *src_mesh_node = Object::cast_to<ImporterMeshInstance3D>(p_node);
386
387
if (src_mesh_node) {
388
MeshInstance3D *mesh_node = memnew(MeshInstance3D);
389
mesh_node->set_name(src_mesh_node->get_name());
390
mesh_node->set_transform(src_mesh_node->get_transform());
391
mesh_node->set_skin(src_mesh_node->get_skin());
392
mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());
393
mesh_node->set_visible(src_mesh_node->is_visible());
394
if (src_mesh_node->get_mesh().is_valid()) {
395
Ref<ImporterMesh> editor_mesh = src_mesh_node->get_mesh();
396
mesh_node->set_mesh(editor_mesh->get_mesh());
397
}
398
// Replace the original mesh node in the scene tree with the new one.
399
if (unlikely(p_node == scene)) {
400
scene = mesh_node;
401
}
402
p_node->replace_by(mesh_node);
403
memdelete(p_node);
404
p_node = mesh_node;
405
}
406
407
String type = p_node->get_class();
408
409
if (!has_theme_icon(type, EditorStringName(EditorIcons))) {
410
type = "Node3D";
411
}
412
413
Ref<Texture2D> icon = get_editor_theme_icon(type);
414
415
TreeItem *item = scene_tree->create_item(p_parent_item);
416
item->set_text(0, p_node->get_name());
417
418
if (p_node == scene) {
419
icon = get_editor_theme_icon(SNAME("PackedScene"));
420
item->set_text(0, TTR("Scene"));
421
}
422
423
item->set_icon(0, icon);
424
425
item->set_meta("type", "Node");
426
item->set_meta("class", type);
427
item->set_meta("import_id", import_id);
428
item->set_tooltip_text(0, vformat(TTR("Type: %s\nImport ID: %s"), type, import_id));
429
430
item->set_selectable(0, true);
431
432
if (!node_map.has(import_id)) {
433
NodeData nd;
434
435
if (p_node != scene) {
436
ResourceImporterScene::InternalImportCategory category;
437
if (src_mesh_node) {
438
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE;
439
} else if (Object::cast_to<AnimationPlayer>(p_node)) {
440
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
441
442
animation_player = Object::cast_to<AnimationPlayer>(p_node);
443
animation_player->connect(SceneStringName(animation_finished), callable_mp(this, &SceneImportSettingsDialog::_animation_finished));
444
} else if (Object::cast_to<Skeleton3D>(p_node)) {
445
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;
446
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);
447
skeleton->connect(SceneStringName(tree_entered), callable_mp(this, &SceneImportSettingsDialog::_skeleton_tree_entered).bind(skeleton));
448
skeletons.push_back(skeleton);
449
} else {
450
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
451
}
452
453
_load_default_subresource_settings(nd.settings, "nodes", import_id, category);
454
}
455
456
node_map[import_id] = nd;
457
}
458
NodeData &node_data = node_map[import_id];
459
460
node_data.node = p_node;
461
node_data.scene_node = item;
462
463
AnimationPlayer *anim_node = Object::cast_to<AnimationPlayer>(p_node);
464
if (anim_node) {
465
Vector<String> animation_list;
466
List<StringName> animations;
467
anim_node->get_animation_list(&animations);
468
for (const StringName &E : animations) {
469
_fill_animation(scene_tree, anim_node->get_animation(E), E, item);
470
animation_list.append(E);
471
}
472
if (scene_import_settings_data != nullptr) {
473
scene_import_settings_data->animation_list = animation_list;
474
}
475
}
476
477
for (int i = 0; i < p_node->get_child_count(); i++) {
478
_fill_scene(p_node->get_child(i), item);
479
}
480
Transform3D accum_xform;
481
Node3D *base = Object::cast_to<Node3D>(p_node);
482
while (base) {
483
accum_xform = base->get_transform() * accum_xform;
484
base = Object::cast_to<Node3D>(base->get_parent());
485
}
486
MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(p_node);
487
if (mesh_node && mesh_node->get_mesh().is_valid()) {
488
// This controls the display of mesh resources in the import settings dialog tree (the white mesh icon).
489
// We want to show these icons for any import type that preserves meshes.
490
if (_resource_importer_scene->get_scene_import_type() != "AnimationLibrary") {
491
_fill_mesh(scene_tree, mesh_node->get_mesh(), item);
492
}
493
494
// Add the collider view.
495
MeshInstance3D *collider_view = memnew(MeshInstance3D);
496
collider_view->set_name("collider_view");
497
collider_view->set_visible(false);
498
mesh_node->add_child(collider_view, true);
499
collider_view->set_owner(mesh_node);
500
501
AABB aabb = accum_xform.xform(mesh_node->get_mesh()->get_aabb());
502
503
if (first_aabb) {
504
contents_aabb = aabb;
505
first_aabb = false;
506
} else {
507
contents_aabb.merge_with(aabb);
508
}
509
}
510
MultiMeshInstance3D *multi_mesh_node = Object::cast_to<MultiMeshInstance3D>(p_node);
511
if (multi_mesh_node && multi_mesh_node->get_multimesh().is_valid()) {
512
const Ref<MultiMesh> multi_mesh = multi_mesh_node->get_multimesh();
513
const Ref<Mesh> mm_mesh = multi_mesh->get_mesh();
514
if (mm_mesh.is_valid()) {
515
_fill_mesh(scene_tree, mm_mesh, item);
516
}
517
const AABB aabb = accum_xform.xform(multi_mesh->get_aabb());
518
if (first_aabb) {
519
contents_aabb = aabb;
520
first_aabb = false;
521
} else {
522
contents_aabb.merge_with(aabb);
523
}
524
}
525
526
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);
527
if (skeleton) {
528
Ref<ArrayMesh> bones_mesh = Skeleton3DGizmoPlugin::get_bones_mesh(skeleton, -1, true);
529
530
bones_mesh_preview->set_mesh(bones_mesh);
531
bones_mesh_preview->set_transform(accum_xform * skeleton->get_transform());
532
533
AABB aabb = accum_xform.xform(bones_mesh->get_aabb());
534
535
if (first_aabb) {
536
contents_aabb = aabb;
537
first_aabb = false;
538
} else {
539
contents_aabb.merge_with(aabb);
540
}
541
}
542
}
543
544
void SceneImportSettingsDialog::_update_scene() {
545
scene_tree->clear();
546
material_tree->clear();
547
mesh_tree->clear();
548
549
// Hidden roots.
550
material_tree->create_item();
551
mesh_tree->create_item();
552
553
_fill_scene(scene, nullptr);
554
}
555
556
void SceneImportSettingsDialog::_update_view_gizmos() {
557
if (!is_visible()) {
558
return;
559
}
560
const HashMap<StringName, Variant> &main_settings = scene_import_settings_data->current;
561
bool reshow_settings = false;
562
if (main_settings.has("nodes/import_as_skeleton_bones")) {
563
bool new_import_as_skeleton = main_settings["nodes/import_as_skeleton_bones"];
564
reshow_settings = reshow_settings || (new_import_as_skeleton != previous_import_as_skeleton);
565
previous_import_as_skeleton = new_import_as_skeleton;
566
}
567
if (main_settings.has("animation/import_rest_as_RESET")) {
568
bool new_rest_as_reset = main_settings["animation/import_rest_as_RESET"];
569
reshow_settings = reshow_settings || (new_rest_as_reset != previous_rest_as_reset);
570
previous_rest_as_reset = new_rest_as_reset;
571
}
572
if (reshow_settings) {
573
_re_import();
574
open_settings(base_path);
575
return;
576
}
577
for (const KeyValue<String, NodeData> &e : node_map) {
578
// Skip import nodes that aren't MeshInstance3D.
579
const MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(e.value.node);
580
if (mesh_node == nullptr || mesh_node->get_mesh().is_null()) {
581
continue;
582
}
583
584
// Determine if the mesh collider should be visible.
585
bool show_collider_view = false;
586
if (e.value.settings.has(SNAME("generate/physics"))) {
587
show_collider_view = e.value.settings[SNAME("generate/physics")];
588
}
589
590
// Get the collider_view MeshInstance3D.
591
TypedArray<Node> descendants = mesh_node->find_children("collider_view", "MeshInstance3D");
592
CRASH_COND_MSG(descendants.is_empty(), "This is unreachable, since the collider view is always created even when the collision is not used! If this is triggered there is a bug on the function `_fill_scene`.");
593
MeshInstance3D *collider_view = Object::cast_to<MeshInstance3D>(descendants[0].operator Object *());
594
595
// Regenerate the physics collider for this MeshInstance3D if either:
596
// - A regeneration is requested for the selected import node.
597
// - The collider is being made visible.
598
if ((generate_collider && e.key == selected_id) || (show_collider_view && !collider_view->is_visible())) {
599
// This collider_view doesn't have a mesh so we need to generate a new one.
600
Ref<ImporterMesh> mesh;
601
mesh.instantiate();
602
// ResourceImporterScene::get_collision_shapes() expects ImporterMesh, not Mesh.
603
// TODO: Duplicate code with EditorSceneFormatImporterESCN::import_scene()
604
// Consider making a utility function to convert from Mesh to ImporterMesh.
605
Ref<Mesh> mesh_3d_mesh = mesh_node->get_mesh();
606
Ref<ArrayMesh> array_mesh_3d_mesh = mesh_3d_mesh;
607
if (array_mesh_3d_mesh.is_valid()) {
608
// For the MeshInstance3D nodes, we need to convert the ArrayMesh to an ImporterMesh specially.
609
mesh->set_name(array_mesh_3d_mesh->get_name());
610
for (int32_t blend_i = 0; blend_i < array_mesh_3d_mesh->get_blend_shape_count(); blend_i++) {
611
mesh->add_blend_shape(array_mesh_3d_mesh->get_blend_shape_name(blend_i));
612
}
613
for (int32_t surface_i = 0; surface_i < array_mesh_3d_mesh->get_surface_count(); surface_i++) {
614
mesh->add_surface(array_mesh_3d_mesh->surface_get_primitive_type(surface_i),
615
array_mesh_3d_mesh->surface_get_arrays(surface_i),
616
array_mesh_3d_mesh->surface_get_blend_shape_arrays(surface_i),
617
array_mesh_3d_mesh->surface_get_lods(surface_i),
618
array_mesh_3d_mesh->surface_get_material(surface_i),
619
array_mesh_3d_mesh->surface_get_name(surface_i),
620
array_mesh_3d_mesh->surface_get_format(surface_i));
621
}
622
mesh->set_blend_shape_mode(array_mesh_3d_mesh->get_blend_shape_mode());
623
} else if (mesh_3d_mesh.is_valid()) {
624
// For the MeshInstance3D nodes, we need to convert the Mesh to an ImporterMesh specially.
625
mesh->set_name(mesh_3d_mesh->get_name());
626
for (int32_t surface_i = 0; surface_i < mesh_3d_mesh->get_surface_count(); surface_i++) {
627
mesh->add_surface(mesh_3d_mesh->surface_get_primitive_type(surface_i),
628
mesh_3d_mesh->surface_get_arrays(surface_i),
629
Array(),
630
mesh_3d_mesh->surface_get_lods(surface_i),
631
mesh_3d_mesh->surface_get_material(surface_i),
632
mesh_3d_mesh->surface_get_material(surface_i).is_valid() ? mesh_3d_mesh->surface_get_material(surface_i)->get_name() : String(),
633
mesh_3d_mesh->surface_get_format(surface_i));
634
}
635
}
636
637
// Generate the mesh collider.
638
Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(mesh, e.value.settings, 1.0);
639
const Transform3D transform = ResourceImporterScene::get_collision_shapes_transform(e.value.settings);
640
641
Ref<ArrayMesh> collider_view_mesh;
642
collider_view_mesh.instantiate();
643
for (Ref<Shape3D> shape : shapes) {
644
Ref<ArrayMesh> debug_shape_mesh;
645
if (shape.is_valid()) {
646
debug_shape_mesh = shape->get_debug_mesh();
647
}
648
if (debug_shape_mesh.is_valid()) {
649
collider_view_mesh->add_surface_from_arrays(
650
debug_shape_mesh->surface_get_primitive_type(0),
651
debug_shape_mesh->surface_get_arrays(0));
652
653
collider_view_mesh->surface_set_material(
654
collider_view_mesh->get_surface_count() - 1,
655
collider_mat);
656
}
657
}
658
659
collider_view->set_mesh(collider_view_mesh);
660
collider_view->set_transform(transform);
661
}
662
663
// Set the collider visibility.
664
collider_view->set_visible(show_collider_view);
665
}
666
667
generate_collider = false;
668
}
669
670
void SceneImportSettingsDialog::_update_camera() {
671
AABB camera_aabb;
672
673
float rot_x = cam_rot_x;
674
float rot_y = cam_rot_y;
675
float zoom = cam_zoom;
676
677
if (selected_type == "Node" || selected_type == "Animation" || selected_type.is_empty()) {
678
camera_aabb = contents_aabb;
679
} else {
680
if (mesh_preview->get_mesh().is_valid()) {
681
camera_aabb = mesh_preview->get_transform().xform(mesh_preview->get_mesh()->get_aabb());
682
} else {
683
camera_aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
684
}
685
if (selected_type == "Mesh" && mesh_map.has(selected_id)) {
686
const MeshData &md = mesh_map[selected_id];
687
rot_x = md.cam_rot_x;
688
rot_y = md.cam_rot_y;
689
zoom = md.cam_zoom;
690
} else if (selected_type == "Material" && material_map.has(selected_id)) {
691
const MaterialData &md = material_map[selected_id];
692
rot_x = md.cam_rot_x;
693
rot_y = md.cam_rot_y;
694
zoom = md.cam_zoom;
695
}
696
}
697
698
Vector3 center = camera_aabb.get_center();
699
float camera_size = camera_aabb.get_longest_axis_size();
700
701
camera->set_orthogonal(camera_size * zoom, 0.0001, camera_size * 2);
702
703
Transform3D xf;
704
xf.basis = Basis(Vector3(0, 1, 0), rot_y) * Basis(Vector3(1, 0, 0), rot_x);
705
xf.origin = center;
706
xf.translate_local(0, 0, camera_size);
707
708
camera->set_transform(xf);
709
}
710
711
void SceneImportSettingsDialog::_load_default_subresource_settings(HashMap<StringName, Variant> &settings, const String &p_type, const String &p_import_id, ResourceImporterScene::InternalImportCategory p_category) {
712
if (base_subresource_settings.has(p_type)) {
713
Dictionary d = base_subresource_settings[p_type];
714
if (d.has(p_import_id)) {
715
d = d[p_import_id];
716
List<ResourceImporterScene::ImportOption> options;
717
_resource_importer_scene->get_internal_import_options(p_category, &options);
718
for (const ResourceImporterScene::ImportOption &E : options) {
719
String key = E.option.name;
720
if (d.has(key)) {
721
settings[key] = d[key];
722
}
723
}
724
}
725
}
726
}
727
728
void SceneImportSettingsDialog::request_generate_collider() {
729
generate_collider = true;
730
}
731
732
void SceneImportSettingsDialog::update_view() {
733
update_view_timer->start();
734
}
735
736
void SceneImportSettingsDialog::open_settings(const String &p_path, const String &p_scene_import_type) {
737
if (scene) {
738
_cleanup();
739
memdelete(scene);
740
scene = nullptr;
741
}
742
743
scene_import_settings_data->settings = nullptr;
744
scene_import_settings_data->path = p_path;
745
746
// AnimationLibrary cannot make use of the mesh and material tabs.
747
const bool disable_mesh_mat_tabs = p_scene_import_type == "AnimationLibrary";
748
data_mode->set_tab_hidden(1, disable_mesh_mat_tabs);
749
data_mode->set_tab_hidden(2, disable_mesh_mat_tabs);
750
if (disable_mesh_mat_tabs) {
751
data_mode->set_current_tab(0);
752
}
753
754
// Only show the save data options for PackedScene imports of scenes, not resource imports.
755
const bool disable_save_mesh_mat = p_scene_import_type != "PackedScene";
756
action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_EXTRACT_MATERIALS), disable_save_mesh_mat);
757
action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_CHOOSE_MESH_SAVE_PATHS), disable_save_mesh_mat);
758
const bool disable_save_anim = disable_save_mesh_mat && p_scene_import_type != "AnimationLibrary";
759
action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_CHOOSE_ANIMATION_SAVE_PATHS), disable_save_anim);
760
761
base_path = p_path;
762
763
mesh_set.clear();
764
animation_map.clear();
765
material_map.clear();
766
unnamed_material_name_map.clear();
767
mesh_map.clear();
768
node_map.clear();
769
defaults.clear();
770
771
mesh_preview->hide();
772
773
selected_id = "";
774
selected_type = "";
775
776
cam_rot_x = -Math::PI / 4;
777
cam_rot_y = -Math::PI / 4;
778
cam_zoom = 1;
779
780
{
781
base_subresource_settings.clear();
782
783
Ref<ConfigFile> config;
784
config.instantiate();
785
Error err = config->load(p_path + ".import");
786
if (err == OK) {
787
Vector<String> keys = config->get_section_keys("params");
788
for (const String &E : keys) {
789
Variant value = config->get_value("params", E);
790
if (E == "_subresources") {
791
base_subresource_settings = value;
792
} else {
793
defaults[E] = value;
794
}
795
}
796
}
797
}
798
799
// Regardless of p_scene_import_type, use PackedScene for pre_import because we want to see the full thing.
800
_resource_importer_scene->set_scene_import_type("PackedScene");
801
scene = _resource_importer_scene->pre_import(p_path, defaults);
802
_resource_importer_scene->set_scene_import_type(p_scene_import_type);
803
if (scene == nullptr) {
804
EditorNode::get_singleton()->show_warning(TTR("Error opening scene"));
805
return;
806
}
807
808
first_aabb = true;
809
810
_update_scene();
811
812
base_viewport->add_child(scene);
813
814
inspector->edit(nullptr);
815
816
if (first_aabb) {
817
contents_aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
818
first_aabb = false;
819
}
820
821
const HashMap<StringName, Variant> &main_settings = scene_import_settings_data->current;
822
if (main_settings.has("nodes/import_as_skeleton_bones")) {
823
previous_import_as_skeleton = main_settings["nodes/import_as_skeleton_bones"];
824
}
825
if (main_settings.has("animation/import_rest_as_RESET")) {
826
previous_rest_as_reset = main_settings["animation/import_rest_as_RESET"];
827
}
828
popup_centered_ratio();
829
_update_view_gizmos();
830
_update_camera();
831
832
// Start with the root item (Scene) selected.
833
scene_tree->get_root()->select(0);
834
835
set_title(vformat(TTR("Advanced Import Settings for %s '%s'"), _resource_importer_scene->get_visible_name(), base_path.get_file()));
836
}
837
838
SceneImportSettingsDialog *SceneImportSettingsDialog::singleton = nullptr;
839
840
SceneImportSettingsDialog *SceneImportSettingsDialog::get_singleton() {
841
return singleton;
842
}
843
844
Node *SceneImportSettingsDialog::get_selected_node() {
845
if (selected_id == "") {
846
return nullptr;
847
}
848
return node_map[selected_id].node;
849
}
850
851
void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, const String &p_id) {
852
selecting = true;
853
scene_import_settings_data->hide_options = false;
854
// Only AnimationLibrary and actual scenes can make use of the animation and skeleton options.
855
const bool hide_anim_and_skel_options = _resource_importer_scene->get_scene_import_type() != "PackedScene" && _resource_importer_scene->get_scene_import_type() != "AnimationLibrary";
856
// Only actual scenes can make use of the node generation options such as generating physics colliders on meshes or setting a script.
857
const bool hide_node_gen_options = _resource_importer_scene->get_scene_import_type() != "PackedScene";
858
859
bones_mesh_preview->hide();
860
if (p_type == "Node") {
861
node_selected->hide(); // Always hide just in case.
862
mesh_preview->hide();
863
_reset_animation();
864
865
if (Object::cast_to<Node3D>(scene)) {
866
Object::cast_to<Node3D>(scene)->show();
867
}
868
material_tree->deselect_all();
869
mesh_tree->deselect_all();
870
NodeData &nd = node_map[p_id];
871
872
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(nd.node);
873
if (mi) {
874
Ref<Mesh> base_mesh = mi->get_mesh();
875
if (base_mesh.is_valid()) {
876
AABB aabb = base_mesh->get_aabb();
877
Transform3D aabb_xf;
878
aabb_xf.basis.scale(aabb.size);
879
aabb_xf.origin = aabb.position;
880
881
aabb_xf = mi->get_global_transform() * aabb_xf;
882
node_selected->set_transform(aabb_xf);
883
node_selected->show();
884
}
885
}
886
887
if (nd.node == scene) {
888
scene_import_settings_data->settings = &defaults;
889
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX;
890
} else {
891
scene_import_settings_data->settings = &nd.settings;
892
if (mi) {
893
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE;
894
scene_import_settings_data->hide_options = hide_node_gen_options;
895
} else if (Object::cast_to<AnimationPlayer>(nd.node)) {
896
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
897
scene_import_settings_data->hide_options = hide_anim_and_skel_options;
898
} else if (Object::cast_to<Skeleton3D>(nd.node)) {
899
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;
900
bones_mesh_preview->show();
901
scene_import_settings_data->hide_options = hide_anim_and_skel_options;
902
} else {
903
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
904
scene_import_settings_data->hide_options = hide_node_gen_options;
905
}
906
}
907
} else if (p_type == "Animation") {
908
node_selected->hide(); // Always hide just in case.
909
mesh_preview->hide();
910
_reset_animation(p_id);
911
912
if (Object::cast_to<Node3D>(scene)) {
913
Object::cast_to<Node3D>(scene)->show();
914
}
915
material_tree->deselect_all();
916
mesh_tree->deselect_all();
917
AnimationData &ad = animation_map[p_id];
918
919
scene_import_settings_data->settings = &ad.settings;
920
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION;
921
scene_import_settings_data->hide_options = hide_anim_and_skel_options;
922
923
_animation_update_skeleton_visibility();
924
} else if (p_type == "Mesh") {
925
node_selected->hide();
926
if (Object::cast_to<Node3D>(scene)) {
927
Object::cast_to<Node3D>(scene)->hide();
928
}
929
930
MeshData &md = mesh_map[p_id];
931
if (md.mesh_node != nullptr) {
932
if (p_from != mesh_tree) {
933
md.mesh_node->uncollapse_tree();
934
md.mesh_node->select(0);
935
mesh_tree->ensure_cursor_is_visible();
936
}
937
if (p_from != scene_tree) {
938
md.scene_node->uncollapse_tree();
939
md.scene_node->select(0);
940
scene_tree->ensure_cursor_is_visible();
941
}
942
}
943
944
mesh_preview->set_mesh(md.mesh);
945
mesh_preview->show();
946
_reset_animation();
947
948
material_tree->deselect_all();
949
950
scene_import_settings_data->settings = &md.settings;
951
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH;
952
} else if (p_type == "Material") {
953
node_selected->hide();
954
if (Object::cast_to<Node3D>(scene)) {
955
Object::cast_to<Node3D>(scene)->hide();
956
}
957
958
mesh_preview->show();
959
_reset_animation();
960
961
MaterialData &md = material_map[p_id];
962
963
material_preview->set_material(md.material);
964
mesh_preview->set_mesh(material_preview);
965
966
if (p_from != mesh_tree) {
967
md.mesh_node->uncollapse_tree();
968
md.mesh_node->select(0);
969
mesh_tree->ensure_cursor_is_visible();
970
}
971
if (p_from != scene_tree) {
972
md.scene_node->uncollapse_tree();
973
md.scene_node->select(0);
974
scene_tree->ensure_cursor_is_visible();
975
}
976
if (p_from != material_tree) {
977
md.material_node->uncollapse_tree();
978
md.material_node->select(0);
979
material_tree->ensure_cursor_is_visible();
980
}
981
982
scene_import_settings_data->settings = &md.settings;
983
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MATERIAL;
984
}
985
986
selected_type = p_type;
987
selected_id = p_id;
988
989
selecting = false;
990
991
_update_camera();
992
993
List<ResourceImporter::ImportOption> options;
994
995
if (scene_import_settings_data->category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) {
996
_resource_importer_scene->get_import_options(base_path, &options);
997
} else {
998
_resource_importer_scene->get_internal_import_options(scene_import_settings_data->category, &options);
999
}
1000
1001
scene_import_settings_data->defaults.clear();
1002
scene_import_settings_data->current.clear();
1003
1004
if (scene_import_settings_data->settings) {
1005
for (const ResourceImporter::ImportOption &E : options) {
1006
scene_import_settings_data->defaults[E.option.name] = E.default_value;
1007
// Needed for visibility toggling (fails if something is missing).
1008
if (scene_import_settings_data->settings->has(E.option.name)) {
1009
scene_import_settings_data->current[E.option.name] = (*scene_import_settings_data->settings)[E.option.name];
1010
} else {
1011
scene_import_settings_data->current[E.option.name] = E.default_value;
1012
}
1013
}
1014
}
1015
1016
scene_import_settings_data->options = options;
1017
inspector->edit(scene_import_settings_data);
1018
scene_import_settings_data->notify_property_list_changed();
1019
}
1020
1021
void SceneImportSettingsDialog::_inspector_property_edited(const String &p_name) {
1022
if (p_name == "settings/loop_mode") {
1023
if (!animation_map.has(selected_id)) {
1024
return;
1025
}
1026
HashMap<StringName, Variant> settings(animation_map[selected_id].settings);
1027
if (settings.has(p_name)) {
1028
animation_loop_mode = static_cast<Animation::LoopMode>((int)settings[p_name]);
1029
} else {
1030
animation_loop_mode = Animation::LoopMode::LOOP_NONE;
1031
}
1032
}
1033
if ((p_name == "use_external/enabled") || (p_name == "use_external/path") || (p_name == "use_external/fallback_path")) {
1034
MaterialData &material_data = material_map[selected_id];
1035
String spath = base_path.get_base_dir();
1036
Variant value;
1037
if (_get_current("materials/extract_path", value)) {
1038
String extpath = value;
1039
if (!extpath.is_empty()) {
1040
spath = extpath;
1041
}
1042
}
1043
String opath = material_data.settings.has("use_external/path") ? (String)material_data.settings["use_external/path"] : String();
1044
if (opath.begins_with("uid://")) {
1045
opath = ResourceUID::uid_to_path(opath);
1046
}
1047
String ext = ResourceImporterScene::material_extension[_get_current("materials/extract_format", value) ? (int)value : 0];
1048
String npath = spath.path_join(selected_id.validate_filename() + ext);
1049
1050
if (!material_data.settings.has("use_external/enabled") || (bool)material_data.settings["use_external/enabled"] == false || opath != npath) {
1051
if (_get_current("materials/extract", value) && (int)value != 0) {
1052
print_line("Material settings changed, automatic material extraction disabled.");
1053
}
1054
_set_default("materials/extract", 0);
1055
}
1056
}
1057
}
1058
1059
void SceneImportSettingsDialog::_reset_bone_transforms() {
1060
for (Skeleton3D *skeleton : skeletons) {
1061
skeleton->reset_bone_poses();
1062
}
1063
}
1064
1065
void SceneImportSettingsDialog::_play_animation() {
1066
if (animation_player == nullptr) {
1067
return;
1068
}
1069
StringName id = StringName(selected_id);
1070
if (animation_player->has_animation(id)) {
1071
if (animation_player->is_playing()) {
1072
animation_player->pause();
1073
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
1074
set_process(false);
1075
} else {
1076
animation_player->play(id);
1077
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
1078
set_process(true);
1079
}
1080
}
1081
}
1082
1083
void SceneImportSettingsDialog::_stop_current_animation() {
1084
animation_pingpong = false;
1085
animation_player->stop();
1086
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
1087
animation_slider->set_value_no_signal(0.0);
1088
set_process(false);
1089
}
1090
1091
void SceneImportSettingsDialog::_reset_animation(const String &p_animation_name) {
1092
if (p_animation_name.is_empty()) {
1093
animation_preview->hide();
1094
1095
if (animation_player != nullptr && animation_player->is_playing()) {
1096
animation_player->stop();
1097
}
1098
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
1099
1100
_reset_bone_transforms();
1101
set_process(false);
1102
} else {
1103
_reset_bone_transforms();
1104
animation_preview->show();
1105
1106
animation_loop_mode = Animation::LoopMode::LOOP_NONE;
1107
animation_pingpong = false;
1108
1109
if (animation_map.has(p_animation_name)) {
1110
HashMap<StringName, Variant> settings(animation_map[p_animation_name].settings);
1111
if (settings.has("settings/loop_mode")) {
1112
animation_loop_mode = static_cast<Animation::LoopMode>((int)settings["settings/loop_mode"]);
1113
}
1114
}
1115
1116
if (animation_player->is_playing() && animation_loop_mode != Animation::LoopMode::LOOP_NONE) {
1117
animation_player->play(p_animation_name);
1118
} else {
1119
animation_player->stop(true);
1120
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
1121
animation_player->set_assigned_animation(p_animation_name);
1122
animation_player->seek(0.0, true);
1123
animation_slider->set_value_no_signal(0.0);
1124
set_process(false);
1125
}
1126
}
1127
}
1128
1129
void SceneImportSettingsDialog::_animation_slider_value_changed(double p_value) {
1130
if (animation_player == nullptr || !animation_map.has(selected_id) || animation_map[selected_id].animation.is_null()) {
1131
return;
1132
}
1133
if (animation_player->is_playing()) {
1134
animation_player->stop();
1135
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
1136
set_process(false);
1137
}
1138
animation_player->seek(p_value * animation_map[selected_id].animation->get_length(), true);
1139
}
1140
1141
void SceneImportSettingsDialog::_skeleton_tree_entered(Skeleton3D *p_skeleton) {
1142
bones_mesh_preview->set_skeleton_path(p_skeleton->get_path());
1143
Ref<Skin> skin = p_skeleton->create_skin_from_rest_transforms();
1144
p_skeleton->register_skin(skin);
1145
bones_mesh_preview->set_skin(skin);
1146
}
1147
1148
void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) {
1149
Animation::LoopMode loop_mode = animation_loop_mode;
1150
1151
switch (loop_mode) {
1152
case Animation::LOOP_NONE: {
1153
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
1154
animation_slider->set_value_no_signal(1.0);
1155
set_process(false);
1156
} break;
1157
case Animation::LOOP_LINEAR: {
1158
animation_player->play(p_name);
1159
} break;
1160
case Animation::LOOP_PINGPONG: {
1161
if (animation_pingpong) {
1162
animation_player->play(p_name);
1163
} else {
1164
animation_player->play_backwards(p_name);
1165
}
1166
animation_pingpong = !animation_pingpong;
1167
} break;
1168
default: {
1169
} break;
1170
}
1171
}
1172
1173
void SceneImportSettingsDialog::_animation_update_skeleton_visibility() {
1174
if (animation_toggle_skeleton_visibility->is_pressed()) {
1175
bones_mesh_preview->show();
1176
} else {
1177
bones_mesh_preview->hide();
1178
}
1179
}
1180
1181
void SceneImportSettingsDialog::_material_tree_selected() {
1182
if (selecting) {
1183
return;
1184
}
1185
TreeItem *item = material_tree->get_selected();
1186
String type = item->get_meta("type");
1187
String import_id = item->get_meta("import_id");
1188
1189
_select(material_tree, type, import_id);
1190
}
1191
1192
void SceneImportSettingsDialog::_mesh_tree_selected() {
1193
if (selecting) {
1194
return;
1195
}
1196
1197
TreeItem *item = mesh_tree->get_selected();
1198
String type = item->get_meta("type");
1199
String import_id = item->get_meta("import_id");
1200
1201
_select(mesh_tree, type, import_id);
1202
}
1203
1204
void SceneImportSettingsDialog::_scene_tree_selected() {
1205
if (selecting) {
1206
return;
1207
}
1208
TreeItem *item = scene_tree->get_selected();
1209
String type = item->get_meta("type");
1210
String import_id = item->get_meta("import_id");
1211
1212
_select(scene_tree, type, import_id);
1213
}
1214
1215
void SceneImportSettingsDialog::_cleanup() {
1216
skeletons.clear();
1217
if (animation_player != nullptr) {
1218
animation_player->disconnect(SceneStringName(animation_finished), callable_mp(this, &SceneImportSettingsDialog::_animation_finished));
1219
animation_player = nullptr;
1220
}
1221
set_process(false);
1222
}
1223
1224
void SceneImportSettingsDialog::_on_light_1_switch_pressed() {
1225
light1->set_visible(light_1_switch->is_pressed());
1226
}
1227
1228
void SceneImportSettingsDialog::_on_light_2_switch_pressed() {
1229
light2->set_visible(light_2_switch->is_pressed());
1230
}
1231
1232
void SceneImportSettingsDialog::_on_light_rotate_switch_pressed() {
1233
bool light_top_level = !light_rotate_switch->is_pressed();
1234
light1->set_as_top_level_keep_local(light_top_level);
1235
light2->set_as_top_level_keep_local(light_top_level);
1236
}
1237
1238
void SceneImportSettingsDialog::_viewport_input(const Ref<InputEvent> &p_input) {
1239
float *rot_x = &cam_rot_x;
1240
float *rot_y = &cam_rot_y;
1241
float *zoom = &cam_zoom;
1242
1243
if (selected_type == "Mesh" && mesh_map.has(selected_id)) {
1244
MeshData &md = mesh_map[selected_id];
1245
rot_x = &md.cam_rot_x;
1246
rot_y = &md.cam_rot_y;
1247
zoom = &md.cam_zoom;
1248
} else if (selected_type == "Material" && material_map.has(selected_id)) {
1249
MaterialData &md = material_map[selected_id];
1250
rot_x = &md.cam_rot_x;
1251
rot_y = &md.cam_rot_y;
1252
zoom = &md.cam_zoom;
1253
}
1254
Ref<InputEventMouseMotion> mm = p_input;
1255
if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
1256
(*rot_x) -= mm->get_relative().y * 0.01 * EDSCALE;
1257
(*rot_y) -= mm->get_relative().x * 0.01 * EDSCALE;
1258
(*rot_x) = CLAMP((*rot_x), -Math::PI / 2, Math::PI / 2);
1259
_update_camera();
1260
}
1261
if (mm.is_valid() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
1262
DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CursorShape::CURSOR_ARROW);
1263
}
1264
Ref<InputEventMouseButton> mb = p_input;
1265
if (mb.is_valid() && mb->get_button_index() == MouseButton::WHEEL_DOWN) {
1266
(*zoom) *= 1.1;
1267
if ((*zoom) > 10.0) {
1268
(*zoom) = 10.0;
1269
}
1270
_update_camera();
1271
}
1272
if (mb.is_valid() && mb->get_button_index() == MouseButton::WHEEL_UP) {
1273
(*zoom) /= 1.1;
1274
if ((*zoom) < 0.1) {
1275
(*zoom) = 0.1;
1276
}
1277
_update_camera();
1278
}
1279
Ref<InputEventMagnifyGesture> mg = p_input;
1280
if (mg.is_valid()) {
1281
real_t mg_factor = mg->get_factor();
1282
if (mg_factor == 0.0) {
1283
mg_factor = 1.0;
1284
}
1285
(*zoom) /= mg_factor;
1286
if ((*zoom) < 0.1) {
1287
(*zoom) = 0.1;
1288
} else if ((*zoom) > 10.0) {
1289
(*zoom) = 10.0;
1290
}
1291
_update_camera();
1292
}
1293
}
1294
1295
void SceneImportSettingsDialog::_re_import() {
1296
HashMap<StringName, Variant> main_settings;
1297
1298
main_settings = scene_import_settings_data->current;
1299
main_settings.erase("_subresources");
1300
Dictionary nodes;
1301
Dictionary materials;
1302
Dictionary meshes;
1303
Dictionary animations;
1304
1305
Dictionary subresources;
1306
1307
for (KeyValue<String, NodeData> &E : node_map) {
1308
if (E.value.settings.size()) {
1309
Dictionary d;
1310
for (const KeyValue<StringName, Variant> &F : E.value.settings) {
1311
d[String(F.key)] = F.value;
1312
}
1313
nodes[E.key] = d;
1314
}
1315
}
1316
if (nodes.size()) {
1317
subresources["nodes"] = nodes;
1318
}
1319
1320
for (KeyValue<String, MaterialData> &E : material_map) {
1321
if (E.value.settings.size()) {
1322
Dictionary d;
1323
for (const KeyValue<StringName, Variant> &F : E.value.settings) {
1324
d[String(F.key)] = F.value;
1325
}
1326
materials[E.key] = d;
1327
}
1328
}
1329
if (materials.size()) {
1330
subresources["materials"] = materials;
1331
}
1332
1333
for (KeyValue<String, MeshData> &E : mesh_map) {
1334
if (E.value.settings.size()) {
1335
Dictionary d;
1336
for (const KeyValue<StringName, Variant> &F : E.value.settings) {
1337
d[String(F.key)] = F.value;
1338
}
1339
meshes[E.key] = d;
1340
}
1341
}
1342
if (meshes.size()) {
1343
subresources["meshes"] = meshes;
1344
}
1345
1346
for (KeyValue<String, AnimationData> &E : animation_map) {
1347
if (E.value.settings.size()) {
1348
Dictionary d;
1349
for (const KeyValue<StringName, Variant> &F : E.value.settings) {
1350
d[String(F.key)] = F.value;
1351
}
1352
animations[E.key] = d;
1353
}
1354
}
1355
if (animations.size()) {
1356
subresources["animations"] = animations;
1357
}
1358
1359
main_settings["_subresources"] = subresources;
1360
1361
_cleanup(); // Prevent skeletons and other pointers from pointing to dangling references.
1362
EditorFileSystem::get_singleton()->reimport_file_with_custom_parameters(base_path, _resource_importer_scene->get_importer_name(), main_settings);
1363
}
1364
1365
void SceneImportSettingsDialog::_update_theme_item_cache() {
1366
ConfirmationDialog::_update_theme_item_cache();
1367
theme_cache.light_1_icon = get_editor_theme_icon(SNAME("MaterialPreviewLight1"));
1368
theme_cache.light_2_icon = get_editor_theme_icon(SNAME("MaterialPreviewLight2"));
1369
theme_cache.rotate_icon = get_editor_theme_icon(SNAME("PreviewRotate"));
1370
}
1371
1372
void SceneImportSettingsDialog::_notification(int p_what) {
1373
switch (p_what) {
1374
case NOTIFICATION_READY: {
1375
connect(SceneStringName(confirmed), callable_mp(this, &SceneImportSettingsDialog::_re_import));
1376
} break;
1377
1378
case NOTIFICATION_THEME_CHANGED: {
1379
if (animation_player != nullptr && animation_player->is_playing()) {
1380
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
1381
} else {
1382
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
1383
}
1384
animation_stop_button->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
1385
1386
light_1_switch->set_button_icon(theme_cache.light_1_icon);
1387
light_2_switch->set_button_icon(theme_cache.light_2_icon);
1388
light_rotate_switch->set_button_icon(theme_cache.rotate_icon);
1389
1390
animation_toggle_skeleton_visibility->set_button_icon(get_editor_theme_icon(SNAME("SkeletonPreview")));
1391
} break;
1392
1393
case NOTIFICATION_PROCESS: {
1394
if (animation_player != nullptr) {
1395
animation_slider->set_value_no_signal(animation_player->get_current_animation_position() / animation_player->get_current_animation_length());
1396
}
1397
} break;
1398
1399
case NOTIFICATION_VISIBILITY_CHANGED: {
1400
if (!is_visible()) {
1401
_cleanup();
1402
}
1403
} break;
1404
}
1405
}
1406
1407
void SceneImportSettingsDialog::_menu_callback(int p_id) {
1408
switch (p_id) {
1409
case ACTION_EXTRACT_MATERIALS: {
1410
save_path->set_title(TTR("Select folder to extract material resources"));
1411
external_extension_type->select(0);
1412
} break;
1413
case ACTION_CHOOSE_MESH_SAVE_PATHS: {
1414
save_path->set_title(TTR("Select folder where mesh resources will save on import"));
1415
external_extension_type->select(1);
1416
} break;
1417
case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: {
1418
save_path->set_title(TTR("Select folder where animations will save on import"));
1419
external_extension_type->select(1);
1420
} break;
1421
}
1422
1423
save_path->set_current_dir(base_path.get_base_dir());
1424
current_action = p_id;
1425
save_path->popup_centered_ratio();
1426
}
1427
1428
void SceneImportSettingsDialog::_save_path_changed(const String &p_path) {
1429
save_path_item->set_text(1, p_path);
1430
1431
if (FileAccess::exists(p_path)) {
1432
save_path_item->set_text(2, TTR("Warning: File exists"));
1433
save_path_item->set_tooltip_text(2, TTR("Existing file with the same name will be replaced."));
1434
save_path_item->set_icon(2, get_editor_theme_icon(SNAME("StatusWarning")));
1435
1436
} else {
1437
save_path_item->set_text(2, TTR("Will create new file"));
1438
save_path_item->set_icon(2, get_editor_theme_icon(SNAME("StatusSuccess")));
1439
}
1440
}
1441
1442
void SceneImportSettingsDialog::_browse_save_callback(Object *p_item, int p_column, int p_id, MouseButton p_button) {
1443
if (p_button != MouseButton::LEFT) {
1444
return;
1445
}
1446
1447
TreeItem *item = Object::cast_to<TreeItem>(p_item);
1448
1449
String path = item->get_text(1);
1450
1451
item_save_path->set_current_file(path);
1452
save_path_item = item;
1453
1454
item_save_path->popup_centered_ratio();
1455
}
1456
1457
void SceneImportSettingsDialog::_save_dir_callback(const String &p_path) {
1458
external_path_tree->clear();
1459
TreeItem *root = external_path_tree->create_item();
1460
save_path_items.clear();
1461
1462
switch (current_action) {
1463
case ACTION_EXTRACT_MATERIALS: {
1464
for (const KeyValue<String, MaterialData> &E : material_map) {
1465
MaterialData &md = material_map[E.key];
1466
1467
TreeItem *item = external_path_tree->create_item(root);
1468
1469
String name = md.material_node->get_text(0);
1470
1471
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
1472
item->set_icon(0, get_editor_theme_icon(SNAME("StandardMaterial3D")));
1473
item->set_text(0, name);
1474
1475
if (md.has_import_id) {
1476
if (md.settings.has("use_external/enabled") && bool(md.settings["use_external/enabled"])) {
1477
item->set_text(2, TTR("Already External"));
1478
item->set_tooltip_text(2, TTR("This material already references an external file, no action will be taken.\nDisable the external property for it to be extracted again."));
1479
} else {
1480
item->set_metadata(0, E.key);
1481
item->set_editable(0, true);
1482
item->set_checked(0, true);
1483
name = name.validate_filename();
1484
String path = p_path.path_join(name);
1485
if (external_extension_type->get_selected() == 0) {
1486
path += ".tres";
1487
} else {
1488
path += ".res";
1489
}
1490
1491
item->set_text(1, path);
1492
if (FileAccess::exists(path)) {
1493
item->set_text(2, TTR("Warning: File exists"));
1494
item->set_tooltip_text(2, TTR("Existing file with the same name will be replaced."));
1495
item->set_icon(2, get_editor_theme_icon(SNAME("StatusWarning")));
1496
1497
} else {
1498
item->set_text(2, TTR("Will create new file"));
1499
item->set_icon(2, get_editor_theme_icon(SNAME("StatusSuccess")));
1500
}
1501
1502
item->add_button(1, get_editor_theme_icon(SNAME("Folder")));
1503
}
1504
1505
} else {
1506
item->set_text(2, TTR("No import ID"));
1507
item->set_tooltip_text(2, TTR("Material has no name nor any other way to identify on re-import.\nPlease name it or ensure it is exported with an unique ID."));
1508
item->set_icon(2, get_editor_theme_icon(SNAME("StatusError")));
1509
}
1510
1511
save_path_items.push_back(item);
1512
}
1513
1514
external_paths->set_title(TTR("Extract Materials to Resource Files"));
1515
external_paths->set_ok_button_text(TTR("Extract"));
1516
} break;
1517
case ACTION_CHOOSE_MESH_SAVE_PATHS: {
1518
for (const KeyValue<String, MeshData> &E : mesh_map) {
1519
MeshData &md = mesh_map[E.key];
1520
1521
TreeItem *item = external_path_tree->create_item(root);
1522
1523
String name = md.mesh_node->get_text(0);
1524
1525
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
1526
item->set_icon(0, get_editor_theme_icon(SNAME("MeshItem")));
1527
item->set_text(0, name);
1528
1529
if (md.has_import_id) {
1530
if (md.settings.has("save_to_file/enabled") && bool(md.settings["save_to_file/enabled"])) {
1531
item->set_text(2, TTR("Already Saving"));
1532
item->set_tooltip_text(2, TTR("This mesh already saves to an external resource, no action will be taken."));
1533
} else {
1534
item->set_metadata(0, E.key);
1535
item->set_editable(0, true);
1536
item->set_checked(0, true);
1537
name = name.validate_filename();
1538
String path = p_path.path_join(name);
1539
if (external_extension_type->get_selected() == 0) {
1540
path += ".tres";
1541
} else {
1542
path += ".res";
1543
}
1544
1545
item->set_text(1, path);
1546
if (FileAccess::exists(path)) {
1547
item->set_text(2, TTR("Warning: File exists"));
1548
item->set_tooltip_text(2, TTR("Existing file with the same name will be replaced on import."));
1549
item->set_icon(2, get_editor_theme_icon(SNAME("StatusWarning")));
1550
1551
} else {
1552
item->set_text(2, TTR("Will save to new file"));
1553
item->set_icon(2, get_editor_theme_icon(SNAME("StatusSuccess")));
1554
}
1555
1556
item->add_button(1, get_editor_theme_icon(SNAME("Folder")));
1557
}
1558
1559
} else {
1560
item->set_text(2, TTR("No import ID"));
1561
item->set_tooltip_text(2, TTR("Mesh has no name nor any other way to identify on re-import.\nPlease name it or ensure it is exported with an unique ID."));
1562
item->set_icon(2, get_editor_theme_icon(SNAME("StatusError")));
1563
}
1564
1565
save_path_items.push_back(item);
1566
}
1567
1568
external_paths->set_title(TTR("Set paths to save meshes as resource files on Reimport"));
1569
external_paths->set_ok_button_text(TTR("Set Paths"));
1570
} break;
1571
case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: {
1572
for (const KeyValue<String, AnimationData> &E : animation_map) {
1573
AnimationData &ad = animation_map[E.key];
1574
1575
TreeItem *item = external_path_tree->create_item(root);
1576
1577
String name = ad.scene_node->get_text(0);
1578
1579
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
1580
item->set_icon(0, get_editor_theme_icon(SNAME("Animation")));
1581
item->set_text(0, name);
1582
1583
if (ad.settings.has("save_to_file/enabled") && bool(ad.settings["save_to_file/enabled"])) {
1584
item->set_text(2, TTR("Already Saving"));
1585
item->set_tooltip_text(2, TTR("This animation already saves to an external resource, no action will be taken."));
1586
} else {
1587
item->set_metadata(0, E.key);
1588
item->set_editable(0, true);
1589
item->set_checked(0, true);
1590
name = name.validate_filename();
1591
String path = p_path.path_join(name);
1592
if (external_extension_type->get_selected() == 0) {
1593
path += ".tres";
1594
} else {
1595
path += ".res";
1596
}
1597
1598
item->set_text(1, path);
1599
if (FileAccess::exists(path)) {
1600
item->set_text(2, TTR("Warning: File exists"));
1601
item->set_tooltip_text(2, TTR("Existing file with the same name will be replaced on import."));
1602
item->set_icon(2, get_editor_theme_icon(SNAME("StatusWarning")));
1603
1604
} else {
1605
item->set_text(2, TTR("Will save to new file"));
1606
item->set_icon(2, get_editor_theme_icon(SNAME("StatusSuccess")));
1607
}
1608
1609
item->add_button(1, get_editor_theme_icon(SNAME("Folder")));
1610
}
1611
1612
save_path_items.push_back(item);
1613
}
1614
1615
external_paths->set_title(TTR("Set paths to save animations as resource files on Reimport"));
1616
external_paths->set_ok_button_text(TTR("Set Paths"));
1617
1618
} break;
1619
}
1620
1621
external_paths->popup_centered_ratio();
1622
}
1623
1624
void SceneImportSettingsDialog::_save_dir_confirm() {
1625
for (int i = 0; i < save_path_items.size(); i++) {
1626
TreeItem *item = save_path_items[i];
1627
if (!item->is_checked(0)) {
1628
continue; //ignore
1629
}
1630
String path = item->get_text(1);
1631
String uid_path = path;
1632
if (path.begins_with("uid://")) {
1633
path = ResourceUID::uid_to_path(uid_path);
1634
}
1635
if (!path.is_resource_file()) {
1636
continue;
1637
}
1638
1639
String id = item->get_metadata(0);
1640
1641
switch (current_action) {
1642
case ACTION_EXTRACT_MATERIALS: {
1643
ERR_CONTINUE(!material_map.has(id));
1644
MaterialData &md = material_map[id];
1645
1646
Error err = ResourceSaver::save(md.material, path);
1647
if (err != OK) {
1648
EditorNode::get_singleton()->add_io_error(TTR("Can't make material external to file, write error:") + "\n\t" + path);
1649
continue;
1650
}
1651
uid_path = ResourceUID::path_to_uid(path);
1652
1653
md.settings["use_external/enabled"] = true;
1654
md.settings["use_external/path"] = uid_path;
1655
md.settings["use_external/fallback_path"] = path;
1656
1657
} break;
1658
case ACTION_CHOOSE_MESH_SAVE_PATHS: {
1659
ERR_CONTINUE(!mesh_map.has(id));
1660
MeshData &md = mesh_map[id];
1661
1662
md.settings["save_to_file/enabled"] = true;
1663
md.settings["save_to_file/path"] = uid_path;
1664
md.settings["save_to_file/fallback_path"] = path;
1665
} break;
1666
case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: {
1667
ERR_CONTINUE(!animation_map.has(id));
1668
AnimationData &ad = animation_map[id];
1669
1670
ad.settings["save_to_file/enabled"] = true;
1671
ad.settings["save_to_file/path"] = uid_path;
1672
ad.settings["save_to_file/fallback_path"] = path;
1673
1674
} break;
1675
}
1676
}
1677
1678
if (current_action == ACTION_EXTRACT_MATERIALS) {
1679
//as this happens right now, the scene needs to be saved and reimported.
1680
_re_import();
1681
open_settings(base_path);
1682
} else {
1683
scene_import_settings_data->notify_property_list_changed();
1684
}
1685
}
1686
1687
SceneImportSettingsDialog::SceneImportSettingsDialog() {
1688
singleton = this;
1689
_resource_importer_scene = memnew(ResourceImporterScene("PackedScene"));
1690
1691
VBoxContainer *main_vb = memnew(VBoxContainer);
1692
add_child(main_vb);
1693
HBoxContainer *menu_hb = memnew(HBoxContainer);
1694
main_vb->add_child(menu_hb);
1695
1696
action_menu = memnew(MenuButton);
1697
action_menu->set_text(TTR("Actions..."));
1698
// Style the MenuButton like a regular Button to make it more noticeable.
1699
action_menu->set_flat(false);
1700
action_menu->set_focus_mode(Control::FOCUS_ALL);
1701
menu_hb->add_child(action_menu);
1702
1703
action_menu->get_popup()->add_item(TTR("Extract Materials"), ACTION_EXTRACT_MATERIALS);
1704
action_menu->get_popup()->add_separator();
1705
action_menu->get_popup()->add_item(TTR("Set Animation Save Paths"), ACTION_CHOOSE_ANIMATION_SAVE_PATHS);
1706
action_menu->get_popup()->add_item(TTR("Set Mesh Save Paths"), ACTION_CHOOSE_MESH_SAVE_PATHS);
1707
1708
action_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &SceneImportSettingsDialog::_menu_callback));
1709
1710
tree_split = memnew(HSplitContainer);
1711
main_vb->add_child(tree_split);
1712
tree_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1713
1714
data_mode = memnew(TabContainer);
1715
tree_split->add_child(data_mode);
1716
data_mode->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
1717
data_mode->set_theme_type_variation("TabContainerOdd");
1718
1719
property_split = memnew(HSplitContainer);
1720
tree_split->add_child(property_split);
1721
property_split->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1722
1723
scene_tree = memnew(Tree);
1724
scene_tree->set_name(TTR("Scene"));
1725
scene_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
1726
data_mode->add_child(scene_tree);
1727
scene_tree->connect("cell_selected", callable_mp(this, &SceneImportSettingsDialog::_scene_tree_selected));
1728
1729
mesh_tree = memnew(Tree);
1730
mesh_tree->set_name(TTR("Meshes"));
1731
mesh_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
1732
data_mode->add_child(mesh_tree);
1733
mesh_tree->set_hide_root(true);
1734
mesh_tree->connect("cell_selected", callable_mp(this, &SceneImportSettingsDialog::_mesh_tree_selected));
1735
1736
material_tree = memnew(Tree);
1737
material_tree->set_name(TTR("Materials"));
1738
material_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
1739
data_mode->add_child(material_tree);
1740
material_tree->connect("cell_selected", callable_mp(this, &SceneImportSettingsDialog::_material_tree_selected));
1741
1742
material_tree->set_hide_root(true);
1743
1744
VBoxContainer *vp_vb = memnew(VBoxContainer);
1745
vp_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1746
vp_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1747
vp_vb->set_anchors_and_offsets_preset(Control::LayoutPreset::PRESET_FULL_RECT);
1748
property_split->add_child(vp_vb);
1749
1750
SubViewportContainer *vp_container = memnew(SubViewportContainer);
1751
vp_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1752
vp_container->set_custom_minimum_size(Size2(10, 10));
1753
vp_container->set_stretch(true);
1754
vp_container->connect(SceneStringName(gui_input), callable_mp(this, &SceneImportSettingsDialog::_viewport_input));
1755
vp_vb->add_child(vp_container);
1756
1757
base_viewport = memnew(SubViewport);
1758
vp_container->add_child(base_viewport);
1759
1760
animation_preview = memnew(PanelContainer);
1761
animation_preview->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1762
vp_vb->add_child(animation_preview);
1763
animation_preview->hide();
1764
1765
HBoxContainer *animation_hbox = memnew(HBoxContainer);
1766
animation_preview->add_child(animation_hbox);
1767
1768
animation_play_button = memnew(Button);
1769
animation_hbox->add_child(animation_play_button);
1770
animation_play_button->set_flat(true);
1771
animation_play_button->set_accessibility_name(TTRC("Selected Animation Play/Pause"));
1772
animation_play_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
1773
animation_play_button->set_shortcut(ED_SHORTCUT("scene_import_settings/play_selected_animation", TTRC("Selected Animation Play/Pause"), Key::SPACE));
1774
animation_play_button->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_play_animation));
1775
1776
animation_stop_button = memnew(Button);
1777
animation_hbox->add_child(animation_stop_button);
1778
animation_stop_button->set_flat(true);
1779
animation_stop_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
1780
animation_stop_button->set_tooltip_text(TTR("Selected Animation Stop"));
1781
animation_stop_button->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_stop_current_animation));
1782
1783
animation_slider = memnew(HSlider);
1784
animation_hbox->add_child(animation_slider);
1785
animation_slider->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1786
animation_slider->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1787
animation_slider->set_max(1.0);
1788
animation_slider->set_step(1.0 / 100.0);
1789
animation_slider->set_value_no_signal(0.0);
1790
animation_slider->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
1791
animation_slider->set_accessibility_name(TTRC("Animation"));
1792
animation_slider->connect(SceneStringName(value_changed), callable_mp(this, &SceneImportSettingsDialog::_animation_slider_value_changed));
1793
1794
animation_toggle_skeleton_visibility = memnew(Button);
1795
animation_hbox->add_child(animation_toggle_skeleton_visibility);
1796
animation_toggle_skeleton_visibility->set_toggle_mode(true);
1797
animation_toggle_skeleton_visibility->set_theme_type_variation("FlatButton");
1798
animation_toggle_skeleton_visibility->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
1799
animation_toggle_skeleton_visibility->set_tooltip_text(TTR("Toggle Animation Skeleton Visibility"));
1800
1801
animation_toggle_skeleton_visibility->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_animation_update_skeleton_visibility));
1802
1803
base_viewport->set_use_own_world_3d(true);
1804
1805
HBoxContainer *viewport_hbox = memnew(HBoxContainer);
1806
vp_container->add_child(viewport_hbox);
1807
viewport_hbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 2);
1808
1809
viewport_hbox->add_spacer();
1810
1811
VBoxContainer *vb_light = memnew(VBoxContainer);
1812
vb_light->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1813
viewport_hbox->add_child(vb_light);
1814
1815
light_rotate_switch = memnew(Button);
1816
light_rotate_switch->set_theme_type_variation("PreviewLightButton");
1817
light_rotate_switch->set_toggle_mode(true);
1818
light_rotate_switch->set_pressed(true);
1819
light_rotate_switch->set_tooltip_text(TTR("Rotate Lights With Model"));
1820
light_rotate_switch->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_on_light_rotate_switch_pressed));
1821
vb_light->add_child(light_rotate_switch);
1822
1823
light_1_switch = memnew(Button);
1824
light_1_switch->set_theme_type_variation("PreviewLightButton");
1825
light_1_switch->set_toggle_mode(true);
1826
light_1_switch->set_pressed(true);
1827
light_1_switch->set_tooltip_text(TTR("Primary Light"));
1828
light_1_switch->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_on_light_1_switch_pressed));
1829
vb_light->add_child(light_1_switch);
1830
1831
light_2_switch = memnew(Button);
1832
light_2_switch->set_theme_type_variation("PreviewLightButton");
1833
light_2_switch->set_toggle_mode(true);
1834
light_2_switch->set_pressed(true);
1835
light_2_switch->set_tooltip_text(TTR("Secondary Light"));
1836
light_2_switch->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_on_light_2_switch_pressed));
1837
vb_light->add_child(light_2_switch);
1838
1839
camera = memnew(Camera3D);
1840
base_viewport->add_child(camera);
1841
camera->make_current();
1842
1843
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) {
1844
camera_attributes.instantiate();
1845
camera->set_attributes(camera_attributes);
1846
}
1847
1848
// Use a grayscale gradient sky to avoid skewing the preview towards a specific color,
1849
// but still allow shaded areas to be easily distinguished (using the ambient and reflected light).
1850
// This also helps the user orient themselves in the preview, since the bottom of the sky is black
1851
// and the top of the sky is white.
1852
procedural_sky_material.instantiate();
1853
procedural_sky_material->set_sky_top_color(Color(1, 1, 1));
1854
procedural_sky_material->set_sky_horizon_color(Color(0.5, 0.5, 0.5));
1855
procedural_sky_material->set_ground_horizon_color(Color(0.5, 0.5, 0.5));
1856
procedural_sky_material->set_ground_bottom_color(Color(0, 0, 0));
1857
procedural_sky_material->set_sky_curve(2.0);
1858
procedural_sky_material->set_ground_curve(0.5);
1859
// Hide the sun from the sky.
1860
procedural_sky_material->set_sun_angle_max(0.0);
1861
sky.instantiate();
1862
sky->set_material(procedural_sky_material);
1863
environment.instantiate();
1864
environment->set_background(Environment::BG_SKY);
1865
environment->set_sky(sky);
1866
// A custom FOV must be specified, as an orthogonal camera is used for the preview.
1867
environment->set_sky_custom_fov(50.0);
1868
camera->set_environment(environment);
1869
1870
light1 = memnew(DirectionalLight3D);
1871
light1->set_transform(Transform3D(Basis::looking_at(Vector3(-1, -1, -1))));
1872
light1->set_shadow(true);
1873
camera->add_child(light1);
1874
1875
light2 = memnew(DirectionalLight3D);
1876
light2->set_transform(Transform3D(Basis::looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1))));
1877
light2->set_color(Color(0.5f, 0.5f, 0.5f));
1878
camera->add_child(light2);
1879
1880
{
1881
Ref<StandardMaterial3D> selection_mat;
1882
selection_mat.instantiate();
1883
selection_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
1884
selection_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
1885
selection_mat->set_albedo(Color(1, 0.8, 1.0));
1886
1887
Ref<SurfaceTool> st;
1888
st.instantiate();
1889
st->begin(Mesh::PRIMITIVE_LINES);
1890
1891
AABB base_aabb;
1892
base_aabb.size = Vector3(1, 1, 1);
1893
1894
for (int i = 0; i < 12; i++) {
1895
Vector3 a, b;
1896
base_aabb.get_edge(i, a, b);
1897
1898
st->add_vertex(a);
1899
st->add_vertex(a.lerp(b, 0.2));
1900
st->add_vertex(b);
1901
st->add_vertex(b.lerp(a, 0.2));
1902
}
1903
1904
selection_mesh.instantiate();
1905
st->commit(selection_mesh);
1906
selection_mesh->surface_set_material(0, selection_mat);
1907
1908
node_selected = memnew(MeshInstance3D);
1909
node_selected->set_mesh(selection_mesh);
1910
node_selected->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF);
1911
base_viewport->add_child(node_selected);
1912
node_selected->hide();
1913
}
1914
1915
{
1916
mesh_preview = memnew(MeshInstance3D);
1917
base_viewport->add_child(mesh_preview);
1918
mesh_preview->hide();
1919
1920
material_preview.instantiate();
1921
}
1922
1923
{
1924
collider_mat.instantiate();
1925
collider_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
1926
collider_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
1927
collider_mat->set_albedo(Color(0.5, 0.5, 1.0));
1928
}
1929
1930
{
1931
bones_mesh_preview = memnew(MeshInstance3D);
1932
bones_mesh_preview->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF);
1933
bones_mesh_preview->set_skeleton_path(NodePath());
1934
base_viewport->add_child(bones_mesh_preview);
1935
}
1936
1937
inspector = memnew(EditorInspector);
1938
inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
1939
inspector->connect(SNAME("property_edited"), callable_mp(this, &SceneImportSettingsDialog::_inspector_property_edited));
1940
// Display the same tooltips as in the Import dock.
1941
inspector->set_object_class(ResourceImporterScene::get_class_static());
1942
inspector->set_use_doc_hints(true);
1943
inspector->set_theme_type_variation(SNAME("EditorInspectorForeground"));
1944
1945
property_split->add_child(inspector);
1946
1947
scene_import_settings_data = memnew(SceneImportSettingsData);
1948
1949
set_ok_button_text(TTR("Reimport"));
1950
set_cancel_button_text(TTR("Close"));
1951
1952
external_paths = memnew(ConfirmationDialog);
1953
add_child(external_paths);
1954
external_path_tree = memnew(Tree);
1955
external_paths->add_child(external_path_tree);
1956
external_path_tree->connect("button_clicked", callable_mp(this, &SceneImportSettingsDialog::_browse_save_callback));
1957
external_paths->connect(SceneStringName(confirmed), callable_mp(this, &SceneImportSettingsDialog::_save_dir_confirm));
1958
external_path_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
1959
external_path_tree->set_columns(3);
1960
external_path_tree->set_column_titles_visible(true);
1961
external_path_tree->set_column_expand(0, true);
1962
external_path_tree->set_column_custom_minimum_width(0, 100 * EDSCALE);
1963
external_path_tree->set_column_title(0, TTR("Resource"));
1964
external_path_tree->set_column_expand(1, true);
1965
external_path_tree->set_column_custom_minimum_width(1, 100 * EDSCALE);
1966
external_path_tree->set_column_title(1, TTR("Path"));
1967
external_path_tree->set_column_expand(2, false);
1968
external_path_tree->set_column_custom_minimum_width(2, 200 * EDSCALE);
1969
external_path_tree->set_column_title(2, TTR("Status"));
1970
save_path = memnew(EditorFileDialog);
1971
save_path->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
1972
HBoxContainer *extension_hb = memnew(HBoxContainer);
1973
save_path->get_vbox()->add_child(extension_hb);
1974
extension_hb->add_spacer();
1975
extension_hb->add_child(memnew(Label(TTR("Save Extension:"))));
1976
external_extension_type = memnew(OptionButton);
1977
extension_hb->add_child(external_extension_type);
1978
external_extension_type->add_item(TTR("Text: *.tres"));
1979
external_extension_type->add_item(TTR("Binary: *.res"));
1980
external_path_tree->set_hide_root(true);
1981
add_child(save_path);
1982
1983
item_save_path = memnew(EditorFileDialog);
1984
item_save_path->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
1985
item_save_path->add_filter("*.tres", TTR("Text Resource"));
1986
item_save_path->add_filter("*.res", TTR("Binary Resource"));
1987
add_child(item_save_path);
1988
item_save_path->connect("file_selected", callable_mp(this, &SceneImportSettingsDialog::_save_path_changed));
1989
1990
save_path->connect("dir_selected", callable_mp(this, &SceneImportSettingsDialog::_save_dir_callback));
1991
1992
update_view_timer = memnew(Timer);
1993
update_view_timer->set_wait_time(0.2);
1994
update_view_timer->set_one_shot(true);
1995
update_view_timer->connect("timeout", callable_mp(this, &SceneImportSettingsDialog::_update_view_gizmos));
1996
add_child(update_view_timer);
1997
}
1998
1999
SceneImportSettingsDialog::~SceneImportSettingsDialog() {
2000
memdelete(scene_import_settings_data);
2001
memdelete(_resource_importer_scene);
2002
}
2003
2004