merge_group_editor_plugin.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /**************************************************************************/
  2. /* merge_group_editor_plugin.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 "merge_group_editor_plugin.h"
  31. #include "core/io/resource_saver.h"
  32. #include "editor/spatial_editor_gizmos.h"
  33. #include "scene/3d/mesh_instance.h"
  34. #include "scene/resources/merging_tool.h"
  35. #include "scene/resources/packed_scene.h"
  36. EditorProgress *MergeGroupEditorPlugin::tmp_progress = nullptr;
  37. EditorProgress *MergeGroupEditorPlugin::tmp_subprogress = nullptr;
  38. void MergeGroupEditorBakeDialog::_bake_confirm() {
  39. _owner_plugin->dialog_pressed_bake(_single_scene->is_pressed(), (int)_subscene_polycount_threshold->get_value());
  40. }
  41. void MergeGroupEditorBakeDialog::_add_bake_checkbox(Node *p_parent, CheckBox **pp_checkbox, const String &p_text, const String &p_tooltip, bool p_default) {
  42. *pp_checkbox = memnew(CheckBox);
  43. (*pp_checkbox)->set_text(TTR(p_text));
  44. (*pp_checkbox)->set_tooltip(TTR(p_tooltip));
  45. (*pp_checkbox)->set_pressed(p_default);
  46. p_parent->add_child(*pp_checkbox);
  47. }
  48. void MergeGroupEditorBakeDialog::_add_bake_spinbox(VBoxContainer *p_parent, SpinBox **pp_spinbox, const String &p_text, const String &p_tooltip, int32_t p_min, int32_t p_max, int32_t p_step, int32_t p_default) {
  49. *pp_spinbox = memnew(SpinBox);
  50. (*pp_spinbox)->set_min(p_min);
  51. (*pp_spinbox)->set_max(p_max);
  52. (*pp_spinbox)->set_step(p_step);
  53. (*pp_spinbox)->set_value(p_default);
  54. (*pp_spinbox)->set_tooltip(p_tooltip);
  55. p_parent->add_margin_child(TTR(p_text), *pp_spinbox);
  56. }
  57. void MergeGroupEditorBakeDialog::fill_merge_group_params(MergeGroup &r_merge_group) {
  58. r_merge_group.set_param_enabled(MergeGroup::PARAM_ENABLED_SHADOW_PROXY, _shadow_proxy->is_pressed());
  59. r_merge_group.set_param_enabled(MergeGroup::PARAM_ENABLED_CONVERT_CSGS, _convert_csgs->is_pressed());
  60. r_merge_group.set_param_enabled(MergeGroup::PARAM_ENABLED_CONVERT_GRIDMAPS, _convert_gridmaps->is_pressed());
  61. r_merge_group.set_param_enabled(MergeGroup::PARAM_ENABLED_COMBINE_SURFACES, _combine_surfaces->is_pressed());
  62. r_merge_group.set_param_enabled(MergeGroup::PARAM_ENABLED_CLEAN_MESHES, _clean_meshes->is_pressed());
  63. r_merge_group.set_param(MergeGroup::PARAM_GROUP_SIZE, _group_size->get_value());
  64. r_merge_group.set_param(MergeGroup::PARAM_SPLITS_HORIZONTAL, _splits_horz->get_value());
  65. r_merge_group.set_param(MergeGroup::PARAM_SPLITS_VERTICAL, _splits_vert->get_value());
  66. r_merge_group.set_param(MergeGroup::PARAM_MIN_SPLIT_POLY_COUNT, _min_split_poly_count->get_value());
  67. }
  68. MergeGroupEditorBakeDialog::MergeGroupEditorBakeDialog(MergeGroupEditorPlugin *p_owner) {
  69. _owner_plugin = p_owner;
  70. set_title("Bake MergeGroup");
  71. get_ok()->connect("pressed", this, "_bake_confirm");
  72. VBoxContainer *vbc = memnew(VBoxContainer);
  73. add_child(vbc);
  74. _add_bake_checkbox(vbc, &_single_scene, "Single Scene", "Save meshes as a single scene or separate scenes.", true);
  75. _add_bake_spinbox(vbc, &_subscene_polycount_threshold, "Subscene Polycount Threshold:", "Threshold polycount to split scenes into separate subscenes.", 0, 1024 * 128, 64, 1024);
  76. _add_bake_checkbox(vbc, &_shadow_proxy, "Shadow Proxy", "Separate meshes for shadow rendering.", true);
  77. _add_bake_checkbox(vbc, &_convert_csgs, "Convert CSGs", "Convert CSGs to meshes so they can be merged.", true);
  78. _add_bake_checkbox(vbc, &_convert_gridmaps, "Convert GridMaps", "Convert GridMaps to meshes so they can be merged.");
  79. _add_bake_checkbox(vbc, &_combine_surfaces, "Combine Surfaces", "Combine merged surfaces to form an \"ubermesh\".", true);
  80. _add_bake_checkbox(vbc, &_clean_meshes, "Clean Meshes", "Clean geometry data, remove degenerate triangles.");
  81. _add_bake_spinbox(vbc, &_group_size, "Group Size", "When non-zero, only local groups of the corresponding number of meshes will be merged.", 0, 128, 1, 0);
  82. _add_bake_spinbox(vbc, &_splits_horz, "Splits Horizontal", "When above 1, the final meshes will be split into a grid on the horizontal axis.", 0, 16, 1, 1);
  83. _add_bake_spinbox(vbc, &_splits_vert, "Splits Vertical", "When above 1, the final meshes will be split into a grid on the vertical axis.", 0, 16, 1, 1);
  84. _add_bake_spinbox(vbc, &_min_split_poly_count, "Min Split Polycount", "When splitting by grid, only meshes above this minimum polycount will be split.", 0, 1024 * 128, 256, 1024);
  85. }
  86. void MergeGroupEditorBakeDialog::_bind_methods() {
  87. ClassDB::bind_method("_bake_confirm", &MergeGroupEditorBakeDialog::_bake_confirm);
  88. }
  89. //////////////////////////////////////////////////////////
  90. bool MergeGroupEditorPlugin::bake_func_step(float p_progress, const String &p_description, void *, bool p_force_refresh) {
  91. if (!tmp_progress) {
  92. tmp_progress = memnew(EditorProgress("bake_merge_group", TTR("Bake MergeGroup"), 1000, true));
  93. ERR_FAIL_NULL_V(tmp_progress, false);
  94. }
  95. return tmp_progress->step(p_description, p_progress * 1000, p_force_refresh);
  96. }
  97. bool MergeGroupEditorPlugin::bake_func_substep(float p_progress, const String &p_description, void *, bool p_force_refresh) {
  98. if (!tmp_subprogress) {
  99. tmp_subprogress = memnew(EditorProgress("bake_merge_group_substep", "", 1000, true));
  100. ERR_FAIL_NULL_V(tmp_subprogress, false);
  101. }
  102. return tmp_subprogress->step(p_description, p_progress * 1000, p_force_refresh);
  103. }
  104. void MergeGroupEditorPlugin::bake_func_end(uint32_t p_time_started) {
  105. if (tmp_progress != nullptr) {
  106. memdelete(tmp_progress);
  107. tmp_progress = nullptr;
  108. }
  109. if (tmp_subprogress != nullptr) {
  110. memdelete(tmp_subprogress);
  111. tmp_subprogress = nullptr;
  112. }
  113. const int time_taken = (OS::get_singleton()->get_ticks_msec() - p_time_started) * 0.001;
  114. if (time_taken >= 1) {
  115. // Only print a message and request attention if baking took at least 1 second.
  116. print_line(vformat("Done baking MergeGroup in %02d:%02d:%02d.", time_taken / 3600, (time_taken % 3600) / 60, time_taken % 60));
  117. // Request attention in case the user was doing something else.
  118. OS::get_singleton()->request_attention();
  119. }
  120. }
  121. void MergeGroupEditorPlugin::dialog_pressed_bake(bool p_single_scene, int p_subscene_polycount_threshold) {
  122. if (!_merge_group) {
  123. return;
  124. }
  125. _params.single_scene = p_single_scene;
  126. _params.subscene_polycount_threshold = p_subscene_polycount_threshold;
  127. Node *root = _merge_group->get_tree()->get_edited_scene_root();
  128. if (root == _merge_group) {
  129. EditorNode::get_singleton()->show_warning(TTR("Cannot bake MergeGroup when it is scene root."));
  130. return;
  131. }
  132. String scene_path = _merge_group->get_filename();
  133. if (scene_path == String()) {
  134. scene_path = _merge_group->get_owner()->get_filename();
  135. }
  136. if (scene_path == String()) {
  137. EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for merge group.\nSave your scene and try again."));
  138. return;
  139. }
  140. scene_path = scene_path.get_basename() + ".tscn";
  141. file_dialog->set_current_path(scene_path);
  142. file_dialog->popup_centered_ratio();
  143. }
  144. bool MergeGroupEditorPlugin::_find_visual_instances_recursive(Node *p_node) {
  145. if (Object::cast_to<VisualInstance>(p_node)) {
  146. return true;
  147. }
  148. for (int n = 0; n < p_node->get_child_count(); n++) {
  149. if (_find_visual_instances_recursive(p_node->get_child(n))) {
  150. return true;
  151. }
  152. }
  153. return false;
  154. }
  155. void MergeGroupEditorPlugin::_bake() {
  156. ERR_FAIL_NULL(_merge_group);
  157. // If the merge group does not contain any VisualInstance children, flag an error.
  158. if (!_find_visual_instances_recursive(_merge_group)) {
  159. EditorNode::get_singleton()->show_warning(TTR("MergeGroup does not contain any VisualInstances.\nCannot Bake."));
  160. return;
  161. }
  162. bake_dialog->show();
  163. }
  164. Spatial *MergeGroupEditorPlugin::_convert_merge_group_to_spatial(MergeGroup *p_merge_group) {
  165. ERR_FAIL_NULL_V(p_merge_group, nullptr);
  166. Node *parent = p_merge_group->get_parent();
  167. ERR_FAIL_NULL_V(parent, nullptr);
  168. Spatial *spatial = memnew(Spatial);
  169. parent->add_child(spatial);
  170. // They can't have the same name at the same time.
  171. String name = p_merge_group->get_name();
  172. p_merge_group->set_name(name + "_temp");
  173. spatial->set_name(name);
  174. // Identical transforms.
  175. spatial->set_transform(p_merge_group->get_transform());
  176. // Move the children.
  177. // GODOT is abysmally bad at moving children in order unfortunately.
  178. // So reverse order for now.
  179. for (int n = p_merge_group->get_child_count() - 1; n >= 0; n--) {
  180. Node *child = p_merge_group->get_child(n);
  181. p_merge_group->remove_child(child);
  182. spatial->add_child(child);
  183. }
  184. // Change owners.
  185. MergingTool::_invalidate_owner_recursive(spatial, nullptr, p_merge_group->get_owner());
  186. // Delete AND detach the merge group from the tree.
  187. p_merge_group->_delete_node(p_merge_group);
  188. return spatial;
  189. }
  190. void MergeGroupEditorPlugin::_bake_select_file(const String &p_file) {
  191. if (!_merge_group) {
  192. return;
  193. }
  194. // Special treatment for scene root.
  195. Node *root = _merge_group->get_tree()->get_edited_scene_root();
  196. // Cannot bake scene root.
  197. if (root == _merge_group) {
  198. EditorNode::get_singleton()->show_warning(TTR("Cannot bake scene root.\nPlease move to a branch before baking."));
  199. ERR_FAIL_COND(root == _merge_group);
  200. }
  201. Node *parent = _merge_group->get_parent();
  202. ERR_FAIL_NULL(parent);
  203. // Disallow saving to the same scene as the root scene
  204. // (this is usually user error), and prevents losing work.
  205. if (root->get_filename() == p_file) {
  206. EditorNode::get_singleton()->show_warning(TTR("Cannot save to the currently edited scene.\nPlease save to a different scene."));
  207. ERR_FAIL_COND(root->get_filename() == p_file);
  208. }
  209. // Ensure to reset this when exiting this routine!
  210. // Spatial gizmos, especially for meshes are very expensive
  211. // in terms of RAM and performance, and are totally
  212. // unnecessary for temporary objects
  213. SpatialEditor::_prevent_gizmo_generation = true;
  214. #ifdef GODOT_MERGING_VERBOSE
  215. MergingTool::debug_branch(_merge_group, "START_SCENE");
  216. #endif
  217. Spatial *hanger = memnew(Spatial);
  218. hanger->set_name("hanger");
  219. parent->add_child(hanger);
  220. hanger->set_owner(_merge_group->get_owner());
  221. uint32_t time_start = OS::get_singleton()->get_ticks_msec();
  222. bake_func_step(0.0, "Duplicating Branch", nullptr, true);
  223. _duplicate_branch(_merge_group, hanger);
  224. // Temporarily hide source branch, to speed things up in the editor.
  225. bool was_visible = _merge_group->is_visible_in_tree();
  226. _merge_group->hide();
  227. MergeGroup *merge_group_copy = Object::cast_to<MergeGroup>(hanger->get_child(0));
  228. // Set the parameters in the copy mergegroup to those set up in the bake dialog.
  229. bake_dialog->fill_merge_group_params(*merge_group_copy);
  230. if (merge_group_copy->merge_meshes_in_editor()) {
  231. if (!bake_func_step(1.0, "Saving Scene", nullptr, true)) {
  232. // Convert the merge node to a spatial..
  233. // Once baked we don't want baked scenes to be merged AGAIN
  234. // when incorporated into scenes.
  235. Spatial *final_branch = _convert_merge_group_to_spatial(merge_group_copy);
  236. // Only save if not cancelled by user.
  237. _save_scene(final_branch, p_file);
  238. }
  239. }
  240. #ifdef GODOT_MERGING_VERBOSE
  241. MergingTool::debug_branch(hanger, "END_SCENE");
  242. #endif
  243. // Finished.
  244. hanger->queue_delete();
  245. _merge_group->set_visible(was_visible);
  246. SpatialEditor::_prevent_gizmo_generation = false;
  247. bake_func_end(time_start);
  248. }
  249. void MergeGroupEditorPlugin::_remove_queue_deleted_nodes_recursive(Node *p_node) {
  250. if (p_node->is_queued_for_deletion()) {
  251. p_node->get_parent()->remove_child(p_node);
  252. return;
  253. }
  254. for (int n = p_node->get_child_count() - 1; n >= 0; n--) {
  255. _remove_queue_deleted_nodes_recursive(p_node->get_child(n));
  256. }
  257. }
  258. uint32_t MergeGroupEditorPlugin::_get_mesh_poly_count(const MeshInstance &p_mi) const {
  259. Ref<Mesh> rmesh = p_mi.get_mesh();
  260. if (rmesh.is_valid()) {
  261. return rmesh->get_triangle_count();
  262. }
  263. return 0;
  264. }
  265. bool MergeGroupEditorPlugin::_replace_with_branch_scene(const String &p_file, Node *p_base) {
  266. Node *old_owner = p_base->get_owner();
  267. Ref<PackedScene> sdata = ResourceLoader::load(p_file);
  268. if (!sdata.is_valid()) {
  269. ERR_PRINT("Error loading scene from \"" + p_file + "\".");
  270. return false;
  271. }
  272. Node *instanced_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
  273. if (!instanced_scene) {
  274. ERR_PRINT("Error instancing scene from \"" + p_file + "\".");
  275. return false;
  276. }
  277. Node *parent = p_base->get_parent();
  278. int pos = p_base->get_index();
  279. parent->remove_child(p_base);
  280. parent->add_child(instanced_scene);
  281. parent->move_child(instanced_scene, pos);
  282. List<Node *> owned;
  283. p_base->get_owned_by(p_base->get_owner(), &owned);
  284. Array owners;
  285. for (List<Node *>::Element *F = owned.front(); F; F = F->next()) {
  286. owners.push_back(F->get());
  287. }
  288. instanced_scene->set_owner(old_owner);
  289. p_base->queue_delete();
  290. return true;
  291. }
  292. bool MergeGroupEditorPlugin::_save_subscene(Node *p_root, Node *p_branch, String p_base_filename, int &r_subscene_count) {
  293. bake_func_substep(0.0, p_branch->get_name(), nullptr, false);
  294. Node *scene_root = p_root;
  295. Map<Node *, Node *> reown;
  296. reown[scene_root] = p_branch;
  297. Node *copy = p_branch->duplicate_and_reown(reown);
  298. bake_func_substep(0.2, p_branch->get_name(), nullptr, false);
  299. if (copy) {
  300. Ref<PackedScene> sdata = memnew(PackedScene);
  301. Error err = sdata->pack(copy);
  302. memdelete(copy);
  303. bake_func_substep(0.4, p_branch->get_name(), nullptr, false);
  304. if (err != OK) {
  305. WARN_PRINT("Couldn't save subscene \"" + p_branch->get_name() + "\" . Likely dependencies (instances) couldn't be satisfied. Saving as part of main scene instead.");
  306. return false;
  307. }
  308. String filename = p_base_filename + "_" + itos(r_subscene_count++) + ".scn";
  309. #ifdef DEV_ENABLED
  310. print_verbose("Save subscene: " + filename);
  311. #endif
  312. err = ResourceSaver::save(filename, sdata, ResourceSaver::FLAG_COMPRESS);
  313. bake_func_substep(0.6, p_branch->get_name(), nullptr, false);
  314. if (err != OK) {
  315. WARN_PRINT("Error saving subscene \"" + p_branch->get_name() + "\" , saving as part of main scene instead.");
  316. return false;
  317. }
  318. _replace_with_branch_scene(filename, p_branch);
  319. } else {
  320. WARN_PRINT("Error duplicating subscene \"" + p_branch->get_name() + "\" , saving as part of main scene instead.");
  321. return false;
  322. }
  323. return true;
  324. }
  325. void MergeGroupEditorPlugin::_save_mesh_subscenes_recursive(Node *p_root, Node *p_node, String p_base_filename, int &r_subscene_count) {
  326. if (p_node->is_queued_for_deletion()) {
  327. return;
  328. }
  329. // Is a subscene already?
  330. if (p_node->get_filename().length() && (p_node != p_root)) {
  331. return;
  332. }
  333. // Is it a mesh instance?
  334. MeshInstance *mi = Object::cast_to<MeshInstance>(p_node);
  335. // Don't save subscenes for trivially sized meshes.
  336. if (mi && (!_params.subscene_polycount_threshold || ((int)_get_mesh_poly_count(*mi) > _params.subscene_polycount_threshold))) {
  337. // Save as subscene.
  338. if (_save_subscene(p_root, p_node, p_base_filename, r_subscene_count)) {
  339. return;
  340. }
  341. }
  342. // Replaced subscenes will be added to the last child, so going in reverse order is necessary.
  343. for (int n = p_node->get_child_count() - 1; n >= 0; n--) {
  344. _save_mesh_subscenes_recursive(p_root, p_node->get_child(n), p_base_filename, r_subscene_count);
  345. }
  346. }
  347. void MergeGroupEditorPlugin::_push_mesh_data_to_gpu_recursive(Node *p_node) {
  348. // Is it a mesh instance?
  349. MeshInstance *mi = Object::cast_to<MeshInstance>(p_node);
  350. if (mi) {
  351. Ref<Mesh> rmesh = mi->get_mesh();
  352. if (rmesh.is_valid()) {
  353. rmesh->set_storage_mode(Mesh::STORAGE_MODE_GPU);
  354. }
  355. }
  356. for (int n = 0; n < p_node->get_child_count(); n++) {
  357. _push_mesh_data_to_gpu_recursive(p_node->get_child(n));
  358. }
  359. }
  360. bool MergeGroupEditorPlugin::_save_scene(Node *p_branch, String p_filename) {
  361. // For some reason the saving machinery doesn't deal well with nodes queued for deletion,
  362. // so we will remove them from the scene tree (as risk of leaks, but the queue delete machinery
  363. // should still work when detached).
  364. _remove_queue_deleted_nodes_recursive(p_branch);
  365. // All mesh data must be on the GPU for the Mesh saving routines to work.
  366. _push_mesh_data_to_gpu_recursive(p_branch);
  367. Node *scene_root = p_branch->get_tree()->get_edited_scene_root();
  368. Map<Node *, Node *> reown;
  369. reown[scene_root] = p_branch;
  370. Node *copy = p_branch->duplicate_and_reown(reown);
  371. #ifdef GODOT_MERGING_VERBOSE
  372. MergingTool::debug_branch(copy, "SAVE SCENE:");
  373. #endif
  374. bake_func_substep(0.0, p_filename, nullptr, false);
  375. if (copy) {
  376. // Save any large meshes as compressed resources.
  377. if (!_params.single_scene) {
  378. int subscene_count = 0;
  379. _save_mesh_subscenes_recursive(copy, copy, p_filename.get_basename(), subscene_count);
  380. }
  381. bake_func_substep(0.4, p_filename, nullptr, false);
  382. Ref<PackedScene> sdata = memnew(PackedScene);
  383. Error err = sdata->pack(copy);
  384. memdelete(copy);
  385. bake_func_substep(0.8, p_filename, nullptr, false);
  386. if (err != OK) {
  387. EditorNode::get_singleton()->show_warning(TTR("Couldn't save merged branch.\nLikely dependencies (instances) couldn't be satisfied."));
  388. return false;
  389. }
  390. err = ResourceSaver::save(p_filename, sdata, ResourceSaver::FLAG_COMPRESS);
  391. if (err != OK) {
  392. EditorNode::get_singleton()->show_warning(TTR("Error saving scene."));
  393. return false;
  394. }
  395. } else {
  396. EditorNode::get_singleton()->show_warning(TTR("Error duplicating scene to save it."));
  397. return false;
  398. }
  399. return true;
  400. }
  401. void MergeGroupEditorPlugin::_duplicate_branch(Node *p_branch, Node *p_new_parent) {
  402. Node *dup = p_branch->duplicate();
  403. ERR_FAIL_NULL(dup);
  404. p_new_parent->add_child(dup);
  405. Node *new_owner = p_new_parent->get_owner();
  406. dup->set_owner(new_owner);
  407. MergingTool::_invalidate_owner_recursive(dup, nullptr, new_owner);
  408. }
  409. void MergeGroupEditorPlugin::edit(Object *p_object) {
  410. MergeGroup *mg = Object::cast_to<MergeGroup>(p_object);
  411. if (!mg) {
  412. return;
  413. }
  414. _merge_group = mg;
  415. }
  416. bool MergeGroupEditorPlugin::handles(Object *p_object) const {
  417. return p_object->is_class("MergeGroup");
  418. }
  419. void MergeGroupEditorPlugin::make_visible(bool p_visible) {
  420. if (p_visible) {
  421. button_bake->show();
  422. } else {
  423. button_bake->hide();
  424. bake_dialog->hide();
  425. }
  426. }
  427. void MergeGroupEditorPlugin::_bind_methods() {
  428. ClassDB::bind_method("_bake", &MergeGroupEditorPlugin::_bake);
  429. ClassDB::bind_method("_bake_select_file", &MergeGroupEditorPlugin::_bake_select_file);
  430. }
  431. MergeGroupEditorPlugin::MergeGroupEditorPlugin(EditorNode *p_node) {
  432. editor = p_node;
  433. button_bake = memnew(ToolButton);
  434. button_bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons"));
  435. button_bake->set_text(TTR("Bake"));
  436. button_bake->set_tooltip(TTR("Bake MergeGroup to Scene."));
  437. button_bake->hide();
  438. button_bake->connect("pressed", this, "_bake");
  439. file_dialog = memnew(EditorFileDialog);
  440. file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
  441. file_dialog->add_filter("*.tscn ; " + TTR("Scene"));
  442. file_dialog->add_filter("*.scn ; " + TTR("Binary Scene"));
  443. file_dialog->set_title(TTR("Save Merged Scene As..."));
  444. file_dialog->connect("file_selected", this, "_bake_select_file");
  445. button_bake->add_child(file_dialog);
  446. bake_dialog = memnew(MergeGroupEditorBakeDialog(this));
  447. bake_dialog->set_anchors_and_margins_preset(Control::PRESET_CENTER);
  448. bake_dialog->hide();
  449. button_bake->add_child(bake_dialog);
  450. add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, button_bake);
  451. _merge_group = nullptr;
  452. MergeGroup::bake_step_function = bake_func_step;
  453. MergeGroup::bake_substep_function = bake_func_substep;
  454. MergeGroup::bake_end_function = bake_func_end;
  455. }
  456. MergeGroupEditorPlugin::~MergeGroupEditorPlugin() {
  457. }