Add NavigationObstacle options to affect and carve navigation mesh

Adds NavigationObstacle options to affect and carve navigation mesh.
This commit is contained in:
smix8 2024-03-01 00:30:09 +01:00
parent 0175be8948
commit 5d5e85fe07
16 changed files with 664 additions and 15 deletions

View File

@ -16,6 +16,14 @@
Adds the outline points of a shape as obstructed area.
</description>
</method>
<method name="add_projected_obstruction">
<return type="void" />
<param index="0" name="vertices" type="PackedVector2Array" />
<param index="1" name="carve" type="bool" />
<description>
Adds a projected obstruction shape to the source geometry. If [param carve] is [code]true[/code] the carved shape will not be affected by additional offsets (e.g. agent radius) of the navigation mesh baking process.
</description>
</method>
<method name="add_traversable_outline">
<return type="void" />
<param index="0" name="shape_outline" type="PackedVector2Array" />
@ -29,12 +37,26 @@
Clears the internal data.
</description>
</method>
<method name="clear_projected_obstructions">
<return type="void" />
<description>
Clears all projected obstructions.
</description>
</method>
<method name="get_obstruction_outlines" qualifiers="const">
<return type="PackedVector2Array[]" />
<description>
Returns all the obstructed area outlines arrays.
</description>
</method>
<method name="get_projected_obstructions" qualifiers="const">
<return type="Array" />
<description>
Returns the projected obstructions as an [Array] of dictionaries. Each [Dictionary] contains the following entries:
- [code]vertices[/code] - A [PackedFloat32Array] that defines the outline points of the projected shape.
- [code]carve[/code] - A [bool] that defines how the projected shape affects the navigation mesh baking. If [code]true[/code] the projected shape will not be affected by addition offsets, e.g. agent radius.
</description>
</method>
<method name="get_traversable_outlines" qualifiers="const">
<return type="PackedVector2Array[]" />
<description>
@ -61,6 +83,19 @@
Sets all the obstructed area outlines arrays.
</description>
</method>
<method name="set_projected_obstructions">
<return type="void" />
<param index="0" name="projected_obstructions" type="Array" />
<description>
Sets the projected obstructions with an Array of Dictionaries with the following key value pairs:
[codeblocks]
[gdscript]
"vertices" : PackedFloat32Array
"carve" : bool
[/gdscript]
[/codeblocks]
</description>
</method>
<method name="set_traversable_outlines">
<return type="void" />
<param index="0" name="traversable_outlines" type="PackedVector2Array[]" />

View File

@ -33,18 +33,44 @@
Adds an [Array] the size of [constant Mesh.ARRAY_MAX] and with vertices at index [constant Mesh.ARRAY_VERTEX] and indices at index [constant Mesh.ARRAY_INDEX] to the navigation mesh baking data. The array must have valid triangulated mesh data to be considered. Since [NavigationMesh] resources have no transform, all vertex positions need to be offset by the node's transform using [param xform].
</description>
</method>
<method name="add_projected_obstruction">
<return type="void" />
<param index="0" name="vertices" type="PackedVector3Array" />
<param index="1" name="elevation" type="float" />
<param index="2" name="height" type="float" />
<param index="3" name="carve" type="bool" />
<description>
Adds a projected obstruction shape to the source geometry. The [param vertices] are considered projected on a xz-axes plane, placed at the global y-axis [param elevation] and extruded by [param height]. If [param carve] is [code]true[/code] the carved shape will not be affected by additional offsets (e.g. agent radius) of the navigation mesh baking process.
</description>
</method>
<method name="clear">
<return type="void" />
<description>
Clears the internal data.
</description>
</method>
<method name="clear_projected_obstructions">
<return type="void" />
<description>
Clears all projected obstructions.
</description>
</method>
<method name="get_indices" qualifiers="const">
<return type="PackedInt32Array" />
<description>
Returns the parsed source geometry data indices array.
</description>
</method>
<method name="get_projected_obstructions" qualifiers="const">
<return type="Array" />
<description>
Returns the projected obstructions as an [Array] of dictionaries. Each [Dictionary] contains the following entries:
- [code]vertices[/code] - A [PackedFloat32Array] that defines the outline points of the projected shape.
- [code]elevation[/code] - A [float] that defines the projected shape placement on the y-axis.
- [code]height[/code] - A [float] that defines how much the projected shape is extruded along the y-axis.
- [code]carve[/code] - A [bool] that defines how the obstacle affects the navigation mesh baking. If [code]true[/code] the projected shape will not be affected by addition offsets, e.g. agent radius.
</description>
</method>
<method name="get_vertices" qualifiers="const">
<return type="PackedFloat32Array" />
<description>
@ -72,6 +98,21 @@
[b]Warning:[/b] Inappropriate data can crash the baking process of the involved third-party libraries.
</description>
</method>
<method name="set_projected_obstructions">
<return type="void" />
<param index="0" name="projected_obstructions" type="Array" />
<description>
Sets the projected obstructions with an Array of Dictionaries with the following key value pairs:
[codeblocks]
[gdscript]
"vertices" : PackedFloat32Array
"elevation" : float
"height" : float
"carve" : bool
[/gdscript]
[/codeblocks]
</description>
</method>
<method name="set_vertices">
<return type="void" />
<param index="0" name="vertices" type="PackedFloat32Array" />

