| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | /**************************************************************************/ | 
					
						
							|  |  |  | /*  navigation_obstacle_3d_editor_plugin.cpp                              */ | 
					
						
							|  |  |  | /**************************************************************************/ | 
					
						
							|  |  |  | /*                         This file is part of:                          */ | 
					
						
							|  |  |  | /*                             GODOT ENGINE                               */ | 
					
						
							|  |  |  | /*                        https://godotengine.org                         */ | 
					
						
							|  |  |  | /**************************************************************************/ | 
					
						
							|  |  |  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | 
					
						
							|  |  |  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | 
					
						
							|  |  |  | /*                                                                        */ | 
					
						
							|  |  |  | /* Permission is hereby granted, free of charge, to any person obtaining  */ | 
					
						
							|  |  |  | /* a copy of this software and associated documentation files (the        */ | 
					
						
							|  |  |  | /* "Software"), to deal in the Software without restriction, including    */ | 
					
						
							|  |  |  | /* without limitation the rights to use, copy, modify, merge, publish,    */ | 
					
						
							|  |  |  | /* distribute, sublicense, and/or sell copies of the Software, and to     */ | 
					
						
							|  |  |  | /* permit persons to whom the Software is furnished to do so, subject to  */ | 
					
						
							|  |  |  | /* the following conditions:                                              */ | 
					
						
							|  |  |  | /*                                                                        */ | 
					
						
							|  |  |  | /* The above copyright notice and this permission notice shall be         */ | 
					
						
							|  |  |  | /* included in all copies or substantial portions of the Software.        */ | 
					
						
							|  |  |  | /*                                                                        */ | 
					
						
							|  |  |  | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */ | 
					
						
							|  |  |  | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */ | 
					
						
							|  |  |  | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ | 
					
						
							|  |  |  | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */ | 
					
						
							|  |  |  | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */ | 
					
						
							|  |  |  | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */ | 
					
						
							|  |  |  | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */ | 
					
						
							|  |  |  | /**************************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "navigation_obstacle_3d_editor_plugin.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "core/math/geometry_2d.h"
 | 
					
						
							|  |  |  | #include "editor/editor_node.h"
 | 
					
						
							|  |  |  | #include "editor/editor_settings.h"
 | 
					
						
							| 
									
										
										
										
											2023-09-13 13:14:07 +02:00
										 |  |  | #include "editor/editor_string_names.h"
 | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | #include "editor/editor_undo_redo_manager.h"
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | #include "editor/plugins/node_3d_editor_plugin.h"
 | 
					
						
							|  |  |  | #include "scene/3d/navigation_obstacle_3d.h"
 | 
					
						
							|  |  |  | #include "scene/gui/button.h"
 | 
					
						
							|  |  |  | #include "scene/gui/dialogs.h"
 | 
					
						
							|  |  |  | #include "servers/navigation_server_3d.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool NavigationObstacle3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { | 
					
						
							|  |  |  | 	return Object::cast_to<NavigationObstacle3D>(p_spatial) != nullptr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | String NavigationObstacle3DGizmoPlugin::get_gizmo_name() const { | 
					
						
							|  |  |  | 	return "NavigationObstacle3D"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NavigationObstacle3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { | 
					
						
							|  |  |  | 	p_gizmo->clear(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!p_gizmo->is_selected() && get_state() == HIDDEN) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	NavigationObstacle3D *obstacle = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!obstacle) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const Vector<Vector3> &vertices = obstacle->get_vertices(); | 
					
						
							|  |  |  | 	if (vertices.is_empty()) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	float height = obstacle->get_height(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 	const Basis safe_basis = Basis(Vector3(0.0, 1.0, 0.0), obstacle->get_global_rotation().y, obstacle->get_global_basis().get_scale().abs().maxf(0.001)); | 
					
						
							|  |  |  | 	const Basis gbi = obstacle->get_global_basis().inverse(); | 
					
						
							|  |  |  | 	const Basis safe_global_basis = gbi * safe_basis; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	const int vertex_count = vertices.size(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Vector<Vector3> lines_mesh_vertices; | 
					
						
							|  |  |  | 	lines_mesh_vertices.resize(vertex_count * 8); | 
					
						
							|  |  |  | 	Vector3 *lines_mesh_vertices_ptrw = lines_mesh_vertices.ptrw(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int vertex_index = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int i = 0; i < vertex_count; i++) { | 
					
						
							|  |  |  | 		Vector3 point = vertices[i]; | 
					
						
							|  |  |  | 		Vector3 next_point = vertices[(i + 1) % vertex_count]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 		Vector3 direction = safe_basis.xform(next_point.direction_to(point)); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		Vector3 arrow_dir = direction.cross(Vector3(0.0, 1.0, 0.0)); | 
					
						
							|  |  |  | 		Vector3 edge_middle = point + ((next_point - point) * 0.5); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 		// Ensure vector stays perpendicular even when scaled non-uniformly.
 | 
					
						
							|  |  |  | 		lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(edge_middle); | 
					
						
							|  |  |  | 		lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(edge_middle) + gbi.xform(arrow_dir) * 0.5; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 		lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(point); | 
					
						
							|  |  |  | 		lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(next_point); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 		lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(point.x, height, point.z)); | 
					
						
							|  |  |  | 		lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(next_point.x, height, next_point.z)); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 		lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(point); | 
					
						
							|  |  |  | 		lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(point.x, height, point.z)); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	NavigationServer3D *ns3d = NavigationServer3D::get_singleton(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-21 19:44:00 +01:00
										 |  |  | 	if (obstacle->are_vertices_valid()) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		p_gizmo->add_lines(lines_mesh_vertices, ns3d->get_debug_navigation_avoidance_static_obstacle_pushout_edge_material()); | 
					
						
							| 
									
										
										
										
											2024-12-21 19:44:00 +01:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		p_gizmo->add_lines(lines_mesh_vertices, ns3d->get_debug_navigation_avoidance_static_obstacle_pushin_edge_material()); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	p_gizmo->add_collision_segments(lines_mesh_vertices); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (p_gizmo->is_selected()) { | 
					
						
							|  |  |  | 		NavigationObstacle3DEditorPlugin::singleton->redraw(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool NavigationObstacle3DGizmoPlugin::can_be_hidden() const { | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int NavigationObstacle3DGizmoPlugin::get_priority() const { | 
					
						
							|  |  |  | 	return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int NavigationObstacle3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const { | 
					
						
							|  |  |  | 	if (NavigationObstacle3DEditorPlugin::singleton->get_mode() != 1) { // MODE_EDIT
 | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); | 
					
						
							|  |  |  | 	ERR_FAIL_NULL_V(obstacle_node, -1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 	const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001); | 
					
						
							|  |  |  | 	const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position()); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	const Vector<Vector3> &vertices = obstacle_node->get_vertices(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int idx = 0; idx < vertices.size(); ++idx) { | 
					
						
							|  |  |  | 		Vector3 pos = gt.xform(vertices[idx]); | 
					
						
							|  |  |  | 		if (p_camera->unproject_position(pos).distance_to(p_point) < 20) { | 
					
						
							|  |  |  | 			return idx; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Vector<int> NavigationObstacle3DGizmoPlugin::subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const { | 
					
						
							|  |  |  | 	Vector<int> contained_points; | 
					
						
							|  |  |  | 	if (NavigationObstacle3DEditorPlugin::singleton->get_mode() != 1) { // MODE_EDIT
 | 
					
						
							|  |  |  | 		return contained_points; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); | 
					
						
							|  |  |  | 	ERR_FAIL_NULL_V(obstacle_node, contained_points); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 	const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001); | 
					
						
							|  |  |  | 	const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position()); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	const Vector<Vector3> &vertices = obstacle_node->get_vertices(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int idx = 0; idx < vertices.size(); ++idx) { | 
					
						
							|  |  |  | 		Vector3 pos = gt.xform(vertices[idx]); | 
					
						
							|  |  |  | 		bool is_contained_in_frustum = true; | 
					
						
							|  |  |  | 		for (int i = 0; i < p_frustum.size(); ++i) { | 
					
						
							|  |  |  | 			if (p_frustum[i].distance_to(pos) > 0) { | 
					
						
							|  |  |  | 				is_contained_in_frustum = false; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (is_contained_in_frustum) { | 
					
						
							|  |  |  | 			contained_points.push_back(idx); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return contained_points; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Transform3D NavigationObstacle3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const { | 
					
						
							|  |  |  | 	NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); | 
					
						
							|  |  |  | 	ERR_FAIL_NULL_V(obstacle_node, Transform3D()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const Vector<Vector3> &vertices = obstacle_node->get_vertices(); | 
					
						
							|  |  |  | 	ERR_FAIL_INDEX_V(p_id, vertices.size(), Transform3D()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 	const Basis safe_basis_inverse = Basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y, obstacle_node->get_global_basis().get_scale().abs().maxf(0.001)).inverse(); | 
					
						
							|  |  |  | 	Transform3D subgizmo_transform = Transform3D(Basis(), safe_basis_inverse.xform(vertices[p_id])); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	return subgizmo_transform; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NavigationObstacle3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) { | 
					
						
							|  |  |  | 	NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); | 
					
						
							|  |  |  | 	ERR_FAIL_NULL(obstacle_node); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 	const Basis safe_basis = Basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y, obstacle_node->get_global_basis().get_scale().abs().maxf(0.001)); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	Vector3 new_vertex_pos = p_transform.origin; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	Vector<Vector3> vertices = obstacle_node->get_vertices(); | 
					
						
							|  |  |  | 	ERR_FAIL_INDEX(p_id, vertices.size()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 	Vector3 vertex = safe_basis.xform(new_vertex_pos); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	vertex.y = 0.0; | 
					
						
							|  |  |  | 	vertices.write[p_id] = vertex; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	obstacle_node->set_vertices(vertices); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NavigationObstacle3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) { | 
					
						
							|  |  |  | 	NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); | 
					
						
							|  |  |  | 	ERR_FAIL_NULL(obstacle_node); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 	const Basis safe_basis = Basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y, obstacle_node->get_global_basis().get_scale().abs().maxf(0.001)); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Vector<Vector3> vertices = obstacle_node->get_vertices(); | 
					
						
							|  |  |  | 	Vector<Vector3> restore_vertices = vertices; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int i = 0; i < p_ids.size(); ++i) { | 
					
						
							|  |  |  | 		const int idx = p_ids[i]; | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 		Vector3 vertex = safe_basis.xform(p_restore[i].origin); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		vertex.y = 0.0; | 
					
						
							|  |  |  | 		restore_vertices.write[idx] = vertex; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (p_cancel) { | 
					
						
							|  |  |  | 		obstacle_node->set_vertices(restore_vertices); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							|  |  |  | 	undo_redo->create_action(TTR("Set Obstacle Vertices")); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(obstacle_node, "set_vertices", vertices); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(obstacle_node, "set_vertices", restore_vertices); | 
					
						
							|  |  |  | 	undo_redo->commit_action(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | NavigationObstacle3DGizmoPlugin::NavigationObstacle3DGizmoPlugin() { | 
					
						
							|  |  |  | 	current_state = VISIBLE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NavigationObstacle3DEditorPlugin::_notification(int p_what) { | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	switch (p_what) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		case NOTIFICATION_ENTER_TREE: { | 
					
						
							|  |  |  | 			_update_theme(); | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		case NOTIFICATION_READY: { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			_update_theme(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 			button_edit->set_pressed(true); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			get_tree()->connect("node_removed", callable_mp(this, &NavigationObstacle3DEditorPlugin::_node_removed)); | 
					
						
							|  |  |  | 			EditorNode::get_singleton()->get_gui_base()->connect(SceneStringName(theme_changed), callable_mp(this, &NavigationObstacle3DEditorPlugin::_update_theme)); | 
					
						
							|  |  |  | 		} break; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		case NOTIFICATION_EXIT_TREE: { | 
					
						
							|  |  |  | 			get_tree()->disconnect("node_removed", callable_mp(this, &NavigationObstacle3DEditorPlugin::_node_removed)); | 
					
						
							|  |  |  | 			EditorNode::get_singleton()->get_gui_base()->disconnect(SceneStringName(theme_changed), callable_mp(this, &NavigationObstacle3DEditorPlugin::_update_theme)); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		} break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | void NavigationObstacle3DEditorPlugin::edit(Object *p_object) { | 
					
						
							|  |  |  | 	obstacle_node = Object::cast_to<NavigationObstacle3D>(p_object); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	RenderingServer *rs = RenderingServer::get_singleton(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (obstacle_node) { | 
					
						
							|  |  |  | 		if (obstacle_node->get_vertices().is_empty()) { | 
					
						
							|  |  |  | 			set_mode(MODE_CREATE); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			set_mode(MODE_EDIT); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		wip_vertices.clear(); | 
					
						
							|  |  |  | 		wip_active = false; | 
					
						
							|  |  |  | 		edited_point = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rs->instance_set_scenario(point_lines_instance_rid, obstacle_node->get_world_3d()->get_scenario()); | 
					
						
							|  |  |  | 		rs->instance_set_scenario(point_handles_instance_rid, obstacle_node->get_world_3d()->get_scenario()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		redraw(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		obstacle_node = nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rs->mesh_clear(point_lines_mesh_rid); | 
					
						
							|  |  |  | 		rs->mesh_clear(point_handle_mesh_rid); | 
					
						
							|  |  |  | 		rs->instance_set_scenario(point_lines_instance_rid, RID()); | 
					
						
							|  |  |  | 		rs->instance_set_scenario(point_handles_instance_rid, RID()); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | bool NavigationObstacle3DEditorPlugin::handles(Object *p_object) const { | 
					
						
							|  |  |  | 	return Object::cast_to<NavigationObstacle3D>(p_object); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NavigationObstacle3DEditorPlugin::make_visible(bool p_visible) { | 
					
						
							|  |  |  | 	if (p_visible) { | 
					
						
							|  |  |  | 		obstacle_editor->show(); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		obstacle_editor->hide(); | 
					
						
							|  |  |  | 		edit(nullptr); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | void NavigationObstacle3DEditorPlugin::action_flip_vertices() { | 
					
						
							|  |  |  | 	if (!obstacle_node) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Vector<Vector3> flipped_vertices = obstacle_node->get_vertices(); | 
					
						
							|  |  |  | 	flipped_vertices.reverse(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	undo_redo->create_action(TTR("Edit Obstacle (Flip Winding)")); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(obstacle_node, "set_vertices", flipped_vertices); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices()); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	undo_redo->commit_action(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	obstacle_node->update_gizmos(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | void NavigationObstacle3DEditorPlugin::action_clear_vertices() { | 
					
						
							|  |  |  | 	if (!obstacle_node) { | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							|  |  |  | 	undo_redo->create_action(TTR("Edit Obstacle (Clear Vertices)")); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(obstacle_node, "set_vertices", Vector<Vector3>()); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices()); | 
					
						
							|  |  |  | 	undo_redo->commit_action(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	obstacle_node->update_gizmos(); | 
					
						
							|  |  |  | 	edit(obstacle_node); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NavigationObstacle3DEditorPlugin::_update_theme() { | 
					
						
							|  |  |  | 	button_create->set_tooltip_text(TTR("Add Vertex")); | 
					
						
							|  |  |  | 	button_edit->set_tooltip_text(TTR("Edit Vertex")); | 
					
						
							|  |  |  | 	button_delete->set_tooltip_text(TTR("Delete Vertex")); | 
					
						
							|  |  |  | 	button_flip->set_tooltip_text(TTR("Flip Winding")); | 
					
						
							|  |  |  | 	button_clear->set_tooltip_text(TTR("Clear Vertices")); | 
					
						
							|  |  |  | 	button_create->set_button_icon(button_create->get_editor_theme_icon(SNAME("CurveCreate"))); | 
					
						
							|  |  |  | 	button_edit->set_button_icon(button_edit->get_editor_theme_icon(SNAME("CurveEdit"))); | 
					
						
							|  |  |  | 	button_delete->set_button_icon(button_delete->get_editor_theme_icon(SNAME("CurveDelete"))); | 
					
						
							|  |  |  | 	button_flip->set_button_icon(button_flip->get_editor_theme_icon(SNAME("FlipWinding"))); | 
					
						
							|  |  |  | 	button_clear->set_button_icon(button_clear->get_editor_theme_icon(SNAME("Clear"))); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NavigationObstacle3DEditorPlugin::_node_removed(Node *p_node) { | 
					
						
							|  |  |  | 	if (obstacle_node == p_node) { | 
					
						
							|  |  |  | 		obstacle_node = nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		RenderingServer *rs = RenderingServer::get_singleton(); | 
					
						
							|  |  |  | 		rs->mesh_clear(point_lines_mesh_rid); | 
					
						
							|  |  |  | 		rs->mesh_clear(point_handle_mesh_rid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		obstacle_editor->hide(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NavigationObstacle3DEditorPlugin::set_mode(int p_option) { | 
					
						
							|  |  |  | 	if (p_option == NavigationObstacle3DEditorPlugin::ACTION_FLIP) { | 
					
						
							|  |  |  | 		button_flip->set_pressed(false); | 
					
						
							|  |  |  | 		action_flip_vertices(); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (p_option == NavigationObstacle3DEditorPlugin::ACTION_CLEAR) { | 
					
						
							|  |  |  | 		button_clear->set_pressed(false); | 
					
						
							|  |  |  | 		button_clear_dialog->reset_size(); | 
					
						
							|  |  |  | 		button_clear_dialog->popup_centered(); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mode = p_option; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	button_create->set_pressed(p_option == NavigationObstacle3DEditorPlugin::MODE_CREATE); | 
					
						
							|  |  |  | 	button_edit->set_pressed(p_option == NavigationObstacle3DEditorPlugin::MODE_EDIT); | 
					
						
							|  |  |  | 	button_delete->set_pressed(p_option == NavigationObstacle3DEditorPlugin::MODE_DELETE); | 
					
						
							|  |  |  | 	button_flip->set_pressed(false); | 
					
						
							|  |  |  | 	button_clear->set_pressed(false); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NavigationObstacle3DEditorPlugin::_wip_cancel() { | 
					
						
							|  |  |  | 	wip_vertices.clear(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	wip_active = false; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	edited_point = -1; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	redraw(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | void NavigationObstacle3DEditorPlugin::_wip_close() { | 
					
						
							|  |  |  | 	ERR_FAIL_NULL_MSG(obstacle_node, "Edited NavigationObstacle3D is not valid."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Vector<Vector2> wip_2d_vertices; | 
					
						
							|  |  |  | 	wip_2d_vertices.resize(wip_vertices.size()); | 
					
						
							|  |  |  | 	for (int i = 0; i < wip_vertices.size(); i++) { | 
					
						
							|  |  |  | 		const Vector3 &vert = wip_vertices[i]; | 
					
						
							|  |  |  | 		wip_2d_vertices.write[i] = Vector2(vert.x, vert.z); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(wip_2d_vertices); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!triangulated_polygon_2d_indices.is_empty()) { | 
					
						
							|  |  |  | 		EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							|  |  |  | 		undo_redo->create_action(TTR("Set Obstacle Vertices")); | 
					
						
							|  |  |  | 		undo_redo->add_do_method(obstacle_node, "set_vertices", wip_vertices); | 
					
						
							|  |  |  | 		undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices()); | 
					
						
							|  |  |  | 		undo_redo->commit_action(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		wip_vertices.clear(); | 
					
						
							|  |  |  | 		wip_active = false; | 
					
						
							|  |  |  | 		//mode = MODE_EDIT;
 | 
					
						
							|  |  |  | 		NavigationObstacle3DEditorPlugin::singleton->set_mode(NavigationObstacle3DEditorPlugin::MODE_EDIT); | 
					
						
							|  |  |  | 		button_edit->set_pressed(true); | 
					
						
							|  |  |  | 		button_create->set_pressed(false); | 
					
						
							|  |  |  | 		edited_point = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | EditorPlugin::AfterGUIInput NavigationObstacle3DEditorPlugin::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) { | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	if (!obstacle_node) { | 
					
						
							|  |  |  | 		return EditorPlugin::AFTER_GUI_INPUT_PASS; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	if (!obstacle_node->is_visible_in_tree()) { | 
					
						
							|  |  |  | 		return EditorPlugin::AFTER_GUI_INPUT_PASS; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Ref<InputEventMouse> mouse_event = p_event; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mouse_event.is_null()) { | 
					
						
							|  |  |  | 		return EditorPlugin::AFTER_GUI_INPUT_PASS; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Ref<InputEventMouseButton> mb = p_event; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mb.is_valid()) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		Vector2 mouse_position = mb->get_position(); | 
					
						
							|  |  |  | 		Vector3 ray_from = p_camera->project_ray_origin(mouse_position); | 
					
						
							|  |  |  | 		Vector3 ray_dir = p_camera->project_ray_normal(mouse_position); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 		const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001); | 
					
						
							|  |  |  | 		const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position()); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		Transform3D gi = gt.affine_inverse(); | 
					
						
							|  |  |  | 		Plane projection_plane(Vector3(0.0, 1.0, 0.0), gt.origin); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		Vector3 spoint; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		if (!projection_plane.intersects_ray(ray_from, ray_dir, &spoint)) { | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 			return EditorPlugin::AFTER_GUI_INPUT_PASS; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		spoint = gi.xform(spoint); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		Vector3 cpoint = Vector3(spoint.x, 0.0, spoint.z); | 
					
						
							|  |  |  | 		Vector<Vector3> obstacle_vertices = obstacle_node->get_vertices(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (mode) { | 
					
						
							|  |  |  | 			case MODE_CREATE: { | 
					
						
							|  |  |  | 				if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 					if (obstacle_vertices.size() >= 3) { | 
					
						
							|  |  |  | 						int closest_idx = -1; | 
					
						
							|  |  |  | 						Vector2 closest_edge_point; | 
					
						
							|  |  |  | 						real_t closest_dist = 1e10; | 
					
						
							|  |  |  | 						for (int i = 0; i < obstacle_vertices.size(); i++) { | 
					
						
							|  |  |  | 							Vector2 points[2] = { | 
					
						
							|  |  |  | 								p_camera->unproject_position(gt.xform(obstacle_vertices[i])), | 
					
						
							|  |  |  | 								p_camera->unproject_position(gt.xform(obstacle_vertices[(i + 1) % obstacle_vertices.size()])) | 
					
						
							|  |  |  | 							}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							Vector2 cp = Geometry2D::get_closest_point_to_segment(mouse_position, points); | 
					
						
							|  |  |  | 							if (cp.distance_squared_to(points[0]) < grab_threshold || cp.distance_squared_to(points[1]) < grab_threshold) { | 
					
						
							|  |  |  | 								continue; // Skip edge as clicked point is too close to existing vertex.
 | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							real_t d = cp.distance_to(mouse_position); | 
					
						
							|  |  |  | 							if (d < closest_dist && d < grab_threshold) { | 
					
						
							|  |  |  | 								closest_dist = d; | 
					
						
							|  |  |  | 								closest_edge_point = cp; | 
					
						
							|  |  |  | 								closest_idx = i; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (closest_idx >= 0) { | 
					
						
							|  |  |  | 							edited_point = -1; | 
					
						
							|  |  |  | 							Vector3 _ray_from = p_camera->project_ray_origin(closest_edge_point); | 
					
						
							|  |  |  | 							Vector3 _ray_dir = p_camera->project_ray_normal(closest_edge_point); | 
					
						
							|  |  |  | 							Vector3 edge_intersection_point; | 
					
						
							|  |  |  | 							if (projection_plane.intersects_ray(_ray_from, _ray_dir, &edge_intersection_point)) { | 
					
						
							|  |  |  | 								edge_intersection_point = gi.xform(edge_intersection_point); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 								EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							|  |  |  | 								undo_redo->create_action(TTR("Edit Obstacle (Add Vertex)")); | 
					
						
							|  |  |  | 								undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_vertices); | 
					
						
							|  |  |  | 								obstacle_vertices.insert(closest_idx + 1, edge_intersection_point); | 
					
						
							|  |  |  | 								undo_redo->add_do_method(obstacle_node, "set_vertices", obstacle_vertices); | 
					
						
							|  |  |  | 								undo_redo->commit_action(); | 
					
						
							|  |  |  | 								redraw(); | 
					
						
							|  |  |  | 								return EditorPlugin::AFTER_GUI_INPUT_STOP; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 					if (!wip_active) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 						wip_vertices.clear(); | 
					
						
							|  |  |  | 						wip_vertices.push_back(cpoint); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 						wip_active = true; | 
					
						
							|  |  |  | 						edited_point_pos = cpoint; | 
					
						
							|  |  |  | 						snap_ignore = false; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 						redraw(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 						edited_point = 1; | 
					
						
							|  |  |  | 						return EditorPlugin::AFTER_GUI_INPUT_STOP; | 
					
						
							|  |  |  | 					} else { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 						if (wip_vertices.size() > 1 && p_camera->unproject_position(gt.xform(wip_vertices[0])).distance_to(mouse_position) < grab_threshold) { | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 							_wip_close(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							return EditorPlugin::AFTER_GUI_INPUT_STOP; | 
					
						
							|  |  |  | 						} else { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 							wip_vertices.push_back(cpoint); | 
					
						
							|  |  |  | 							edited_point = wip_vertices.size(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 							snap_ignore = false; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 							redraw(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 							return EditorPlugin::AFTER_GUI_INPUT_STOP; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && wip_active) { | 
					
						
							|  |  |  | 					_wip_close(); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			} break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			case MODE_EDIT: { | 
					
						
							|  |  |  | 				if (mb->get_button_index() == MouseButton::LEFT) { | 
					
						
							|  |  |  | 					if (mb->is_pressed()) { | 
					
						
							|  |  |  | 						if (mb->is_ctrl_pressed()) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 							if (obstacle_vertices.size() < 3) { | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 								undo_redo->create_action(TTR("Edit Obstacle (Add Vertex)")); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices()); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 								obstacle_vertices.push_back(cpoint); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								undo_redo->commit_action(); | 
					
						
							|  |  |  | 								return EditorPlugin::AFTER_GUI_INPUT_STOP; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							//search edges
 | 
					
						
							|  |  |  | 							int closest_idx = -1; | 
					
						
							|  |  |  | 							Vector2 closest_pos; | 
					
						
							|  |  |  | 							real_t closest_dist = 1e10; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 							for (int i = 0; i < obstacle_vertices.size(); i++) { | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								Vector2 points[2] = { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 									p_camera->unproject_position(gt.xform(obstacle_vertices[i])), | 
					
						
							|  |  |  | 									p_camera->unproject_position(gt.xform(obstacle_vertices[(i + 1) % obstacle_vertices.size()])) | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 								Vector2 cp = Geometry2D::get_closest_point_to_segment(mouse_position, points); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) { | 
					
						
							|  |  |  | 									continue; //not valid to reuse point
 | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 								real_t d = cp.distance_to(mouse_position); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								if (d < closest_dist && d < grab_threshold) { | 
					
						
							|  |  |  | 									closest_dist = d; | 
					
						
							|  |  |  | 									closest_pos = cp; | 
					
						
							|  |  |  | 									closest_idx = i; | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							if (closest_idx >= 0) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 								pre_move_edit = obstacle_vertices; | 
					
						
							|  |  |  | 								obstacle_vertices.insert(closest_idx + 1, cpoint); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								edited_point = closest_idx + 1; | 
					
						
							|  |  |  | 								edited_point_pos = cpoint; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 								obstacle_node->set_vertices(obstacle_vertices); | 
					
						
							|  |  |  | 								redraw(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								snap_ignore = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 								return EditorPlugin::AFTER_GUI_INPUT_STOP; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							int closest_idx = -1; | 
					
						
							|  |  |  | 							Vector2 closest_pos; | 
					
						
							|  |  |  | 							real_t closest_dist = 1e10; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 							for (int i = 0; i < obstacle_vertices.size(); i++) { | 
					
						
							|  |  |  | 								Vector2 cp = p_camera->unproject_position(gt.xform(obstacle_vertices[i])); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 								real_t d = cp.distance_to(mouse_position); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								if (d < closest_dist && d < grab_threshold) { | 
					
						
							|  |  |  | 									closest_dist = d; | 
					
						
							|  |  |  | 									closest_pos = cp; | 
					
						
							|  |  |  | 									closest_idx = i; | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							if (closest_idx >= 0) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 								pre_move_edit = obstacle_vertices; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								edited_point = closest_idx; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 								edited_point_pos = obstacle_vertices[closest_idx]; | 
					
						
							|  |  |  | 								redraw(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 								snap_ignore = false; | 
					
						
							|  |  |  | 								return EditorPlugin::AFTER_GUI_INPUT_STOP; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						snap_ignore = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						if (edited_point != -1) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 							ERR_FAIL_INDEX_V(edited_point, obstacle_vertices.size(), EditorPlugin::AFTER_GUI_INPUT_PASS); | 
					
						
							|  |  |  | 							obstacle_vertices.write[edited_point] = edited_point_pos; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 							EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 							undo_redo->create_action(TTR("Edit Obstacle (Move Vertex)")); | 
					
						
							|  |  |  | 							undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices()); | 
					
						
							|  |  |  | 							undo_redo->add_do_method(obstacle_node, "set_vertices", obstacle_vertices); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 							undo_redo->commit_action(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							edited_point = -1; | 
					
						
							|  |  |  | 							return EditorPlugin::AFTER_GUI_INPUT_STOP; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			} break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			case MODE_DELETE: { | 
					
						
							|  |  |  | 				if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 					int closest_idx = -1; | 
					
						
							|  |  |  | 					real_t closest_dist = 1e10; | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 					for (int i = 0; i < obstacle_vertices.size(); i++) { | 
					
						
							|  |  |  | 						Vector2 point = p_camera->unproject_position(gt.xform(obstacle_vertices[i])); | 
					
						
							|  |  |  | 						real_t d = point.distance_to(mouse_position); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 						if (d < closest_dist && d < grab_threshold) { | 
					
						
							|  |  |  | 							closest_dist = d; | 
					
						
							|  |  |  | 							closest_idx = i; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (closest_idx >= 0) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 						edited_point = -1; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 						EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 						undo_redo->create_action(TTR("Edit Obstacle (Remove Vertex)")); | 
					
						
							|  |  |  | 						undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_vertices); | 
					
						
							|  |  |  | 						obstacle_vertices.remove_at(closest_idx); | 
					
						
							|  |  |  | 						undo_redo->add_do_method(obstacle_node, "set_vertices", obstacle_vertices); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 						undo_redo->commit_action(); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 						redraw(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 						return EditorPlugin::AFTER_GUI_INPUT_STOP; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			} break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Ref<InputEventMouseMotion> mm = p_event; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mm.is_valid()) { | 
					
						
							|  |  |  | 		if (edited_point != -1 && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			Vector2 mouse_position = mm->get_position(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			Vector3 ray_from = p_camera->project_ray_origin(mouse_position); | 
					
						
							|  |  |  | 			Vector3 ray_dir = p_camera->project_ray_normal(mouse_position); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 			const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001); | 
					
						
							|  |  |  | 			const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position()); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			Transform3D gi = gt.affine_inverse(); | 
					
						
							|  |  |  | 			Plane projection_plane(Vector3(0.0, 1.0, 0.0), gt.origin); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			Vector3 intersection_point; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (!projection_plane.intersects_ray(ray_from, ray_dir, &intersection_point)) { | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 				return EditorPlugin::AFTER_GUI_INPUT_PASS; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			intersection_point = gi.xform(intersection_point); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			Vector2 cpoint(intersection_point.x, intersection_point.z); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (snap_ignore && !Input::get_singleton()->is_key_pressed(Key::CTRL)) { | 
					
						
							|  |  |  | 				snap_ignore = false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (!snap_ignore && Node3DEditor::get_singleton()->is_snap_enabled()) { | 
					
						
							| 
									
										
										
										
											2024-03-03 14:37:52 +01:00
										 |  |  | 				cpoint = cpoint.snappedf(Node3DEditor::get_singleton()->get_translate_snap()); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			edited_point_pos = Vector3(cpoint.x, 0.0, cpoint.y); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			redraw(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	Ref<InputEventKey> k = p_event; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	if (k.is_valid() && k->is_pressed()) { | 
					
						
							|  |  |  | 		if (wip_active && k->get_keycode() == Key::ENTER) { | 
					
						
							|  |  |  | 			_wip_close(); | 
					
						
							|  |  |  | 		} else if (wip_active && k->get_keycode() == Key::ESCAPE) { | 
					
						
							|  |  |  | 			_wip_cancel(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	return EditorPlugin::AFTER_GUI_INPUT_PASS; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | void NavigationObstacle3DEditorPlugin::redraw() { | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	if (!obstacle_node) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	RenderingServer *rs = RenderingServer::get_singleton(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rs->mesh_clear(point_lines_mesh_rid); | 
					
						
							|  |  |  | 	rs->mesh_clear(point_handle_mesh_rid); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	if (!obstacle_node->is_visible_in_tree()) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Vector<Vector3> edited_vertices; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (wip_active) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		edited_vertices = wip_vertices; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		edited_vertices = obstacle_node->get_vertices(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	if (edited_vertices.is_empty()) { | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	Array point_lines_mesh_array; | 
					
						
							|  |  |  | 	point_lines_mesh_array.resize(Mesh::ARRAY_MAX); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	Vector<Vector3> point_lines_mesh_vertices; | 
					
						
							|  |  |  | 	point_lines_mesh_vertices.resize(edited_vertices.size() * 2); | 
					
						
							|  |  |  | 	Vector3 *point_lines_mesh_vertices_ptr = point_lines_mesh_vertices.ptrw(); | 
					
						
							| 
									
										
										
										
											2024-09-08 21:55:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	int vertex_index = 0; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	for (int i = 0; i < edited_vertices.size(); i++) { | 
					
						
							|  |  |  | 		Vector3 point, next_point; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		if (i == edited_point) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			point = edited_point_pos; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			point = edited_vertices[i]; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		if ((wip_active && i == edited_vertices.size() - 1) || (((i + 1) % edited_vertices.size()) == edited_point)) { | 
					
						
							|  |  |  | 			next_point = edited_point_pos; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			next_point = edited_vertices[(i + 1) % edited_vertices.size()]; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 		point_lines_mesh_vertices_ptr[vertex_index++] = point; | 
					
						
							|  |  |  | 		point_lines_mesh_vertices_ptr[vertex_index++] = next_point; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	point_lines_mesh_array[Mesh::ARRAY_VERTEX] = point_lines_mesh_vertices; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rs->mesh_add_surface_from_arrays(point_lines_mesh_rid, RS::PRIMITIVE_LINES, point_lines_mesh_array); | 
					
						
							|  |  |  | 	rs->instance_set_surface_override_material(point_lines_instance_rid, 0, line_material->get_rid()); | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 	const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001); | 
					
						
							|  |  |  | 	const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position()); | 
					
						
							|  |  |  | 	rs->instance_set_transform(point_lines_instance_rid, gt); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Array point_handle_mesh_array; | 
					
						
							|  |  |  | 	point_handle_mesh_array.resize(Mesh::ARRAY_MAX); | 
					
						
							|  |  |  | 	Vector<Vector3> point_handle_mesh_vertices; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	point_handle_mesh_vertices.resize(edited_vertices.size()); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	Vector3 *point_handle_mesh_vertices_ptr = point_handle_mesh_vertices.ptrw(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	for (int i = 0; i < edited_vertices.size(); i++) { | 
					
						
							|  |  |  | 		Vector3 point_handle_3d; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (i == edited_point) { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			point_handle_3d = edited_point_pos; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 			point_handle_3d = edited_vertices[i]; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		point_handle_mesh_vertices_ptr[i] = point_handle_3d; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	point_handle_mesh_array[Mesh::ARRAY_VERTEX] = point_handle_mesh_vertices; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	rs->mesh_add_surface_from_arrays(point_handle_mesh_rid, RS::PRIMITIVE_POINTS, point_handle_mesh_array); | 
					
						
							|  |  |  | 	rs->instance_set_surface_override_material(point_handles_instance_rid, 0, handle_material->get_rid()); | 
					
						
							| 
									
										
										
										
											2024-11-13 16:16:48 +01:00
										 |  |  | 	rs->instance_set_transform(point_handles_instance_rid, gt); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | NavigationObstacle3DEditorPlugin *NavigationObstacle3DEditorPlugin::singleton = nullptr; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | NavigationObstacle3DEditorPlugin::NavigationObstacle3DEditorPlugin() { | 
					
						
							|  |  |  | 	singleton = this; | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); | 
					
						
							|  |  |  | 	line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); | 
					
						
							|  |  |  | 	line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); | 
					
						
							| 
									
										
										
										
											2023-09-27 00:45:57 +02:00
										 |  |  | 	line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	line_material->set_albedo(Color(1, 0.3, 0.1, 0.8)); | 
					
						
							|  |  |  | 	line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); | 
					
						
							|  |  |  | 	handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); | 
					
						
							|  |  |  | 	handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); | 
					
						
							| 
									
										
										
										
											2023-09-27 00:45:57 +02:00
										 |  |  | 	handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); | 
					
						
							| 
									
										
										
										
											2023-09-13 13:14:07 +02:00
										 |  |  | 	Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons)); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 	handle_material->set_point_size(handle->get_width()); | 
					
						
							|  |  |  | 	handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	RenderingServer *rs = RenderingServer::get_singleton(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	point_lines_mesh_rid = rs->mesh_create(); | 
					
						
							|  |  |  | 	point_handle_mesh_rid = rs->mesh_create(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	point_lines_instance_rid = rs->instance_create(); | 
					
						
							|  |  |  | 	point_handles_instance_rid = rs->instance_create(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	rs->instance_set_base(point_lines_instance_rid, point_lines_mesh_rid); | 
					
						
							|  |  |  | 	rs->instance_set_base(point_handles_instance_rid, point_handle_mesh_rid); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	obstacle_editor = memnew(HBoxContainer); | 
					
						
							|  |  |  | 	obstacle_editor->hide(); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	Ref<ButtonGroup> bg; | 
					
						
							|  |  |  | 	bg.instantiate(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	button_create = memnew(Button); | 
					
						
							| 
									
										
										
										
											2024-12-05 17:13:20 +03:00
										 |  |  | 	button_create->set_theme_type_variation(SceneStringName(FlatButton)); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	obstacle_editor->add_child(button_create); | 
					
						
							|  |  |  | 	button_create->set_tooltip_text(TTR("Add Vertex")); | 
					
						
							|  |  |  | 	button_create->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::MODE_CREATE)); | 
					
						
							|  |  |  | 	button_create->set_toggle_mode(true); | 
					
						
							|  |  |  | 	button_create->set_button_group(bg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	button_edit = memnew(Button); | 
					
						
							| 
									
										
										
										
											2024-12-05 17:13:20 +03:00
										 |  |  | 	button_edit->set_theme_type_variation(SceneStringName(FlatButton)); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	obstacle_editor->add_child(button_edit); | 
					
						
							|  |  |  | 	button_edit->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::MODE_EDIT)); | 
					
						
							|  |  |  | 	button_edit->set_toggle_mode(true); | 
					
						
							|  |  |  | 	button_edit->set_button_group(bg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	button_delete = memnew(Button); | 
					
						
							| 
									
										
										
										
											2024-12-05 17:13:20 +03:00
										 |  |  | 	button_delete->set_theme_type_variation(SceneStringName(FlatButton)); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	obstacle_editor->add_child(button_delete); | 
					
						
							|  |  |  | 	button_delete->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::MODE_DELETE)); | 
					
						
							|  |  |  | 	button_delete->set_toggle_mode(true); | 
					
						
							|  |  |  | 	button_delete->set_button_group(bg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	button_flip = memnew(Button); | 
					
						
							| 
									
										
										
										
											2024-12-05 17:13:20 +03:00
										 |  |  | 	button_flip->set_theme_type_variation(SceneStringName(FlatButton)); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	obstacle_editor->add_child(button_flip); | 
					
						
							|  |  |  | 	button_flip->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::ACTION_FLIP)); | 
					
						
							|  |  |  | 	button_flip->set_toggle_mode(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	button_clear = memnew(Button); | 
					
						
							| 
									
										
										
										
											2024-12-05 17:13:20 +03:00
										 |  |  | 	button_clear->set_theme_type_variation(SceneStringName(FlatButton)); | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	obstacle_editor->add_child(button_clear); | 
					
						
							|  |  |  | 	button_clear->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::ACTION_CLEAR)); | 
					
						
							|  |  |  | 	button_clear->set_toggle_mode(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	button_clear_dialog = memnew(ConfirmationDialog); | 
					
						
							|  |  |  | 	button_clear_dialog->set_title(TTR("Please Confirm...")); | 
					
						
							|  |  |  | 	button_clear_dialog->set_text(TTR("Remove all vertices?")); | 
					
						
							|  |  |  | 	button_clear_dialog->connect(SceneStringName(confirmed), callable_mp(NavigationObstacle3DEditorPlugin::singleton, &NavigationObstacle3DEditorPlugin::action_clear_vertices)); | 
					
						
							|  |  |  | 	obstacle_editor->add_child(button_clear_dialog); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Node3DEditor::get_singleton()->add_control_to_menu_panel(obstacle_editor); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	Ref<NavigationObstacle3DGizmoPlugin> gizmo_plugin = memnew(NavigationObstacle3DGizmoPlugin()); | 
					
						
							|  |  |  | 	obstacle_3d_gizmo_plugin = gizmo_plugin; | 
					
						
							|  |  |  | 	Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin); | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | NavigationObstacle3DEditorPlugin::~NavigationObstacle3DEditorPlugin() { | 
					
						
							| 
									
										
										
										
											2024-06-02 20:41:49 +02:00
										 |  |  | 	RenderingServer *rs = RenderingServer::get_singleton(); | 
					
						
							|  |  |  | 	ERR_FAIL_NULL(rs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (point_lines_instance_rid.is_valid()) { | 
					
						
							|  |  |  | 		rs->free(point_lines_instance_rid); | 
					
						
							|  |  |  | 		point_lines_instance_rid = RID(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (point_lines_mesh_rid.is_valid()) { | 
					
						
							|  |  |  | 		rs->free(point_lines_mesh_rid); | 
					
						
							|  |  |  | 		point_lines_mesh_rid = RID(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (point_handles_instance_rid.is_valid()) { | 
					
						
							|  |  |  | 		rs->free(point_handles_instance_rid); | 
					
						
							|  |  |  | 		point_handles_instance_rid = RID(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (point_handle_mesh_rid.is_valid()) { | 
					
						
							|  |  |  | 		rs->free(point_handle_mesh_rid); | 
					
						
							|  |  |  | 		point_handle_mesh_rid = RID(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-01-10 07:14:16 +01:00
										 |  |  | } |