Skip to content

Commit 8d530bc

Browse files
committed
Merge pull request #99176 from tracefree/obstacle_transform
Fix obstacle avoidance and 3D editor ignoring transform
2 parents 7ac9d7f + 31a3b41 commit 8d530bc

File tree

5 files changed

+79
-34
lines changed

5 files changed

+79
-34
lines changed

editor/plugins/navigation_obstacle_3d_editor_plugin.cpp

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,10 @@ void NavigationObstacle3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
6969
}
7070

7171
float height = obstacle->get_height();
72-
Basis gbi = obstacle->get_global_basis().inverse();
7372

73+
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));
74+
const Basis gbi = obstacle->get_global_basis().inverse();
75+
const Basis safe_global_basis = gbi * safe_basis;
7476
const int vertex_count = vertices.size();
7577

7678
Vector<Vector3> lines_mesh_vertices;
@@ -83,21 +85,22 @@ void NavigationObstacle3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
8385
Vector3 point = vertices[i];
8486
Vector3 next_point = vertices[(i + 1) % vertex_count];
8587

86-
Vector3 direction = next_point.direction_to(point);
88+
Vector3 direction = safe_basis.xform(next_point.direction_to(point));
8789
Vector3 arrow_dir = direction.cross(Vector3(0.0, 1.0, 0.0));
8890
Vector3 edge_middle = point + ((next_point - point) * 0.5);
8991

90-
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(edge_middle);
91-
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(edge_middle + (arrow_dir * 0.5));
92+
// Ensure vector stays perpendicular even when scaled non-uniformly.
93+
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(edge_middle);
94+
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(edge_middle) + gbi.xform(arrow_dir) * 0.5;
9295

93-
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(point);
94-
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(next_point);
96+
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(point);
97+
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(next_point);
9598

96-
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(Vector3(point.x, height, point.z));
97-
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(Vector3(next_point.x, height, next_point.z));
99+
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(point.x, height, point.z));
100+
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(next_point.x, height, next_point.z));
98101

99-
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(point);
100-
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(Vector3(point.x, height, point.z));
102+
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(point);
103+
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(point.x, height, point.z));
101104
}
102105

103106
Vector<Vector2> polygon_2d_vertices;
@@ -138,7 +141,8 @@ int NavigationObstacle3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DG
138141
NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
139142
ERR_FAIL_NULL_V(obstacle_node, -1);
140143

141-
Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
144+
const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001);
145+
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());
142146
const Vector<Vector3> &vertices = obstacle_node->get_vertices();
143147

144148
for (int idx = 0; idx < vertices.size(); ++idx) {
@@ -160,7 +164,8 @@ Vector<int> NavigationObstacle3DGizmoPlugin::subgizmos_intersect_frustum(const E
160164
NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
161165
ERR_FAIL_NULL_V(obstacle_node, contained_points);
162166

163-
Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
167+
const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001);
168+
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());
164169
const Vector<Vector3> &vertices = obstacle_node->get_vertices();
165170

166171
for (int idx = 0; idx < vertices.size(); ++idx) {
@@ -188,24 +193,22 @@ Transform3D NavigationObstacle3DGizmoPlugin::get_subgizmo_transform(const Editor
188193
const Vector<Vector3> &vertices = obstacle_node->get_vertices();
189194
ERR_FAIL_INDEX_V(p_id, vertices.size(), Transform3D());
190195

191-
Basis gbi = obstacle_node->get_global_basis().inverse();
192-
193-
Transform3D subgizmo_transform = Transform3D(Basis(), gbi.xform(vertices[p_id]));
196+
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();
197+
Transform3D subgizmo_transform = Transform3D(Basis(), safe_basis_inverse.xform(vertices[p_id]));
194198
return subgizmo_transform;
195199
}
196200

197201
void NavigationObstacle3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) {
198202
NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
199203
ERR_FAIL_NULL(obstacle_node);
200204

201-
Basis gb = obstacle_node->get_global_basis();
202-
205+
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));
203206
Vector3 new_vertex_pos = p_transform.origin;
204207