View File

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NavigationObstacle2D" inherits="Node2D" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
2D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area.
2D obstacle used to affect navigation mesh baking or constrain velocities of avoidance controlled agents.
</brief_description>
<description>
2D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area. The obstacle needs a navigation map and outline vertices defined to work correctly.
If the obstacle's vertices are winded in clockwise order, avoidance agents will be pushed in by the obstacle, otherwise, avoidance agents will be pushed out. Outlines must not cross or overlap.
Obstacles are [b]not[/b] a replacement for a (re)baked navigation mesh. Obstacles [b]don't[/b] change the resulting path from the pathfinding, obstacles only affect the navigation avoidance agent movement by altering the suggested velocity of the avoidance agent.
Obstacles using vertices can warp to a new position but should not moved every frame as each move requires a rebuild of the avoidance map.
An obstacle needs a navigation map and outline [member vertices] defined to work correctly. The outlines can not cross or overlap.
Obstacles can be included in the navigation mesh baking process when [member affect_navigation_mesh] is enabled. They do not add walkable geometry, instead their role is to discard other source geometry inside the shape. This can be used to prevent navigation mesh from appearing in unwanted places. If [member carve_navigation_mesh] is enabled the baked shape will not be affected by offsets of the navigation mesh baking, e.g. the agent radius.
With [member avoidance_enabled] the obstacle can constrain the avoidance velocities of avoidance using agents. If the obstacle's vertices are wound in clockwise order, avoidance agents will be pushed in by the obstacle, otherwise, avoidance agents will be pushed out. Obstacles using vertices and avoidance can warp to a new position but should not be moved every single frame as each change requires a rebuild of the avoidance map.
</description>
<tutorials>
<link title="Using NavigationObstacles">$DOCS_URL/tutorials/navigation/navigation_using_navigationobstacles.html</link>
@ -49,12 +48,20 @@
</method>
</methods>
<members>
<member name="affect_navigation_mesh" type="bool" setter="set_affect_navigation_mesh" getter="get_affect_navigation_mesh" default="false">
If enabled and parsed in a navigation mesh baking process the obstacle will discard source geometry inside its [member vertices] defined shape.
</member>
<member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="true">
If [code]true[/code] the obstacle affects avoidance using agents.
</member>
<member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
A bitfield determining the avoidance layers for this obstacle. Agents with a matching bit on the their avoidance mask will avoid this obstacle.
</member>
<member name="carve_navigation_mesh" type="bool" setter="set_carve_navigation_mesh" getter="get_carve_navigation_mesh" default="false">
If enabled the obstacle vertices will carve into the baked navigation mesh with the shape unaffected by additional offsets (e.g. agent radius).
It will still be affected by further postprocessing of the baking process, like edge and polygon simplification.
Requires [member affect_navigation_mesh] to be enabled.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.0">
Sets the avoidance radius for the obstacle.
</member>

View File

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NavigationObstacle3D" inherits="Node3D" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
3D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area.
3D obstacle used to affect navigation mesh baking or constrain velocities of avoidance controlled agents.
</brief_description>
<description>
3D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area. The obstacle needs a navigation map and outline vertices defined to work correctly.
If the obstacle's vertices are winded in clockwise order, avoidance agents will be pushed in by the obstacle, otherwise, avoidance agents will be pushed out. Outlines must not cross or overlap.
Obstacles are [b]not[/b] a replacement for a (re)baked navigation mesh. Obstacles [b]don't[/b] change the resulting path from the pathfinding, obstacles only affect the navigation avoidance agent movement by altering the suggested velocity of the avoidance agent.
Obstacles using vertices can warp to a new position but should not moved every frame as each move requires a rebuild of the avoidance map.
An obstacle needs a navigation map and outline [member vertices] defined to work correctly. The outlines can not cross or overlap and are restricted to a plane projection. This means the y-axis of the vertices is ignored, instead the obstacle's global y-axis position is used for placement. The projected shape is extruded by the obstacles height along the y-axis.
Obstacles can be included in the navigation mesh baking process when [member affect_navigation_mesh] is enabled. They do not add walkable geometry, instead their role is to discard other source geometry inside the shape. This can be used to prevent navigation mesh from appearing in unwanted places, e.g. inside "solid" geometry or on top of it. If [member carve_navigation_mesh] is enabled the baked shape will not be affected by offsets of the navigation mesh baking, e.g. the agent radius.
With [member avoidance_enabled] the obstacle can constrain the avoidance velocities of avoidance using agents. If the obstacle's vertices are wound in clockwise order, avoidance agents will be pushed in by the obstacle, otherwise, avoidance agents will be pushed out. Obstacles using vertices and avoidance can warp to a new position but should not be moved every single frame as each change requires a rebuild of the avoidance map.
</description>
<tutorials>
<link title="Using NavigationObstacles">$DOCS_URL/tutorials/navigation/navigation_using_navigationobstacles.html</link>
@ -49,12 +48,20 @@
</method>
</methods>
<members>
<member name="affect_navigation_mesh" type="bool" setter="set_affect_navigation_mesh" getter="get_affect_navigation_mesh" default="false">
If enabled and parsed in a navigation mesh baking process the obstacle will discard source geometry inside its [member vertices] and [member height] defined shape.
</member>
<member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="true">
If [code]true[/code] the obstacle affects avoidance using agents.
</member>
<member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
A bitfield determining the avoidance layers for this obstacle. Agents with a matching bit on the their avoidance mask will avoid this obstacle.
</member>
<member name="carve_navigation_mesh" type="bool" setter="set_carve_navigation_mesh" getter="get_carve_navigation_mesh" default="false">
If enabled the obstacle vertices will carve into the baked navigation mesh with the shape unaffected by additional offsets (e.g. agent radius).
It will still be affected by further postprocessing of the baking process, like edge and polygon simplification.
Requires [member affect_navigation_mesh] to be enabled.
</member>
<member name="height" type="float" setter="set_height" getter="get_height" default="1.0">
Sets the obstacle height used in 2D avoidance. 2D avoidance using agent's ignore obstacles that are below or above them.
</member>

