1 | /**************************************************************************/ |
2 | /* navigation_mesh_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 | |
31 | #include "navigation_mesh_editor_plugin.h" |
32 | |
33 | #ifdef TOOLS_ENABLED |
34 | |
35 | #include "core/io/marshalls.h" |
36 | #include "core/io/resource_saver.h" |
37 | #include "editor/editor_node.h" |
38 | #include "editor/editor_string_names.h" |
39 | #include "scene/3d/mesh_instance_3d.h" |
40 | #include "scene/3d/navigation_region_3d.h" |
41 | #include "scene/gui/box_container.h" |
42 | #include "scene/gui/button.h" |
43 | #include "scene/gui/dialogs.h" |
44 | #include "scene/gui/label.h" |
45 | #include "scene/resources/navigation_mesh_source_geometry_data_3d.h" |
46 | |
47 | void NavigationMeshEditor::_node_removed(Node *p_node) { |
48 | if (p_node == node) { |
49 | node = nullptr; |
50 | |
51 | hide(); |
52 | } |
53 | } |
54 | |
55 | void NavigationMeshEditor::_notification(int p_what) { |
56 | switch (p_what) { |
57 | case NOTIFICATION_ENTER_TREE: { |
58 | button_bake->set_icon(get_theme_icon(SNAME("Bake" ), EditorStringName(EditorIcons))); |
59 | button_reset->set_icon(get_theme_icon(SNAME("Reload" ), EditorStringName(EditorIcons))); |
60 | } break; |
61 | } |
62 | } |
63 | |
64 | void NavigationMeshEditor::_bake_pressed() { |
65 | button_bake->set_pressed(false); |
66 | |
67 | ERR_FAIL_COND(!node); |
68 | Ref<NavigationMesh> navmesh = node->get_navigation_mesh(); |
69 | if (!navmesh.is_valid()) { |
70 | err_dialog->set_text(TTR("A NavigationMesh resource must be set or created for this node to work." )); |
71 | err_dialog->popup_centered(); |
72 | return; |
73 | } |
74 | |
75 | String path = navmesh->get_path(); |
76 | if (!path.is_resource_file()) { |
77 | int srpos = path.find("::" ); |
78 | if (srpos != -1) { |
79 | String base = path.substr(0, srpos); |
80 | if (ResourceLoader::get_resource_type(base) == "PackedScene" ) { |
81 | if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) { |
82 | err_dialog->set_text(TTR("Cannot generate navigation mesh because it does not belong to the edited scene. Make it unique first." )); |
83 | err_dialog->popup_centered(); |
84 | return; |
85 | } |
86 | } else { |
87 | if (FileAccess::exists(base + ".import" )) { |
88 | err_dialog->set_text(TTR("Cannot generate navigation mesh because it belongs to a resource which was imported." )); |
89 | err_dialog->popup_centered(); |
90 | return; |
91 | } |
92 | } |
93 | } |
94 | } else { |
95 | if (FileAccess::exists(path + ".import" )) { |
96 | err_dialog->set_text(TTR("Cannot generate navigation mesh because the resource was imported from another type." )); |
97 | err_dialog->popup_centered(); |
98 | return; |
99 | } |
100 | } |
101 | |
102 | node->bake_navigation_mesh(false); |
103 | |
104 | node->update_gizmos(); |
105 | } |
106 | |
107 | void NavigationMeshEditor::_clear_pressed() { |
108 | if (node) { |
109 | if (node->get_navigation_mesh().is_valid()) { |
110 | node->get_navigation_mesh()->clear(); |
111 | } |
112 | } |
113 | |
114 | button_bake->set_pressed(false); |
115 | bake_info->set_text("" ); |
116 | |
117 | if (node) { |
118 | node->update_gizmos(); |
119 | } |
120 | } |
121 | |
122 | void NavigationMeshEditor::edit(NavigationRegion3D *p_nav_region) { |
123 | if (p_nav_region == nullptr || node == p_nav_region) { |
124 | return; |
125 | } |
126 | |
127 | node = p_nav_region; |
128 | } |
129 | |
130 | void NavigationMeshEditor::_bind_methods() { |
131 | } |
132 | |
133 | NavigationMeshEditor::NavigationMeshEditor() { |
134 | bake_hbox = memnew(HBoxContainer); |
135 | |
136 | button_bake = memnew(Button); |
137 | button_bake->set_flat(true); |
138 | bake_hbox->add_child(button_bake); |
139 | button_bake->set_toggle_mode(true); |
140 | button_bake->set_text(TTR("Bake NavigationMesh" )); |
141 | button_bake->set_tooltip_text(TTR("Bakes the NavigationMesh by first parsing the scene for source geometry and then creating the navigation mesh vertices and polygons." )); |
142 | button_bake->connect("pressed" , callable_mp(this, &NavigationMeshEditor::_bake_pressed)); |
143 | |
144 | button_reset = memnew(Button); |
145 | button_reset->set_flat(true); |
146 | bake_hbox->add_child(button_reset); |
147 | button_reset->set_text(TTR("Clear NavigationMesh" )); |
148 | button_reset->set_tooltip_text(TTR("Clears the internal NavigationMesh vertices and polygons." )); |
149 | button_reset->connect("pressed" , callable_mp(this, &NavigationMeshEditor::_clear_pressed)); |
150 | |
151 | bake_info = memnew(Label); |
152 | bake_hbox->add_child(bake_info); |
153 | |
154 | err_dialog = memnew(AcceptDialog); |
155 | add_child(err_dialog); |
156 | node = nullptr; |
157 | } |
158 | |
159 | NavigationMeshEditor::~NavigationMeshEditor() { |
160 | } |
161 | |
162 | void NavigationMeshEditorPlugin::edit(Object *p_object) { |
163 | navigation_mesh_editor->edit(Object::cast_to<NavigationRegion3D>(p_object)); |
164 | } |
165 | |
166 | bool NavigationMeshEditorPlugin::handles(Object *p_object) const { |
167 | return p_object->is_class("NavigationRegion3D" ); |
168 | } |
169 | |
170 | void NavigationMeshEditorPlugin::make_visible(bool p_visible) { |
171 | if (p_visible) { |
172 | navigation_mesh_editor->show(); |
173 | navigation_mesh_editor->bake_hbox->show(); |
174 | } else { |
175 | navigation_mesh_editor->hide(); |
176 | navigation_mesh_editor->bake_hbox->hide(); |
177 | navigation_mesh_editor->edit(nullptr); |
178 | } |
179 | } |
180 | |
181 | NavigationMeshEditorPlugin::NavigationMeshEditorPlugin() { |
182 | navigation_mesh_editor = memnew(NavigationMeshEditor); |
183 | EditorNode::get_singleton()->get_main_screen_control()->add_child(navigation_mesh_editor); |
184 | add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, navigation_mesh_editor->bake_hbox); |
185 | navigation_mesh_editor->hide(); |
186 | navigation_mesh_editor->bake_hbox->hide(); |
187 | } |
188 | |
189 | NavigationMeshEditorPlugin::~NavigationMeshEditorPlugin() { |
190 | } |
191 | |
192 | #endif // TOOLS_ENABLED |
193 | |