Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend NavigationMeshSourceGeometryData[23]D to allow data merging #88221

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/classes/NavigationMeshSourceGeometryData2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@
Returns [code]true[/code] when parsed source geometry data exists.
</description>
</method>
<method name="merge">
<return type="void" />
<param index="0" name="other_geometry" type="NavigationMeshSourceGeometryData2D" />
<description>
Adds the geometry data of another [NavigationMeshSourceGeometryData2D] to the navigation mesh baking data.
</description>
</method>
<method name="set_obstruction_outlines">
<return type="void" />
<param index="0" name="obstruction_outlines" type="PackedVector2Array[]" />
Expand Down
7 changes: 7 additions & 0 deletions doc/classes/NavigationMeshSourceGeometryData3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@
Returns [code]true[/code] when parsed source geometry data exists.
</description>
</method>
<method name="merge">
<return type="void" />
<param index="0" name="other_geometry" type="NavigationMeshSourceGeometryData3D" />
<description>
Adds the geometry data of another [NavigationMeshSourceGeometryData3D] to the navigation mesh baking data.
</description>
</method>
<method name="set_indices">
<return type="void" />
<param index="0" name="indices" type="PackedInt32Array" />
Expand Down
8 changes: 8 additions & 0 deletions scene/resources/navigation_mesh_source_geometry_data_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ void NavigationMeshSourceGeometryData2D::add_obstruction_outline(const PackedVec
}
}

void NavigationMeshSourceGeometryData2D::merge(const Ref<NavigationMeshSourceGeometryData2D> &p_other_geometry) {
// 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);
}

void NavigationMeshSourceGeometryData2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &NavigationMeshSourceGeometryData2D::clear);
ClassDB::bind_method(D_METHOD("has_data"), &NavigationMeshSourceGeometryData2D::has_data);
Expand All @@ -126,6 +132,8 @@ void NavigationMeshSourceGeometryData2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_traversable_outline", "shape_outline"), &NavigationMeshSourceGeometryData2D::add_traversable_outline);
ClassDB::bind_method(D_METHOD("add_obstruction_outline", "shape_outline"), &NavigationMeshSourceGeometryData2D::add_obstruction_outline);

ClassDB::bind_method(D_METHOD("merge", "other_geometry"), &NavigationMeshSourceGeometryData2D::merge);

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");
}
2 changes: 2 additions & 0 deletions scene/resources/navigation_mesh_source_geometry_data_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class NavigationMeshSourceGeometryData2D : public Resource {
bool has_data() { return traversable_outlines.size(); };
void clear();

void merge(const Ref<NavigationMeshSourceGeometryData2D> &p_other_geometry);

NavigationMeshSourceGeometryData2D() {}
~NavigationMeshSourceGeometryData2D() { clear(); }
};
Expand Down
12 changes: 12 additions & 0 deletions scene/resources/navigation_mesh_source_geometry_data_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ void NavigationMeshSourceGeometryData3D::add_faces(const PackedVector3Array &p_f
_add_faces(p_faces, root_node_transform * p_xform);
}

void NavigationMeshSourceGeometryData3D::merge(const Ref<NavigationMeshSourceGeometryData3D> &p_other_geometry) {
// No need to worry about `root_node_transform` here as the vertices are already xformed.
const int64_t number_of_vertices_before_merge = vertices.size();
const int64_t number_of_indices_before_merge = indices.size();
vertices.append_array(p_other_geometry->vertices);
indices.append_array(p_other_geometry->indices);
for (int64_t i = number_of_indices_before_merge; i < indices.size(); i++) {
indices.set(i, indices[i] + number_of_vertices_before_merge / 3);
}
}