View File

@ -35,6 +35,7 @@
#include "core/config/project_settings.h"
#include "scene/2d/mesh_instance_2d.h"
#include "scene/2d/multimesh_instance_2d.h"
#include "scene/2d/navigation_obstacle_2d.h"
#include "scene/2d/physics/static_body_2d.h"
#include "scene/2d/polygon_2d.h"
#include "scene/2d/tile_map.h"
@ -233,6 +234,7 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_
generator_parse_polygon2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_staticbody2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_tilemap_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_navigationobstacle_node(p_navigation_mesh, p_source_geometry_data, p_node);
if (p_recurse_children) {
for (int i = 0; i < p_node->get_child_count(); i++) {
@ -660,6 +662,58 @@ void NavMeshGenerator2D::generator_parse_tilemap_node(const Ref<NavigationPolygo
}
}
void NavMeshGenerator2D::generator_parse_navigationobstacle_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
NavigationObstacle2D *obstacle = Object::cast_to<NavigationObstacle2D>(p_node);
if (obstacle == nullptr) {
return;
}
if (!obstacle->get_affect_navigation_mesh()) {
return;
}
const Transform2D node_xform = p_source_geometry_data->root_node_transform * Transform2D(0.0, obstacle->get_global_position());
const float obstacle_radius = obstacle->get_radius();
if (obstacle_radius > 0.0) {
Vector<Vector2> obstruction_circle_vertices;
// The point of this is that the moving obstacle can make a simple hole in the navigation mesh and affect the pathfinding.
// Without, navigation paths can go directly through the middle of the obstacle and conflict with the avoidance to get agents stuck.
// No place for excessive "round" detail here. Every additional edge adds a high cost for something that needs to be quick, not pretty.
static const int circle_points = 12;
obstruction_circle_vertices.resize(circle_points);
Vector2 *circle_vertices_ptrw = obstruction_circle_vertices.ptrw();
const real_t circle_point_step = Math_TAU / circle_points;
for (int i = 0; i < circle_points; i++) {
const float angle = i * circle_point_step;
circle_vertices_ptrw[i] = node_xform.xform(Vector2(Math::cos(angle) * obstacle_radius, Math::sin(angle) * obstacle_radius));
}
p_source_geometry_data->add_projected_obstruction(obstruction_circle_vertices, obstacle->get_carve_navigation_mesh());
}
const Vector<Vector2> &obstacle_vertices = obstacle->get_vertices();
if (obstacle_vertices.is_empty()) {
return;
}
Vector<Vector2> obstruction_shape_vertices;
obstruction_shape_vertices.resize(obstacle_vertices.size());
const Vector2 *obstacle_vertices_ptr = obstacle_vertices.ptr();
Vector2 *obstruction_shape_vertices_ptrw = obstruction_shape_vertices.ptrw();
for (int i = 0; i < obstacle_vertices.size(); i++) {
obstruction_shape_vertices_ptrw[i] = node_xform.xform(obstacle_vertices_ptr[i]);
}
p_source_geometry_data->add_projected_obstruction(obstruction_shape_vertices, obstacle->get_carve_navigation_mesh());
}
void NavMeshGenerator2D::generator_parse_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_root_node) {
List<Node *> parse_nodes;
@ -779,6 +833,30 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
obstruction_polygon_paths.push_back(clip_path);
}
const Vector<NavigationMeshSourceGeometryData2D::ProjectedObstruction> &projected_obstructions = p_source_geometry_data->_get_projected_obstructions();
if (!projected_obstructions.is_empty()) {
for (const NavigationMeshSourceGeometryData2D::ProjectedObstruction &projected_obstruction : projected_obstructions) {
if (projected_obstruction.carve) {
continue;
}
if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 2 != 0) {
continue;
}
Path64 clip_path;
clip_path.reserve(projected_obstruction.vertices.size() / 2);
for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) {
const Point64 &point = Point64(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]);
clip_path.push_back(point);
}
if (!IsPositive(clip_path)) {
std::reverse(clip_path.begin(), clip_path.end());
}
obstruction_polygon_paths.push_back(clip_path);
}
}
Rect2 baking_rect = p_navigation_mesh->get_baking_rect();
if (baking_rect.has_area()) {
Vector2 baking_rect_offset = p_navigation_mesh->get_baking_rect_offset();
@ -809,6 +887,33 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
if (agent_radius_offset > 0.0) {
path_solution = InflatePaths(path_solution, -agent_radius_offset, JoinType::Miter, EndType::Polygon);
}
if (!projected_obstructions.is_empty()) {
obstruction_polygon_paths.resize(0);
for (const NavigationMeshSourceGeometryData2D::ProjectedObstruction &projected_obstruction : projected_obstructions) {
if (!projected_obstruction.carve) {
continue;
}
if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 2 != 0) {
continue;
}
Path64 clip_path;
clip_path.reserve(projected_obstruction.vertices.size() / 2);
for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) {
const Point64 &point = Point64(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]);
clip_path.push_back(point);
}
if (!IsPositive(clip_path)) {
std::reverse(clip_path.begin(), clip_path.end());
}
obstruction_polygon_paths.push_back(clip_path);
}
if (obstruction_polygon_paths.size() > 0) {
path_solution = Difference(path_solution, obstruction_polygon_paths, FillRule::NonZero);
}
}
//path_solution = RamerDouglasPeucker(path_solution, 0.025); //
real_t border_size = p_navigation_mesh->get_border_size();