205208
Vector<Vector3> vertices = obstacle_node->get_vertices();
206209
ERR_FAIL_INDEX(p_id, vertices.size());
207210

208-
Vector3 vertex = gb.xform(new_vertex_pos);
211+
Vector3 vertex = safe_basis.xform(new_vertex_pos);
209212
vertex.y = 0.0;
210213
vertices.write[p_id] = vertex;
211214

@@ -216,14 +219,14 @@ void NavigationObstacle3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *
216219
NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
217220
ERR_FAIL_NULL(obstacle_node);
218221

219-
Basis gb = obstacle_node->get_global_basis();
222+
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));
220223

221224
Vector<Vector3> vertices = obstacle_node->get_vertices();
222225
Vector<Vector3> restore_vertices = vertices;
223226

224227
for (int i = 0; i < p_ids.size(); ++i) {
225228
const int idx = p_ids[i];
226-
Vector3 vertex = gb.xform(p_restore[i].origin);
229+
Vector3 vertex = safe_basis.xform(p_restore[i].origin);
227230
vertex.y = 0.0;
228231
restore_vertices.write[idx] = vertex;
229232
}
@@ -446,7 +449,8 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditorPlugin::forward_3d_gui_inp
446449
Vector3 ray_from = p_camera->project_ray_origin(mouse_position);
447450
Vector3 ray_dir = p_camera->project_ray_normal(mouse_position);
448451

449-
Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
452+
const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001);
453+
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());
450454
Transform3D gi = gt.affine_inverse();
451455
Plane projection_plane(Vector3(0.0, 1.0, 0.0), gt.origin);
452456

@@ -666,7 +670,8 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditorPlugin::forward_3d_gui_inp
666670
Vector3 ray_from = p_camera->project_ray_origin(mouse_position);
667671
Vector3 ray_dir = p_camera->project_ray_normal(mouse_position);
668672

669-
Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
673+
const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001);
674+
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());
670675
Transform3D gi = gt.affine_inverse();
671676
Plane projection_plane(Vector3(0.0, 1.0, 0.0), gt.origin);
672677

@@ -762,7 +767,9 @@ void NavigationObstacle3DEditorPlugin::redraw() {
762767

763768
rs->mesh_add_surface_from_arrays(point_lines_mesh_rid, RS::PRIMITIVE_LINES, point_lines_mesh_array);
764769
rs->instance_set_surface_override_material(point_lines_instance_rid, 0, line_material->get_rid());
765-
rs->instance_set_transform(point_lines_instance_rid, Transform3D(Basis(), obstacle_node->get_global_position()));
770+
const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001);
771+
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());
772+
rs->instance_set_transform(point_lines_instance_rid, gt);
766773

767774
Array point_handle_mesh_array;
768775
point_handle_mesh_array.resize(Mesh::ARRAY_MAX);
@@ -787,7 +794,7 @@ void NavigationObstacle3DEditorPlugin::redraw() {
787794

788795
rs->mesh_add_surface_from_arrays(point_handle_mesh_rid, RS::PRIMITIVE_POINTS, point_handle_mesh_array);
789796
rs->instance_set_surface_override_material(point_handles_instance_rid, 0, handle_material->get_rid());
790-
rs->instance_set_transform(point_handles_instance_rid, Transform3D(Basis(), obstacle_node->get_global_position()));
797+
rs->instance_set_transform(point_handles_instance_rid, gt);
791798
}
792799

793800
NavigationObstacle3DEditorPlugin *NavigationObstacle3DEditorPlugin::singleton = nullptr;

