scene_import_settings.cpp 70 KB

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