View File

@ -81,6 +81,7 @@ class NavMeshGenerator2D : public Object {
static void generator_parse_polygon2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static void generator_parse_staticbody2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static void generator_parse_tilemap_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static void generator_parse_navigationobstacle_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static bool generator_emit_callback(const Callable &p_callback);

View File

@ -37,6 +37,7 @@
#include "core/os/thread.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/multimesh_instance_3d.h"
#include "scene/3d/navigation_obstacle_3d.h"
#include "scene/3d/physics/static_body_3d.h"
#include "scene/resources/3d/box_shape_3d.h"
#include "scene/resources/3d/capsule_shape_3d.h"
@ -251,6 +252,7 @@ void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh>
#ifdef MODULE_GRIDMAP_ENABLED
generator_parse_gridmap_node(p_navigation_mesh, p_source_geometry_data, p_node);
#endif
generator_parse_navigationobstacle_node(p_navigation_mesh, p_source_geometry_data, p_node);
if (p_recurse_children) {
for (int i = 0; i < p_node->get_child_count(); i++) {
@ -569,6 +571,59 @@ void NavMeshGenerator3D::generator_parse_gridmap_node(const Ref<NavigationMesh>
}
#endif // MODULE_GRIDMAP_ENABLED
void NavMeshGenerator3D::generator_parse_navigationobstacle_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
NavigationObstacle3D *obstacle = Object::cast_to<NavigationObstacle3D>(p_node);
if (obstacle == nullptr) {
return;
}
if (!obstacle->get_affect_navigation_mesh()) {
return;
}
const Transform3D node_xform = p_source_geometry_data->root_node_transform * Transform3D(Basis(), obstacle->get_global_position());
const float obstacle_radius = obstacle->get_radius();
if (obstacle_radius > 0.0) {
Vector<Vector3> obstruction_circle_vertices;
// The point of this is that the moving obstacle can make a simple hole in the navigation mesh and affect the pathfinding.
// Without, navigation paths can go directly through the middle of the obstacle and conflict with the avoidance to get agents stuck.
// No place for excessive "round" detail here. Every additional edge adds a high cost for something that needs to be quick, not pretty.
static const int circle_points = 12;
obstruction_circle_vertices.resize(circle_points);
Vector3 *circle_vertices_ptrw = obstruction_circle_vertices.ptrw();
const real_t circle_point_step = Math_TAU / circle_points;
for (int i = 0; i < circle_points; i++) {
const float angle = i * circle_point_step;
circle_vertices_ptrw[i] = node_xform.xform(Vector3(Math::cos(angle) * obstacle_radius, 0.0, Math::sin(angle) * obstacle_radius));
}
p_source_geometry_data->add_projected_obstruction(obstruction_circle_vertices, obstacle->get_global_position().y + p_source_geometry_data->root_node_transform.origin.y - obstacle_radius, obstacle_radius, obstacle->get_carve_navigation_mesh());
}
const Vector<Vector3> &obstacle_vertices = obstacle->get_vertices();
if (obstacle_vertices.is_empty()) {
return;
}
Vector<Vector3> obstruction_shape_vertices;
obstruction_shape_vertices.resize(obstacle_vertices.size());
const Vector3 *obstacle_vertices_ptr = obstacle_vertices.ptr();
Vector3 *obstruction_shape_vertices_ptrw = obstruction_shape_vertices.ptrw();
for (int i = 0; i < obstacle_vertices.size(); i++) {
obstruction_shape_vertices_ptrw[i] = node_xform.xform(obstacle_vertices_ptr[i]);
obstruction_shape_vertices_ptrw[i].y = 0.0;
}
p_source_geometry_data->add_projected_obstruction(obstruction_shape_vertices, obstacle->get_global_position().y + p_source_geometry_data->root_node_transform.origin.y, obstacle->get_height(), obstacle->get_carve_navigation_mesh());
}
void NavMeshGenerator3D::generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node) {
List<Node *> parse_nodes;
@ -741,10 +796,46 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
rcFreeHeightField(hf);
hf = nullptr;
const Vector<NavigationMeshSourceGeometryData3D::ProjectedObstruction> &projected_obstructions = p_source_geometry_data->_get_projected_obstructions();
// Add obstacles to the source geometry. Those will be affected by e.g. agent_radius.
if (!projected_obstructions.is_empty()) {
for (const NavigationMeshSourceGeometryData3D::ProjectedObstruction &projected_obstruction : projected_obstructions) {
if (projected_obstruction.carve) {
continue;
}
if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 3 != 0) {
continue;
}
const float *projected_obstruction_verts = projected_obstruction.vertices.ptr();
const int projected_obstruction_nverts = projected_obstruction.vertices.size() / 3;
rcMarkConvexPolyArea(&ctx, projected_obstruction_verts, projected_obstruction_nverts, projected_obstruction.elevation, projected_obstruction.elevation + projected_obstruction.height, RC_NULL_AREA, *chf);
}
}
bake_state = "Eroding walkable area..."; // step #6
ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf));
// Carve obstacles to the eroded geometry. Those will NOT be affected by e.g. agent_radius because that step is already done.
if (!projected_obstructions.is_empty()) {
for (const NavigationMeshSourceGeometryData3D::ProjectedObstruction &projected_obstruction : projected_obstructions) {
if (!projected_obstruction.carve) {
continue;
}
if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 3 != 0) {
continue;
}
const float *projected_obstruction_verts = projected_obstruction.vertices.ptr();
const int projected_obstruction_nverts = projected_obstruction.vertices.size() / 3;
rcMarkConvexPolyArea(&ctx, projected_obstruction_verts, projected_obstruction_nverts, projected_obstruction.elevation, projected_obstruction.elevation + projected_obstruction.height, RC_NULL_AREA, *chf);
}
}
bake_state = "Partitioning..."; // step #7
if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) {

View File

@ -86,6 +86,7 @@ class NavMeshGenerator3D : public Object {
#ifdef MODULE_GRIDMAP_ENABLED
static void generator_parse_gridmap_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
#endif // MODULE_GRIDMAP_ENABLED
static void generator_parse_navigationobstacle_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
static bool generator_emit_callback(const Callable &p_callback);

View File

@ -55,14 +55,24 @@ void NavigationObstacle2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationObstacle2D::set_avoidance_layers);
ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationObstacle2D::get_avoidance_layers);
ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationObstacle2D::set_avoidance_layer_value);
ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationObstacle2D::get_avoidance_layer_value);
ClassDB::bind_method(D_METHOD("set_affect_navigation_mesh", "enabled"), &NavigationObstacle2D::set_affect_navigation_mesh);
ClassDB::bind_method(D_METHOD("get_affect_navigation_mesh"), &NavigationObstacle2D::get_affect_navigation_mesh);
ClassDB::bind_method(D_METHOD("set_carve_navigation_mesh", "enabled"), &NavigationObstacle2D::set_carve_navigation_mesh);
ClassDB::bind_method(D_METHOD("get_carve_navigation_mesh"), &NavigationObstacle2D::get_carve_navigation_mesh);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.0,500,0.01,suffix:px"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices"), "set_vertices", "get_vertices");
ADD_GROUP("NavigationMesh", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "affect_navigation_mesh"), "set_affect_navigation_mesh", "get_affect_navigation_mesh");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "carve_navigation_mesh"), "set_carve_navigation_mesh", "get_carve_navigation_mesh");
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.0,500,0.01,suffix:px"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices"), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
}
@ -277,6 +287,22 @@ void NavigationObstacle2D::set_velocity(const Vector2 p_velocity) {
velocity_submitted = true;
}
void NavigationObstacle2D::set_affect_navigation_mesh(bool p_enabled) {
affect_navigation_mesh = p_enabled;
}
bool NavigationObstacle2D::get_affect_navigation_mesh() const {
return affect_navigation_mesh;
}
void NavigationObstacle2D::set_carve_navigation_mesh(bool p_enabled) {
carve_navigation_mesh = p_enabled;
}
bool NavigationObstacle2D::get_carve_navigation_mesh() const {
return carve_navigation_mesh;
}
void NavigationObstacle2D::_update_map(RID p_map) {
map_current = p_map;
NavigationServer2D::get_singleton()->obstacle_set_map(obstacle, p_map);

View File

@ -54,6 +54,9 @@ class NavigationObstacle2D : public Node2D {
Vector2 previous_velocity;
bool velocity_submitted = false;
bool affect_navigation_mesh = false;
bool carve_navigation_mesh = false;
#ifdef DEBUG_ENABLED
private:
RID debug_canvas_item;
@ -97,6 +100,12 @@ public:
void _avoidance_done(Vector3 p_new_velocity); // Dummy
void set_affect_navigation_mesh(bool p_enabled);
bool get_affect_navigation_mesh() const;
void set_carve_navigation_mesh(bool p_enabled);
bool get_carve_navigation_mesh() const;
private:
void _update_map(RID p_map);
void _update_position(const Vector2 p_position);

View File

@ -63,12 +63,21 @@ void NavigationObstacle3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_3d_avoidance", "enabled"), &NavigationObstacle3D::set_use_3d_avoidance);
ClassDB::bind_method(D_METHOD("get_use_3d_avoidance"), &NavigationObstacle3D::get_use_3d_avoidance);
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");
ClassDB::bind_method(D_METHOD("set_affect_navigation_mesh", "enabled"), &NavigationObstacle3D::set_affect_navigation_mesh);
ClassDB::bind_method(D_METHOD("get_affect_navigation_mesh"), &NavigationObstacle3D::get_affect_navigation_mesh);
ClassDB::bind_method(D_METHOD("set_carve_navigation_mesh", "enabled"), &NavigationObstacle3D::set_carve_navigation_mesh);
ClassDB::bind_method(D_METHOD("get_carve_navigation_mesh"), &NavigationObstacle3D::get_carve_navigation_mesh);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.0,100,0.01,suffix:m"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.0,100,0.01,suffix:m"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices"), "set_vertices", "get_vertices");
ADD_GROUP("NavigationMesh", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "affect_navigation_mesh"), "set_affect_navigation_mesh", "get_affect_navigation_mesh");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "carve_navigation_mesh"), "set_carve_navigation_mesh", "get_carve_navigation_mesh");
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_3d_avoidance"), "set_use_3d_avoidance", "get_use_3d_avoidance");
}
@ -321,6 +330,22 @@ void NavigationObstacle3D::set_velocity(const Vector3 p_velocity) {
velocity_submitted = true;
}
void NavigationObstacle3D::set_affect_navigation_mesh(bool p_enabled) {
affect_navigation_mesh = p_enabled;
}
bool NavigationObstacle3D::get_affect_navigation_mesh() const {
return affect_navigation_mesh;
}
void NavigationObstacle3D::set_carve_navigation_mesh(bool p_enabled) {
carve_navigation_mesh = p_enabled;
}
bool NavigationObstacle3D::get_carve_navigation_mesh() const {
return carve_navigation_mesh;
}
void NavigationObstacle3D::_update_map(RID p_map) {
NavigationServer3D::get_singleton()->obstacle_set_map(obstacle, p_map);
map_current = p_map;

View File

@ -57,6 +57,9 @@ class NavigationObstacle3D : public Node3D {
Vector3 previous_velocity;
bool velocity_submitted = false;
bool affect_navigation_mesh = false;
bool carve_navigation_mesh = false;
#ifdef DEBUG_ENABLED
RID fake_agent_radius_debug_instance;
Ref<ArrayMesh> fake_agent_radius_debug_mesh;
@ -108,6 +111,12 @@ public:
void _avoidance_done(Vector3 p_new_velocity); // Dummy
void set_affect_navigation_mesh(bool p_enabled);
bool get_affect_navigation_mesh() const;
void set_carve_navigation_mesh(bool p_enabled);
bool get_carve_navigation_mesh() const;
private:
void _update_map(RID p_map);
void _update_position(const Vector3 p_position);

View File

@ -35,6 +35,12 @@
void NavigationMeshSourceGeometryData2D::clear() {
traversable_outlines.clear();
obstruction_outlines.clear();
clear_projected_obstructions();
}
void NavigationMeshSourceGeometryData2D::clear_projected_obstructions() {
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.clear();
}
void NavigationMeshSourceGeometryData2D::_set_traversable_outlines(const Vector<Vector<Vector2>> &p_traversable_outlines) {
@ -117,6 +123,109 @@ void NavigationMeshSourceGeometryData2D::merge(const Ref<NavigationMeshSourceGeo
// No need to worry about `root_node_transform` here as the data is already xformed.
traversable_outlines.append_array(p_other_geometry->traversable_outlines);
obstruction_outlines.append_array(p_other_geometry->obstruction_outlines);
if (p_other_geometry->_projected_obstructions.size() > 0) {
RWLockWrite write_lock(geometry_rwlock);
for (const ProjectedObstruction &other_projected_obstruction : p_other_geometry->_projected_obstructions) {
ProjectedObstruction projected_obstruction;
projected_obstruction.vertices.resize(other_projected_obstruction.vertices.size());
const float *other_obstruction_vertices_ptr = other_projected_obstruction.vertices.ptr();
float *obstruction_vertices_ptrw = projected_obstruction.vertices.ptrw();
for (int j = 0; j < other_projected_obstruction.vertices.size(); j++) {
obstruction_vertices_ptrw[j] = other_obstruction_vertices_ptr[j];
}
projected_obstruction.carve = other_projected_obstruction.carve;
_projected_obstructions.push_back(projected_obstruction);
}
}
}
void NavigationMeshSourceGeometryData2D::add_projected_obstruction(const Vector<Vector2> &p_vertices, bool p_carve) {
ERR_FAIL_COND(p_vertices.size() < 2);
ProjectedObstruction projected_obstruction;
projected_obstruction.vertices.resize(p_vertices.size() * 2);
projected_obstruction.carve = p_carve;
float *obstruction_vertices_ptrw = projected_obstruction.vertices.ptrw();
int vertex_index = 0;
for (const Vector2 &vertex : p_vertices) {
obstruction_vertices_ptrw[vertex_index++] = vertex.x;
obstruction_vertices_ptrw[vertex_index++] = vertex.y;
}
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
}
void NavigationMeshSourceGeometryData2D::set_projected_obstructions(const Array &p_array) {
clear_projected_obstructions();
for (int i = 0; i < p_array.size(); i++) {
Dictionary data = p_array[i];
ERR_FAIL_COND(!data.has("version"));
uint32_t po_version = data["version"];
if (po_version == 1) {
ERR_FAIL_COND(!data.has("vertices"));
ERR_FAIL_COND(!data.has("carve"));
}
ProjectedObstruction projected_obstruction;
projected_obstruction.vertices = Vector<float>(data["vertices"]);
projected_obstruction.carve = data["carve"];
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
}
}
Vector<NavigationMeshSourceGeometryData2D::ProjectedObstruction> NavigationMeshSourceGeometryData2D::_get_projected_obstructions() const {
RWLockRead read_lock(geometry_rwlock);
return _projected_obstructions;
}
Array NavigationMeshSourceGeometryData2D::get_projected_obstructions() const {
RWLockRead read_lock(geometry_rwlock);
Array ret;
ret.resize(_projected_obstructions.size());
for (int i = 0; i < _projected_obstructions.size(); i++) {
const ProjectedObstruction &projected_obstruction = _projected_obstructions[i];
Dictionary data;
data["version"] = (int)ProjectedObstruction::VERSION;
data["vertices"] = projected_obstruction.vertices;
data["carve"] = projected_obstruction.carve;
ret[i] = data;
}
return ret;
}
bool NavigationMeshSourceGeometryData2D::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "projected_obstructions") {
set_projected_obstructions(p_value);
return true;
}
return false;
}
bool NavigationMeshSourceGeometryData2D::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "projected_obstructions") {
r_ret = get_projected_obstructions();
return true;
}
return false;
}
void NavigationMeshSourceGeometryData2D::_bind_methods() {
@ -134,6 +243,12 @@ void NavigationMeshSourceGeometryData2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("merge", "other_geometry"), &NavigationMeshSourceGeometryData2D::merge);
ClassDB::bind_method(D_METHOD("add_projected_obstruction", "vertices", "carve"), &NavigationMeshSourceGeometryData2D::add_projected_obstruction);
ClassDB::bind_method(D_METHOD("clear_projected_obstructions"), &NavigationMeshSourceGeometryData2D::clear_projected_obstructions);
ClassDB::bind_method(D_METHOD("set_projected_obstructions", "projected_obstructions"), &NavigationMeshSourceGeometryData2D::set_projected_obstructions);
ClassDB::bind_method(D_METHOD("get_projected_obstructions"), &NavigationMeshSourceGeometryData2D::get_projected_obstructions);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "traversable_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_traversable_outlines", "get_traversable_outlines");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "obstruction_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_obstruction_outlines", "get_obstruction_outlines");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "projected_obstructions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_projected_obstructions", "get_projected_obstructions");
}