scene/2d/navigation_obstacle_2d.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ void NavigationObstacle2D::_notification(int p_what) {
8989
previous_transform = get_global_transform();
9090
// need to trigger map controlled agent assignment somehow for the fake_agent since obstacles use no callback like regular agents
9191
NavigationServer2D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
92-
_update_position(get_global_position());
92+
_update_transform();
9393
set_physics_process_internal(true);
9494
#ifdef DEBUG_ENABLED
9595
RS::get_singleton()->canvas_item_set_parent(debug_canvas_item, get_world_2d()->get_canvas());
@@ -142,7 +142,7 @@ void NavigationObstacle2D::_notification(int p_what) {
142142

143143
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
144144
if (is_inside_tree()) {
145-
_update_position(get_global_position());
145+
_update_transform();
146146

147147
if (velocity_submitted) {
148148
velocity_submitted = false;
@@ -206,7 +206,8 @@ NavigationObstacle2D::~NavigationObstacle2D() {
206206

207207
void NavigationObstacle2D::set_vertices(const Vector<Vector2> &p_vertices) {
208208
vertices = p_vertices;
209-
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, vertices);
209+
const Transform2D node_transform = is_inside_tree() ? get_global_transform() : Transform2D();
210+
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, node_transform.xform(vertices));
210211
#ifdef DEBUG_ENABLED
211212
queue_redraw();
212213
#endif // DEBUG_ENABLED
@@ -237,7 +238,8 @@ void NavigationObstacle2D::set_radius(real_t p_radius) {
237238

238239
radius = p_radius;
239240

240-
NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, radius);
241+
const Vector2 safe_scale = (is_inside_tree() ? get_global_scale() : get_scale()).abs().maxf(0.001);
242+
NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, safe_scale[safe_scale.max_axis_index()] * radius);
241243
#ifdef DEBUG_ENABLED
242244
queue_redraw();
243245
#endif // DEBUG_ENABLED
@@ -341,6 +343,18 @@ void NavigationObstacle2D::_update_position(const Vector2 p_position) {
341343
#endif // DEBUG_ENABLED
342344
}
343345

346+
void NavigationObstacle2D::_update_transform() {
347+
_update_position(get_global_position());
348+
// Prevent non-positive or non-uniform scaling of dynamic obstacle radius.
349+
const Vector2 safe_scale = get_global_scale().abs().maxf(0.001);
350+
const float scaling_max_value = safe_scale[safe_scale.max_axis_index()];
351+
NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, scaling_max_value * radius);
352+
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, get_global_transform().translated(-get_global_position()).xform(vertices));
353+
#ifdef DEBUG_ENABLED
354+
queue_redraw();
355+
#endif // DEBUG_ENABLED
356+
}
357+
344358
#ifdef DEBUG_ENABLED
345359
void NavigationObstacle2D::_update_fake_agent_radius_debug() {
346360
if (radius > 0.0 && NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius()) {
@@ -373,7 +387,7 @@ void NavigationObstacle2D::_update_static_obstacle_debug() {
373387
debug_obstacle_polygon_colors.resize(debug_obstacle_polygon_vertices.size());
374388
debug_obstacle_polygon_colors.fill(debug_static_obstacle_face_color);
375389

376-
RS::get_singleton()->canvas_item_add_polygon(debug_canvas_item, debug_obstacle_polygon_vertices, debug_obstacle_polygon_colors);
390+
RS::get_singleton()->canvas_item_add_polygon(debug_canvas_item, get_global_transform().xform(debug_obstacle_polygon_vertices), debug_obstacle_polygon_colors);
377391

378392
Color debug_static_obstacle_edge_color;
379393

scene/2d/navigation_obstacle_2d.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class NavigationObstacle2D : public Node2D {
111111
private:
112112
void _update_map(RID p_map);
113113
void _update_position(const Vector2 p_position);
114+
void _update_transform();
114115
};
115116

116117
#endif // NAVIGATION_OBSTACLE_2D_H

scene/3d/navigation_obstacle_3d.cpp

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ void NavigationObstacle3D::_notification(int p_what) {
9494
}
9595
// need to trigger map controlled agent assignment somehow for the fake_agent since obstacles use no callback like regular agents
9696
NavigationServer3D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
97-
_update_position(get_global_position());
97+
_update_transform();
9898
set_physics_process_internal(true);
9999
#ifdef DEBUG_ENABLED
100100
_update_debug();
@@ -153,7 +153,7 @@ void NavigationObstacle3D::_notification(int p_what) {
153153

154154
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
155155
if (is_inside_tree()) {
156-
_update_position(get_global_position());
156+
_update_transform();
157157

158158
if (velocity_submitted) {
159159
velocity_submitted = false;
@@ -258,7 +258,11 @@ NavigationObstacle3D::~NavigationObstacle3D() {
258258
void NavigationObstacle3D::set_vertices(const Vector<Vector3> &p_vertices) {
259259
vertices = p_vertices;
260260

261-
NavigationServer3D::get_singleton()->obstacle_set_vertices(obstacle, vertices);
261+
const Basis basis = is_inside_tree() ? get_global_basis() : get_basis();
262+
const float rotation_y = is_inside_tree() ? get_global_rotation().y : get_rotation().y;
263+
const Vector3 safe_scale = basis.get_scale().abs().maxf(0.001);
264+
const Transform3D safe_transform = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), rotation_y), Vector3());
265+
NavigationServer3D::get_singleton()->obstacle_set_vertices(obstacle, safe_transform.xform(vertices));
262266
#ifdef DEBUG_ENABLED
263267
_update_static_obstacle_debug();
264268
update_gizmos();
@@ -289,7 +293,10 @@ void NavigationObstacle3D::set_radius(real_t p_radius) {
289293
}
290294

291295
radius = p_radius;
292-
NavigationServer3D::get_singleton()->obstacle_set_radius(obstacle, radius);
296+
297+
// Prevent non-positive or non-uniform scaling of dynamic obstacle radius.
298+
const Vector3 safe_scale = (is_inside_tree() ? get_global_basis() : get_basis()).get_scale().abs().maxf(0.001);
299+
NavigationServer3D::get_singleton()->obstacle_set_radius(obstacle, safe_scale[safe_scale.max_axis_index()] * radius);
293300

294301
#ifdef DEBUG_ENABLED
295302
_update_fake_agent_radius_debug();
@@ -304,7 +311,8 @@ void NavigationObstacle3D::set_height(real_t p_height) {
304311
}
305312

306313
height = p_height;
307-
NavigationServer3D::get_singleton()->obstacle_set_height(obstacle, height);
314+
const float scale_factor = MAX(Math::abs((is_inside_tree() ? get_global_basis() : get_basis()).get_scale().y), 0.001);
315+
NavigationServer3D::get_singleton()->obstacle_set_height(obstacle, scale_factor * height);
308316

309317
#ifdef DEBUG_ENABLED
310318
_update_static_obstacle_debug();
@@ -407,6 +415,20 @@ void NavigationObstacle3D::_update_position(const Vector3 p_position) {
407415
NavigationServer3D::get_singleton()->obstacle_set_position(obstacle, p_position);
408416
}
409417

418+
void NavigationObstacle3D::_update_transform() {
419+
_update_position(get_global_position());
420+
421+
// Prevent non-positive or non-uniform scaling of dynamic obstacle radius.
422+
const Vector3 safe_scale = get_global_basis().get_scale().abs().maxf(0.001);
423+
const float scaling_max_value = safe_scale[safe_scale.max_axis_index()];
424+
NavigationServer3D::get_singleton()->obstacle_set_radius(obstacle, scaling_max_value * radius);
425+
426+
// Apply modified node transform which only takes y-axis rotation into account to vertices.
427+
const Transform3D safe_transform = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), get_global_rotation().y), Vector3());
428+
NavigationServer3D::get_singleton()->obstacle_set_vertices(obstacle, safe_transform.xform(vertices));
429+
NavigationServer3D::get_singleton()->obstacle_set_height(obstacle, safe_scale.y * height);
430+
}
431+
410432
void NavigationObstacle3D::_update_use_3d_avoidance(bool p_use_3d_avoidance) {
411433
NavigationServer3D::get_singleton()->obstacle_set_use_3d_avoidance(obstacle, use_3d_avoidance);
412434
_update_map(map_current);

scene/3d/navigation_obstacle_3d.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ class NavigationObstacle3D : public Node3D {
121121
private:
122122
void _update_map(RID p_map);
123123
void _update_position(const Vector3 p_position);
124+
void _update_transform();
124125
void _update_use_3d_avoidance(bool p_use_3d_avoidance);
125126
};
126127

0 commit comments

Comments
 (0)