void NavigationMeshSourceGeometryData3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationMeshSourceGeometryData3D::set_vertices);
ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationMeshSourceGeometryData3D::get_vertices);
Expand All @@ -178,6 +189,7 @@ void NavigationMeshSourceGeometryData3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_mesh", "mesh", "xform"), &NavigationMeshSourceGeometryData3D::add_mesh);
ClassDB::bind_method(D_METHOD("add_mesh_array", "mesh_array", "xform"), &NavigationMeshSourceGeometryData3D::add_mesh_array);
ClassDB::bind_method(D_METHOD("add_faces", "faces", "xform"), &NavigationMeshSourceGeometryData3D::add_faces);
ClassDB::bind_method(D_METHOD("merge", "other_geometry"), &NavigationMeshSourceGeometryData3D::merge);

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");
Expand Down
2 changes: 2 additions & 0 deletions scene/resources/navigation_mesh_source_geometry_data_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ class NavigationMeshSourceGeometryData3D : public Resource {
void add_mesh_array(const Array &p_mesh_array, const Transform3D &p_xform);
void add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform);

void merge(const Ref<NavigationMeshSourceGeometryData3D> &p_other_geometry);

NavigationMeshSourceGeometryData3D() {}
~NavigationMeshSourceGeometryData3D() { clear(); }
};
Expand Down
47 changes: 45 additions & 2 deletions tests/servers/test_navigation_server_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
#include "scene/resources/3d/primitive_meshes.h"
#include "servers/navigation_server_3d.h"

#include "tests/test_macros.h"

namespace TestNavigationServer3D {

// TODO: Find a more generic way to create `Callable` mocks.
Expand Down Expand Up @@ -580,6 +578,51 @@ TEST_SUITE("[Navigation]") {
}
#endif // DISABLE_DEPRECATED

TEST_CASE("[NavigationServer3D][SceneTree] Server should be able to parse geometry") {
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();

// Prepare scene tree with simple mesh to serve as an input geometry.
Node3D *node_3d = memnew(Node3D);
SceneTree::get_singleton()->get_root()->add_child(node_3d);
Ref<PlaneMesh> plane_mesh = memnew(PlaneMesh);
plane_mesh->set_size(Size2(10.0, 10.0));
MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
mesh_instance->set_mesh(plane_mesh);
node_3d->add_child(mesh_instance);

Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
Ref<NavigationMeshSourceGeometryData3D> source_geometry = memnew(NavigationMeshSourceGeometryData3D);
CHECK_EQ(source_geometry->get_vertices().size(), 0);
CHECK_EQ(source_geometry->get_indices().size(), 0);

navigation_server->parse_source_geometry_data(navigation_mesh, source_geometry, mesh_instance);
CHECK_EQ(source_geometry->get_vertices().size(), 12);
CHECK_EQ(source_geometry->get_indices().size(), 6);

SUBCASE("By default, parsing should remove any data that was parsed before") {
navigation_server->parse_source_geometry_data(navigation_mesh, source_geometry, mesh_instance);
CHECK_EQ(source_geometry->get_vertices().size(), 12);
CHECK_EQ(source_geometry->get_indices().size(), 6);
}

SUBCASE("Parsed geometry should be extendible with other geometry") {
source_geometry->merge(source_geometry); // Merging with itself.
const Vector<float> vertices = source_geometry->get_vertices();
const Vector<int> indices = source_geometry->get_indices();
REQUIRE_EQ(vertices.size(), 24);
REQUIRE_EQ(indices.size(), 12);
// Check if first newly added vertex is the same as first vertex.
CHECK_EQ(vertices[0], vertices[12]);
CHECK_EQ(vertices[1], vertices[13]);
CHECK_EQ(vertices[2], vertices[14]);
// Check if first newly added index is the same as first index.
CHECK_EQ(indices[0] + 4, indices[6]);
}

memdelete(mesh_instance);
memdelete(node_3d);
}

// This test case uses only public APIs on purpose - other test cases use simplified baking.
TEST_CASE("[NavigationServer3D][SceneTree] Server should be able to bake map correctly") {
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
Expand Down
Loading