View File

@ -31,19 +31,36 @@
#ifndef NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_2D_H
#define NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_2D_H
#include "core/os/rw_lock.h"
#include "scene/2d/node_2d.h"
#include "scene/resources/navigation_polygon.h"
class NavigationMeshSourceGeometryData2D : public Resource {
GDCLASS(NavigationMeshSourceGeometryData2D, Resource);
RWLock geometry_rwlock;
Vector<Vector<Vector2>> traversable_outlines;
Vector<Vector<Vector2>> obstruction_outlines;
public:
struct ProjectedObstruction;
private:
Vector<ProjectedObstruction> _projected_obstructions;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
static void _bind_methods();
public:
struct ProjectedObstruction {
static inline uint32_t VERSION = 1; // Increase when format changes so we can detect outdated formats and provide compatibility.
Vector<float> vertices;
bool carve = false;
};
void _set_traversable_outlines(const Vector<Vector<Vector2>> &p_traversable_outlines);
const Vector<Vector<Vector2>> &_get_traversable_outlines() const { return traversable_outlines; }
@ -70,6 +87,13 @@ public:
bool has_data() { return traversable_outlines.size(); };
void clear();
void clear_projected_obstructions();
void add_projected_obstruction(const Vector<Vector2> &p_vertices, bool p_carve);
Vector<ProjectedObstruction> _get_projected_obstructions() const;
void set_projected_obstructions(const Array &p_array);
Array get_projected_obstructions() const;
void merge(const Ref<NavigationMeshSourceGeometryData2D> &p_other_geometry);

View File

@ -41,6 +41,12 @@ void NavigationMeshSourceGeometryData3D::set_indices(const Vector<int> &p_indice
void NavigationMeshSourceGeometryData3D::clear() {
vertices.clear();
indices.clear();
clear_projected_obstructions();
}
void NavigationMeshSourceGeometryData3D::clear_projected_obstructions() {
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.clear();
}
void NavigationMeshSourceGeometryData3D::_add_vertex(const Vector3 &p_vec3) {
@ -174,6 +180,121 @@ void NavigationMeshSourceGeometryData3D::merge(const Ref<NavigationMeshSourceGeo
for (int64_t i = number_of_indices_before_merge; i < indices.size(); i++) {
indices.set(i, indices[i] + number_of_vertices_before_merge / 3);
}
if (p_other_geometry->_projected_obstructions.size() > 0) {
RWLockWrite write_lock(geometry_rwlock);
for (const ProjectedObstruction &other_projected_obstruction : p_other_geometry->_projected_obstructions) {
ProjectedObstruction projected_obstruction;
projected_obstruction.vertices.resize(other_projected_obstruction.vertices.size());
const float *other_obstruction_vertices_ptr = other_projected_obstruction.vertices.ptr();
float *obstruction_vertices_ptrw = projected_obstruction.vertices.ptrw();
for (int j = 0; j < other_projected_obstruction.vertices.size(); j++) {
obstruction_vertices_ptrw[j] = other_obstruction_vertices_ptr[j];
}
projected_obstruction.elevation = other_projected_obstruction.elevation;
projected_obstruction.height = other_projected_obstruction.height;
projected_obstruction.carve = other_projected_obstruction.carve;
_projected_obstructions.push_back(projected_obstruction);
}
}
}
void NavigationMeshSourceGeometryData3D::add_projected_obstruction(const Vector<Vector3> &p_vertices, float p_elevation, float p_height, bool p_carve) {
ERR_FAIL_COND(p_vertices.size() < 3);
ERR_FAIL_COND(p_height < 0.0);
ProjectedObstruction projected_obstruction;
projected_obstruction.vertices.resize(p_vertices.size() * 3);
projected_obstruction.elevation = p_elevation;
projected_obstruction.height = p_height;
projected_obstruction.carve = p_carve;
float *obstruction_vertices_ptrw = projected_obstruction.vertices.ptrw();
int vertex_index = 0;
for (const Vector3 &vertex : p_vertices) {
obstruction_vertices_ptrw[vertex_index++] = vertex.x;
obstruction_vertices_ptrw[vertex_index++] = vertex.y;
obstruction_vertices_ptrw[vertex_index++] = vertex.z;
}
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
}
void NavigationMeshSourceGeometryData3D::set_projected_obstructions(const Array &p_array) {
clear_projected_obstructions();
for (int i = 0; i < p_array.size(); i++) {
Dictionary data = p_array[i];
ERR_FAIL_COND(!data.has("version"));
uint32_t po_version = data["version"];
if (po_version == 1) {
ERR_FAIL_COND(!data.has("vertices"));
ERR_FAIL_COND(!data.has("elevation"));
ERR_FAIL_COND(!data.has("height"));
ERR_FAIL_COND(!data.has("carve"));
}
ProjectedObstruction projected_obstruction;
projected_obstruction.vertices = Vector<float>(data["vertices"]);
projected_obstruction.elevation = data["elevation"];
projected_obstruction.height = data["height"];
projected_obstruction.carve = data["carve"];
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
}
}
Vector<NavigationMeshSourceGeometryData3D::ProjectedObstruction> NavigationMeshSourceGeometryData3D::_get_projected_obstructions() const {
RWLockRead read_lock(geometry_rwlock);
return _projected_obstructions;
}
Array NavigationMeshSourceGeometryData3D::get_projected_obstructions() const {
RWLockRead read_lock(geometry_rwlock);
Array ret;
ret.resize(_projected_obstructions.size());
for (int i = 0; i < _projected_obstructions.size(); i++) {
const ProjectedObstruction &projected_obstruction = _projected_obstructions[i];
Dictionary data;
data["version"] = (int)ProjectedObstruction::VERSION;
data["vertices"] = projected_obstruction.vertices;
data["elevation"] = projected_obstruction.elevation;
data["height"] = projected_obstruction.height;
data["carve"] = projected_obstruction.carve;
ret[i] = data;
}
return ret;
}
bool NavigationMeshSourceGeometryData3D::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "projected_obstructions") {
set_projected_obstructions(p_value);
return true;
}
return false;
}
bool NavigationMeshSourceGeometryData3D::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "projected_obstructions") {
r_ret = get_projected_obstructions();
return true;
}
return false;
}
void NavigationMeshSourceGeometryData3D::_bind_methods() {
@ -191,6 +312,12 @@ void NavigationMeshSourceGeometryData3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_faces", "faces", "xform"), &NavigationMeshSourceGeometryData3D::add_faces);
ClassDB::bind_method(D_METHOD("merge", "other_geometry"), &NavigationMeshSourceGeometryData3D::merge);
ClassDB::bind_method(D_METHOD("add_projected_obstruction", "vertices", "elevation", "height", "carve"), &NavigationMeshSourceGeometryData3D::add_projected_obstruction);
ClassDB::bind_method(D_METHOD("clear_projected_obstructions"), &NavigationMeshSourceGeometryData3D::clear_projected_obstructions);
ClassDB::bind_method(D_METHOD("set_projected_obstructions", "projected_obstructions"), &NavigationMeshSourceGeometryData3D::set_projected_obstructions);
ClassDB::bind_method(D_METHOD("get_projected_obstructions"), &NavigationMeshSourceGeometryData3D::get_projected_obstructions);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_indices", "get_indices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "projected_obstructions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_projected_obstructions", "get_projected_obstructions");
}

View File

@ -31,15 +31,25 @@
#ifndef NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_3D_H
#define NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_3D_H
#include "core/os/rw_lock.h"
#include "scene/resources/mesh.h"
class NavigationMeshSourceGeometryData3D : public Resource {
GDCLASS(NavigationMeshSourceGeometryData3D, Resource);
RWLock geometry_rwlock;
Vector<float> vertices;
Vector<int> indices;
public:
struct ProjectedObstruction;
private:
Vector<ProjectedObstruction> _projected_obstructions;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
static void _bind_methods();
private:
@ -49,6 +59,15 @@ private:
void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform);
public:
struct ProjectedObstruction {
static inline uint32_t VERSION = 1; // Increase when format changes so we can detect outdated formats and provide compatibility.
Vector<float> vertices;
float elevation = 0.0;
float height = 0.0;
bool carve = false;
};
// kept root node transform here on the geometry data
// if we add this transform to all exposed functions we need to break comp on all functions later
// when navmesh changes from global transform to relative to navregion
@ -63,6 +82,7 @@ public:
bool has_data() { return vertices.size() && indices.size(); };
void clear();
void clear_projected_obstructions();
void add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform);
void add_mesh_array(const Array &p_mesh_array, const Transform3D &p_xform);
@ -70,6 +90,12 @@ public:
void merge(const Ref<NavigationMeshSourceGeometryData3D> &p_other_geometry);
void add_projected_obstruction(const Vector<Vector3> &p_vertices, float p_elevation, float p_height, bool p_carve);
Vector<ProjectedObstruction> _get_projected_obstructions() const;
void set_projected_obstructions(const Array &p_array);
Array get_projected_obstructions() const;
NavigationMeshSourceGeometryData3D() {}
~NavigationMeshSourceGeometryData3D() { clear(); }
};