feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -237,11 +237,29 @@ real_t GodotNavigationServer3D::map_get_link_connection_radius(RID p_map) const
|
|||
return map->get_link_connection_radius();
|
||||
}
|
||||
|
||||
Vector<Vector3> GodotNavigationServer3D::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
|
||||
Vector<Vector3> GodotNavigationServer3D::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) {
|
||||
const NavMap *map = map_owner.get_or_null(p_map);
|
||||
ERR_FAIL_NULL_V(map, Vector<Vector3>());
|
||||
|
||||
return map->get_path(p_origin, p_destination, p_optimize, p_navigation_layers, nullptr, nullptr, nullptr);
|
||||
Ref<NavigationPathQueryParameters3D> query_parameters;
|
||||
query_parameters.instantiate();
|
||||
|
||||
query_parameters->set_map(p_map);
|
||||
query_parameters->set_start_position(p_origin);
|
||||
query_parameters->set_target_position(p_destination);
|
||||
query_parameters->set_navigation_layers(p_navigation_layers);
|
||||
query_parameters->set_pathfinding_algorithm(NavigationPathQueryParameters3D::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR);
|
||||
query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL);
|
||||
if (!p_optimize) {
|
||||
query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PATH_POSTPROCESSING_EDGECENTERED);
|
||||
}
|
||||
|
||||
Ref<NavigationPathQueryResult3D> query_result;
|
||||
query_result.instantiate();
|
||||
|
||||
query_path(query_parameters, query_result);
|
||||
|
||||
return query_result->get_path();
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer3D::map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {
|
||||
|
|
@ -346,6 +364,19 @@ RID GodotNavigationServer3D::agent_get_map(RID p_agent) const {
|
|||
return RID();
|
||||
}
|
||||
|
||||
COMMAND_2(map_set_use_async_iterations, RID, p_map, bool, p_enabled) {
|
||||
NavMap *map = map_owner.get_or_null(p_map);
|
||||
ERR_FAIL_NULL(map);
|
||||
map->set_use_async_iterations(p_enabled);
|
||||
}
|
||||
|
||||
bool GodotNavigationServer3D::map_get_use_async_iterations(RID p_map) const {
|
||||
const NavMap *map = map_owner.get_or_null(p_map);
|
||||
ERR_FAIL_NULL_V(map, false);
|
||||
|
||||
return map->get_use_async_iterations();
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer3D::map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const {
|
||||
const NavMap *map = map_owner.get_or_null(p_map);
|
||||
ERR_FAIL_NULL_V(map, Vector3());
|
||||
|
|
@ -509,22 +540,52 @@ void GodotNavigationServer3D::region_bake_navigation_mesh(Ref<NavigationMesh> p_
|
|||
int GodotNavigationServer3D::region_get_connections_count(RID p_region) const {
|
||||
NavRegion *region = region_owner.get_or_null(p_region);
|
||||
ERR_FAIL_NULL_V(region, 0);
|
||||
|
||||
return region->get_connections_count();
|
||||
NavMap *map = region->get_map();
|
||||
if (map) {
|
||||
return map->get_region_connections_count(region);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer3D::region_get_connection_pathway_start(RID p_region, int p_connection_id) const {
|
||||
NavRegion *region = region_owner.get_or_null(p_region);
|
||||
ERR_FAIL_NULL_V(region, Vector3());
|
||||
|
||||
return region->get_connection_pathway_start(p_connection_id);
|
||||
NavMap *map = region->get_map();
|
||||
if (map) {
|
||||
return map->get_region_connection_pathway_start(region, p_connection_id);
|
||||
}
|
||||
return Vector3();
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer3D::region_get_connection_pathway_end(RID p_region, int p_connection_id) const {
|
||||
NavRegion *region = region_owner.get_or_null(p_region);
|
||||
ERR_FAIL_NULL_V(region, Vector3());
|
||||
NavMap *map = region->get_map();
|
||||
if (map) {
|
||||
return map->get_region_connection_pathway_end(region, p_connection_id);
|
||||
}
|
||||
return Vector3();
|
||||
}
|
||||
|
||||
return region->get_connection_pathway_end(p_connection_id);
|
||||
Vector3 GodotNavigationServer3D::region_get_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const {
|
||||
const NavRegion *region = region_owner.get_or_null(p_region);
|
||||
ERR_FAIL_NULL_V(region, Vector3());
|
||||
|
||||
return region->get_closest_point_to_segment(p_from, p_to, p_use_collision);
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer3D::region_get_closest_point(RID p_region, const Vector3 &p_point) const {
|
||||
const NavRegion *region = region_owner.get_or_null(p_region);
|
||||
ERR_FAIL_NULL_V(region, Vector3());
|
||||
|
||||
return region->get_closest_point_info(p_point).point;
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer3D::region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const {
|
||||
const NavRegion *region = region_owner.get_or_null(p_region);
|
||||
ERR_FAIL_NULL_V(region, Vector3());
|
||||
|
||||
return region->get_closest_point_info(p_point).normal;
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer3D::region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const {
|
||||
|
|
@ -534,6 +595,13 @@ Vector3 GodotNavigationServer3D::region_get_random_point(RID p_region, uint32_t
|
|||
return region->get_random_point(p_navigation_layers, p_uniformly);
|
||||
}
|
||||
|
||||
AABB GodotNavigationServer3D::region_get_bounds(RID p_region) const {
|
||||
const NavRegion *region = region_owner.get_or_null(p_region);
|
||||
ERR_FAIL_NULL_V(region, AABB());
|
||||
|
||||
return region->get_bounds();
|
||||
}
|
||||
|
||||
RID GodotNavigationServer3D::link_create() {
|
||||
MutexLock lock(operations_mutex);
|
||||
|
||||
|
|
@ -1102,7 +1170,7 @@ uint32_t GodotNavigationServer3D::obstacle_get_avoidance_layers(RID p_obstacle)
|
|||
void GodotNavigationServer3D::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
|
||||
#ifndef _3D_DISABLED
|
||||
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
|
||||
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
|
||||
ERR_FAIL_COND_MSG(p_navigation_mesh.is_null(), "Invalid navigation mesh.");
|
||||
ERR_FAIL_NULL_MSG(p_root_node, "No parsing root node specified.");
|
||||
ERR_FAIL_COND_MSG(!p_root_node->is_inside_tree(), "The root node needs to be inside the SceneTree.");
|
||||
|
||||
|
|
@ -1113,8 +1181,8 @@ void GodotNavigationServer3D::parse_source_geometry_data(const Ref<NavigationMes
|
|||
|
||||
void GodotNavigationServer3D::bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
|
||||
#ifndef _3D_DISABLED
|
||||
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
|
||||
ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
|
||||
ERR_FAIL_COND_MSG(p_navigation_mesh.is_null(), "Invalid navigation mesh.");
|
||||
ERR_FAIL_COND_MSG(p_source_geometry_data.is_null(), "Invalid NavigationMeshSourceGeometryData3D.");
|
||||
|
||||
ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
|
||||
NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
|
||||
|
|
@ -1123,8 +1191,8 @@ void GodotNavigationServer3D::bake_from_source_geometry_data(const Ref<Navigatio
|
|||
|
||||
void GodotNavigationServer3D::bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
|
||||
#ifndef _3D_DISABLED
|
||||
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
|
||||
ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
|
||||
ERR_FAIL_COND_MSG(p_navigation_mesh.is_null(), "Invalid navigation mesh.");
|
||||
ERR_FAIL_COND_MSG(p_source_geometry_data.is_null(), "Invalid NavigationMeshSourceGeometryData3D.");
|
||||
|
||||
ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
|
||||
NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data_async(p_navigation_mesh, p_source_geometry_data, p_callback);
|
||||
|
|
@ -1202,10 +1270,17 @@ COMMAND_1(free, RID, p_object) {
|
|||
} else if (obstacle_owner.owns(p_object)) {
|
||||
internal_free_obstacle(p_object);
|
||||
|
||||
} else if (geometry_parser_owner.owns(p_object)) {
|
||||
RWLockWrite write_lock(geometry_parser_rwlock);
|
||||
|
||||
NavMeshGeometryParser3D *parser = geometry_parser_owner.get_or_null(p_object);
|
||||
ERR_FAIL_NULL(parser);
|
||||
|
||||
generator_parsers.erase(parser);
|
||||
#ifndef _3D_DISABLED
|
||||
} else if (navmesh_generator_3d && navmesh_generator_3d->owns(p_object)) {
|
||||
navmesh_generator_3d->free(p_object);
|
||||
#endif // _3D_DISABLED
|
||||
NavMeshGenerator3D::get_singleton()->set_generator_parsers(generator_parsers);
|
||||
#endif
|
||||
geometry_parser_owner.free(parser->self);
|
||||
|
||||
} else {
|
||||
ERR_PRINT("Attempted to free a NavigationServer RID that did not exist (or was already freed).");
|
||||
|
|
@ -1298,6 +1373,7 @@ void GodotNavigationServer3D::process(real_t p_delta_time) {
|
|||
int _new_pm_edge_merge_count = 0;
|
||||
int _new_pm_edge_connection_count = 0;
|
||||
int _new_pm_edge_free_count = 0;
|
||||
int _new_pm_obstacle_count = 0;
|
||||
|
||||
// In c++ we can't be sure that this is performed in the main thread
|
||||
// even with mutable functions.
|
||||
|
|
@ -1315,6 +1391,7 @@ void GodotNavigationServer3D::process(real_t p_delta_time) {
|
|||
_new_pm_edge_merge_count += active_maps[i]->get_pm_edge_merge_count();
|
||||
_new_pm_edge_connection_count += active_maps[i]->get_pm_edge_connection_count();
|
||||
_new_pm_edge_free_count += active_maps[i]->get_pm_edge_free_count();
|
||||
_new_pm_obstacle_count += active_maps[i]->get_pm_obstacle_count();
|
||||
|
||||
// Emit a signal if a map changed.
|
||||
const uint32_t new_map_iteration_id = active_maps[i]->get_iteration_id();
|
||||
|
|
@ -1332,11 +1409,14 @@ void GodotNavigationServer3D::process(real_t p_delta_time) {
|
|||
pm_edge_merge_count = _new_pm_edge_merge_count;
|
||||
pm_edge_connection_count = _new_pm_edge_connection_count;
|
||||
pm_edge_free_count = _new_pm_edge_free_count;
|
||||
pm_obstacle_count = _new_pm_obstacle_count;
|
||||
}
|
||||
|
||||
void GodotNavigationServer3D::init() {
|
||||
#ifndef _3D_DISABLED
|
||||
navmesh_generator_3d = memnew(NavMeshGenerator3D);
|
||||
RWLockRead read_lock(geometry_parser_rwlock);
|
||||
navmesh_generator_3d->set_generator_parsers(generator_parsers);
|
||||
#endif // _3D_DISABLED
|
||||
}
|
||||
|
||||
|
|
@ -1351,103 +1431,38 @@ void GodotNavigationServer3D::finish() {
|
|||
#endif // _3D_DISABLED
|
||||
}
|
||||
|
||||
PathQueryResult GodotNavigationServer3D::_query_path(const PathQueryParameters &p_parameters) const {
|
||||
PathQueryResult r_query_result;
|
||||
void GodotNavigationServer3D::query_path(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result, const Callable &p_callback) {
|
||||
ERR_FAIL_COND(p_query_parameters.is_null());
|
||||
ERR_FAIL_COND(p_query_result.is_null());
|
||||
|
||||
const NavMap *map = map_owner.get_or_null(p_parameters.map);
|
||||
ERR_FAIL_NULL_V(map, r_query_result);
|
||||
NavMap *map = map_owner.get_or_null(p_query_parameters->get_map());
|
||||
ERR_FAIL_NULL(map);
|
||||
|
||||
// run the pathfinding
|
||||
|
||||
if (p_parameters.pathfinding_algorithm == PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR) {
|
||||
// while postprocessing is still part of map.get_path() need to check and route it here for the correct "optimize" post-processing
|
||||
if (p_parameters.path_postprocessing == PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL) {
|
||||
r_query_result.path = map->get_path(
|
||||
p_parameters.start_position,
|
||||
p_parameters.target_position,
|
||||
true,
|
||||
p_parameters.navigation_layers,
|
||||
p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES) ? &r_query_result.path_types : nullptr,
|
||||
p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS) ? &r_query_result.path_rids : nullptr,
|
||||
p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS) ? &r_query_result.path_owner_ids : nullptr);
|
||||
} else if (p_parameters.path_postprocessing == PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED) {
|
||||
r_query_result.path = map->get_path(
|
||||
p_parameters.start_position,
|
||||
p_parameters.target_position,
|
||||
false,
|
||||
p_parameters.navigation_layers,
|
||||
p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES) ? &r_query_result.path_types : nullptr,
|
||||
p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS) ? &r_query_result.path_rids : nullptr,
|
||||
p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS) ? &r_query_result.path_owner_ids : nullptr);
|
||||
}
|
||||
} else {
|
||||
return r_query_result;
|
||||
}
|
||||
|
||||
// add path postprocessing
|
||||
|
||||
if (r_query_result.path.size() > 2 && p_parameters.simplify_path) {
|
||||
const LocalVector<uint32_t> &simplified_path_indices = get_simplified_path_indices(r_query_result.path, p_parameters.simplify_epsilon);
|
||||
|
||||
uint32_t indices_count = simplified_path_indices.size();
|
||||
|
||||
{
|
||||
Vector3 *w = r_query_result.path.ptrw();
|
||||
const Vector3 *r = r_query_result.path.ptr();
|
||||
for (uint32_t i = 0; i < indices_count; i++) {
|
||||
w[i] = r[simplified_path_indices[i]];
|
||||
}
|
||||
r_query_result.path.resize(indices_count);
|
||||
}
|
||||
|
||||
if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES)) {
|
||||
int32_t *w = r_query_result.path_types.ptrw();
|
||||
const int32_t *r = r_query_result.path_types.ptr();
|
||||
for (uint32_t i = 0; i < indices_count; i++) {
|
||||
w[i] = r[simplified_path_indices[i]];
|
||||
}
|
||||
r_query_result.path_types.resize(indices_count);
|
||||
}
|
||||
|
||||
if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS)) {
|
||||
TypedArray<RID> simplified_path_rids;
|
||||
simplified_path_rids.resize(indices_count);
|
||||
for (uint32_t i = 0; i < indices_count; i++) {
|
||||
simplified_path_rids[i] = r_query_result.path_rids[i];
|
||||
}
|
||||
r_query_result.path_rids = simplified_path_rids;
|
||||
}
|
||||
|
||||
if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS)) {
|
||||
int64_t *w = r_query_result.path_owner_ids.ptrw();
|
||||
const int64_t *r = r_query_result.path_owner_ids.ptr();
|
||||
for (uint32_t i = 0; i < indices_count; i++) {
|
||||
w[i] = r[simplified_path_indices[i]];
|
||||
}
|
||||
r_query_result.path_owner_ids.resize(indices_count);
|
||||
}
|
||||
}
|
||||
|
||||
// add path stats
|
||||
|
||||
return r_query_result;
|
||||
NavMeshQueries3D::map_query_path(map, p_query_parameters, p_query_result, p_callback);
|
||||
}
|
||||
|
||||
RID GodotNavigationServer3D::source_geometry_parser_create() {
|
||||
RWLockWrite write_lock(geometry_parser_rwlock);
|
||||
|
||||
RID rid = geometry_parser_owner.make_rid();
|
||||
|
||||
NavMeshGeometryParser3D *parser = geometry_parser_owner.get_or_null(rid);
|
||||
parser->self = rid;
|
||||
|
||||
generator_parsers.push_back(parser);
|
||||
#ifndef _3D_DISABLED
|
||||
if (navmesh_generator_3d) {
|
||||
return navmesh_generator_3d->source_geometry_parser_create();
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
return RID();
|
||||
NavMeshGenerator3D::get_singleton()->set_generator_parsers(generator_parsers);
|
||||
#endif
|
||||
return rid;
|
||||
}
|
||||
|
||||
void GodotNavigationServer3D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) {
|
||||
#ifndef _3D_DISABLED
|
||||
if (navmesh_generator_3d) {
|
||||
navmesh_generator_3d->source_geometry_parser_set_callback(p_parser, p_callback);
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
RWLockWrite write_lock(geometry_parser_rwlock);
|
||||
|
||||
NavMeshGeometryParser3D *parser = geometry_parser_owner.get_or_null(p_parser);
|
||||
ERR_FAIL_NULL(parser);
|
||||
|
||||
parser->callback = p_callback;
|
||||
}
|
||||
|
||||
Vector<Vector3> GodotNavigationServer3D::simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) {
|
||||
|
|
@ -1457,86 +1472,32 @@ Vector<Vector3> GodotNavigationServer3D::simplify_path(const Vector<Vector3> &p_
|
|||
|
||||
p_epsilon = MAX(0.0, p_epsilon);
|
||||
|
||||
LocalVector<uint32_t> simplified_path_indices = get_simplified_path_indices(p_path, p_epsilon);
|
||||
LocalVector<Vector3> source_path;
|
||||
{
|
||||
source_path.resize(p_path.size());
|
||||
const Vector3 *r = p_path.ptr();
|
||||
for (uint32_t i = 0; i < p_path.size(); i++) {
|
||||
source_path[i] = r[i];
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t indices_count = simplified_path_indices.size();
|
||||
LocalVector<uint32_t> simplified_path_indices = NavMeshQueries3D::get_simplified_path_indices(source_path, p_epsilon);
|
||||
|
||||
uint32_t index_count = simplified_path_indices.size();
|
||||
|
||||
Vector<Vector3> simplified_path;
|
||||
simplified_path.resize(indices_count);
|
||||
|
||||
Vector3 *w = simplified_path.ptrw();
|
||||
const Vector3 *r = p_path.ptr();
|
||||
for (uint32_t i = 0; i < indices_count; i++) {
|
||||
w[i] = r[simplified_path_indices[i]];
|
||||
{
|
||||
simplified_path.resize(index_count);
|
||||
Vector3 *w = simplified_path.ptrw();
|
||||
const Vector3 *r = source_path.ptr();
|
||||
for (uint32_t i = 0; i < index_count; i++) {
|
||||
w[i] = r[simplified_path_indices[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return simplified_path;
|
||||
}
|
||||
|
||||
LocalVector<uint32_t> GodotNavigationServer3D::get_simplified_path_indices(const Vector<Vector3> &p_path, real_t p_epsilon) {
|
||||
p_epsilon = MAX(0.0, p_epsilon);
|
||||
real_t squared_epsilon = p_epsilon * p_epsilon;
|
||||
|
||||
LocalVector<bool> valid_points;
|
||||
valid_points.resize(p_path.size());
|
||||
for (uint32_t i = 0; i < valid_points.size(); i++) {
|
||||
valid_points[i] = false;
|
||||
}
|
||||
|
||||
simplify_path_segment(0, p_path.size() - 1, p_path, squared_epsilon, valid_points);
|
||||
|
||||
int valid_point_index = 0;
|
||||
|
||||
for (bool valid : valid_points) {
|
||||
if (valid) {
|
||||
valid_point_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
LocalVector<uint32_t> simplified_path_indices;
|
||||
simplified_path_indices.resize(valid_point_index);
|
||||
valid_point_index = 0;
|
||||
|
||||
for (uint32_t i = 0; i < valid_points.size(); i++) {
|
||||
if (valid_points[i]) {
|
||||
simplified_path_indices[valid_point_index] = i;
|
||||
valid_point_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return simplified_path_indices;
|
||||
}
|
||||
|
||||
void GodotNavigationServer3D::simplify_path_segment(int p_start_inx, int p_end_inx, const Vector<Vector3> &p_points, real_t p_epsilon, LocalVector<bool> &r_valid_points) {
|
||||
r_valid_points[p_start_inx] = true;
|
||||
r_valid_points[p_end_inx] = true;
|
||||
|
||||
const Vector3 &start_point = p_points[p_start_inx];
|
||||
const Vector3 &end_point = p_points[p_end_inx];
|
||||
|
||||
Vector3 path_segment[2] = { start_point, end_point };
|
||||
|
||||
real_t point_max_distance = 0.0;
|
||||
int point_max_index = 0;
|
||||
|
||||
for (int i = p_start_inx; i < p_end_inx; i++) {
|
||||
const Vector3 &checked_point = p_points[i];
|
||||
|
||||
const Vector3 closest_point = Geometry3D::get_closest_point_to_segment(checked_point, path_segment);
|
||||
real_t distance_squared = closest_point.distance_squared_to(checked_point);
|
||||
|
||||
if (distance_squared > point_max_distance) {
|
||||
point_max_index = i;
|
||||
point_max_distance = distance_squared;
|
||||
}
|
||||
}
|
||||
|
||||
if (point_max_distance > p_epsilon) {
|
||||
simplify_path_segment(p_start_inx, point_max_index, p_points, p_epsilon, r_valid_points);
|
||||
simplify_path_segment(point_max_index, p_end_inx, p_points, p_epsilon, r_valid_points);
|
||||
}
|
||||
}
|
||||
|
||||
int GodotNavigationServer3D::get_process_info(ProcessInfo p_info) const {
|
||||
switch (p_info) {
|
||||
case INFO_ACTIVE_MAPS: {
|
||||
|
|
@ -1566,6 +1527,9 @@ int GodotNavigationServer3D::get_process_info(ProcessInfo p_info) const {
|
|||
case INFO_EDGE_FREE_COUNT: {
|
||||
return pm_edge_free_count;
|
||||
} break;
|
||||
case INFO_OBSTACLE_COUNT: {
|
||||
return pm_obstacle_count;
|
||||
} break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@
|
|||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/rid.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/navigation/navigation_path_query_parameters_3d.h"
|
||||
#include "servers/navigation/navigation_path_query_result_3d.h"
|
||||
#include "servers/navigation_server_3d.h"
|
||||
|
||||
/// The commands are functions executed during the `sync` phase.
|
||||
|
|
@ -95,6 +97,7 @@ class GodotNavigationServer3D : public NavigationServer3D {
|
|||
int pm_edge_merge_count = 0;
|
||||
int pm_edge_connection_count = 0;
|
||||
int pm_edge_free_count = 0;
|
||||
int pm_obstacle_count = 0;
|
||||
|
||||
public:
|
||||
GodotNavigationServer3D();
|
||||
|
|
@ -129,7 +132,7 @@ public:
|
|||
COMMAND_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius);
|
||||
virtual real_t map_get_link_connection_radius(RID p_map) const override;
|
||||
|
||||
virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override;
|
||||
virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) override;
|
||||
|
||||
virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const override;
|
||||
virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const override;
|
||||
|
|
@ -144,6 +147,9 @@ public:
|
|||
virtual void map_force_update(RID p_map) override;
|
||||
virtual uint32_t map_get_iteration_id(RID p_map) const override;
|
||||
|
||||
COMMAND_2(map_set_use_async_iterations, RID, p_map, bool, p_enabled);
|
||||
virtual bool map_get_use_async_iterations(RID p_map) const override;
|
||||
|
||||
virtual Vector3 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const override;
|
||||
|
||||
virtual RID region_create() override;
|
||||
|
|
@ -177,7 +183,11 @@ public:
|
|||
virtual int region_get_connections_count(RID p_region) const override;
|
||||
virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override;
|
||||
virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override;
|
||||
virtual Vector3 region_get_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const override;
|
||||
virtual Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const override;
|
||||
virtual Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const override;
|
||||
virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override;
|
||||
virtual AABB region_get_bounds(RID p_region) const override;
|
||||
|
||||
virtual RID link_create() override;
|
||||
COMMAND_2(link_set_map, RID, p_link, RID, p_map);
|
||||
|
|
@ -269,10 +279,6 @@ public:
|
|||
|
||||
virtual Vector<Vector3> simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) override;
|
||||
|
||||
private:
|
||||
static void simplify_path_segment(int p_start_inx, int p_end_inx, const Vector<Vector3> &p_points, real_t p_epsilon, LocalVector<bool> &r_valid_points);
|
||||
static LocalVector<uint32_t> get_simplified_path_indices(const Vector<Vector3> &p_path, real_t p_epsilon);
|
||||
|
||||
public:
|
||||
COMMAND_1(free, RID, p_object);
|
||||
|
||||
|
|
@ -284,7 +290,7 @@ public:
|
|||
virtual void sync() override;
|
||||
virtual void finish() override;
|
||||
|
||||
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override;
|
||||
virtual void query_path(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result, const Callable &p_callback = Callable()) override;
|
||||
|
||||
int get_process_info(ProcessInfo p_info) const override;
|
||||
|
||||
|
|
|
|||
57
engine/modules/navigation/3d/nav_base_iteration_3d.h
Normal file
57
engine/modules/navigation/3d/nav_base_iteration_3d.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/**************************************************************************/
|
||||
/* nav_base_iteration_3d.h */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef NAV_BASE_ITERATION_3D_H
|
||||
#define NAV_BASE_ITERATION_3D_H
|
||||
|
||||
#include "servers/navigation/navigation_utilities.h"
|
||||
|
||||
struct NavBaseIteration {
|
||||
uint32_t id = UINT32_MAX;
|
||||
bool enabled = true;
|
||||
uint32_t navigation_layers = 1;
|
||||
real_t enter_cost = 0.0;
|
||||
real_t travel_cost = 1.0;
|
||||
NavigationUtilities::PathSegmentType owner_type;
|
||||
ObjectID owner_object_id;
|
||||
RID owner_rid;
|
||||
bool owner_use_edge_connections = false;
|
||||
|
||||
bool get_enabled() const { return enabled; }
|
||||
NavigationUtilities::PathSegmentType get_type() const { return owner_type; }
|
||||
RID get_self() const { return owner_rid; }
|
||||
ObjectID get_owner_id() const { return owner_object_id; }
|
||||
uint32_t get_navigation_layers() const { return navigation_layers; }
|
||||
real_t get_enter_cost() const { return enter_cost; }
|
||||
real_t get_travel_cost() const { return travel_cost; }
|
||||
bool get_use_edge_connections() const { return owner_use_edge_connections; }
|
||||
};
|
||||
|
||||
#endif // NAV_BASE_ITERATION_3D_H
|
||||
409
engine/modules/navigation/3d/nav_map_builder_3d.cpp
Normal file
409
engine/modules/navigation/3d/nav_map_builder_3d.cpp
Normal file
|
|
@ -0,0 +1,409 @@
|
|||
/**************************************************************************/
|
||||
/* nav_map_builder_3d.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
|
||||
#include "nav_map_builder_3d.h"
|
||||
|
||||
#include "../nav_link.h"
|
||||
#include "../nav_map.h"
|
||||
#include "../nav_region.h"
|
||||
#include "nav_map_iteration_3d.h"
|
||||
#include "nav_region_iteration_3d.h"
|
||||
|
||||
gd::PointKey NavMapBuilder3D::get_point_key(const Vector3 &p_pos, const Vector3 &p_cell_size) {
|
||||
const int x = static_cast<int>(Math::floor(p_pos.x / p_cell_size.x));
|
||||
const int y = static_cast<int>(Math::floor(p_pos.y / p_cell_size.y));
|
||||
const int z = static_cast<int>(Math::floor(p_pos.z / p_cell_size.z));
|
||||
|
||||
gd::PointKey p;
|
||||
p.key = 0;
|
||||
p.x = x;
|
||||
p.y = y;
|
||||
p.z = z;
|
||||
return p;
|
||||
}
|
||||
|
||||
void NavMapBuilder3D::build_navmap_iteration(NavMapIterationBuild &r_build) {
|
||||
gd::PerformanceData &performance_data = r_build.performance_data;
|
||||
|
||||
performance_data.pm_polygon_count = 0;
|
||||
performance_data.pm_edge_count = 0;
|
||||
performance_data.pm_edge_merge_count = 0;
|
||||
performance_data.pm_edge_connection_count = 0;
|
||||
performance_data.pm_edge_free_count = 0;
|
||||
|
||||
_build_step_gather_region_polygons(r_build);
|
||||
|
||||
_build_step_find_edge_connection_pairs(r_build);
|
||||
|
||||
_build_step_merge_edge_connection_pairs(r_build);
|
||||
|
||||
_build_step_edge_connection_margin_connections(r_build);
|
||||
|
||||
_build_step_navlink_connections(r_build);
|
||||
|
||||
_build_update_map_iteration(r_build);
|
||||
}
|
||||
|
||||
void NavMapBuilder3D::_build_step_gather_region_polygons(NavMapIterationBuild &r_build) {
|
||||
gd::PerformanceData &performance_data = r_build.performance_data;
|
||||
NavMapIteration *map_iteration = r_build.map_iteration;
|
||||
|
||||
LocalVector<NavRegionIteration> ®ions = map_iteration->region_iterations;
|
||||
HashMap<uint32_t, LocalVector<gd::Edge::Connection>> ®ion_external_connections = map_iteration->external_region_connections;
|
||||
|
||||
// Remove regions connections.
|
||||
region_external_connections.clear();
|
||||
for (const NavRegionIteration ®ion : regions) {
|
||||
region_external_connections[region.id] = LocalVector<gd::Edge::Connection>();
|
||||
}
|
||||
|
||||
// Copy all region polygons in the map.
|
||||
int polygon_count = 0;
|
||||
for (NavRegionIteration ®ion : regions) {
|
||||
if (!region.get_enabled()) {
|
||||
continue;
|
||||
}
|
||||
LocalVector<gd::Polygon> &polygons_source = region.navmesh_polygons;
|
||||
for (uint32_t n = 0; n < polygons_source.size(); n++) {
|
||||
polygons_source[n].id = polygon_count;
|
||||
polygon_count++;
|
||||
}
|
||||
}
|
||||
|
||||
performance_data.pm_polygon_count = polygon_count;
|
||||
r_build.polygon_count = polygon_count;
|
||||
}
|
||||
|
||||
void NavMapBuilder3D::_build_step_find_edge_connection_pairs(NavMapIterationBuild &r_build) {
|
||||
gd::PerformanceData &performance_data = r_build.performance_data;
|
||||
NavMapIteration *map_iteration = r_build.map_iteration;
|
||||
int polygon_count = r_build.polygon_count;
|
||||
|
||||
HashMap<gd::EdgeKey, gd::EdgeConnectionPair, gd::EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
|
||||
|
||||
// Group all edges per key.
|
||||
connection_pairs_map.clear();
|
||||
connection_pairs_map.reserve(polygon_count);
|
||||
int free_edges_count = 0; // How many ConnectionPairs have only one Connection.
|
||||
|
||||
for (NavRegionIteration ®ion : map_iteration->region_iterations) {
|
||||
if (!region.get_enabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (gd::Polygon &poly : region.navmesh_polygons) {
|
||||
for (uint32_t p = 0; p < poly.points.size(); p++) {
|
||||
const int next_point = (p + 1) % poly.points.size();
|
||||
const gd::EdgeKey ek(poly.points[p].key, poly.points[next_point].key);
|
||||
|
||||
HashMap<gd::EdgeKey, gd::EdgeConnectionPair, gd::EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
|
||||
if (!pair_it) {
|
||||
pair_it = connection_pairs_map.insert(ek, gd::EdgeConnectionPair());
|
||||
performance_data.pm_edge_count += 1;
|
||||
++free_edges_count;
|
||||
}
|
||||
gd::EdgeConnectionPair &pair = pair_it->value;
|
||||
if (pair.size < 2) {
|
||||
// Add the polygon/edge tuple to this key.
|
||||
gd::Edge::Connection new_connection;
|
||||
new_connection.polygon = &poly;
|
||||
new_connection.edge = p;
|
||||
new_connection.pathway_start = poly.points[p].pos;
|
||||
new_connection.pathway_end = poly.points[next_point].pos;
|
||||
|
||||
pair.connections[pair.size] = new_connection;
|
||||
++pair.size;
|
||||
if (pair.size == 2) {
|
||||
--free_edges_count;
|
||||
}
|
||||
|
||||
} else {
|
||||
// The edge is already connected with another edge, skip.
|
||||
ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to merge a navigation mesh polygon edge with another already-merged edge. This is usually caused by crossing edges, overlapping polygons, or a mismatch of the NavigationMesh / NavigationPolygon baked 'cell_size' and navigation map 'cell_size'. If you're certain none of above is the case, change 'navigation/3d/merge_rasterizer_cell_scale' to 0.001.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r_build.free_edge_count = free_edges_count;
|
||||
}
|
||||
|
||||
void NavMapBuilder3D::_build_step_merge_edge_connection_pairs(NavMapIterationBuild &r_build) {
|
||||
gd::PerformanceData &performance_data = r_build.performance_data;
|
||||
|
||||
HashMap<gd::EdgeKey, gd::EdgeConnectionPair, gd::EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
|
||||
LocalVector<gd::Edge::Connection> &free_edges = r_build.iter_free_edges;
|
||||
int free_edges_count = r_build.free_edge_count;
|
||||
bool use_edge_connections = r_build.use_edge_connections;
|
||||
|
||||
free_edges.clear();
|
||||
free_edges.reserve(free_edges_count);
|
||||
|
||||
for (const KeyValue<gd::EdgeKey, gd::EdgeConnectionPair> &pair_it : connection_pairs_map) {
|
||||
const gd::EdgeConnectionPair &pair = pair_it.value;
|
||||
if (pair.size == 2) {
|
||||
// Connect edge that are shared in different polygons.
|
||||
const gd::Edge::Connection &c1 = pair.connections[0];
|
||||
const gd::Edge::Connection &c2 = pair.connections[1];
|
||||
c1.polygon->edges[c1.edge].connections.push_back(c2);
|
||||
c2.polygon->edges[c2.edge].connections.push_back(c1);
|
||||
// Note: The pathway_start/end are full for those connection and do not need to be modified.
|
||||
performance_data.pm_edge_merge_count += 1;
|
||||
} else {
|
||||
CRASH_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));
|
||||
if (use_edge_connections && pair.connections[0].polygon->owner->get_use_edge_connections()) {
|
||||
free_edges.push_back(pair.connections[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavMapBuilder3D::_build_step_edge_connection_margin_connections(NavMapIterationBuild &r_build) {
|
||||
gd::PerformanceData &performance_data = r_build.performance_data;
|
||||
NavMapIteration *map_iteration = r_build.map_iteration;
|
||||
|
||||
real_t edge_connection_margin = r_build.edge_connection_margin;
|
||||
LocalVector<gd::Edge::Connection> &free_edges = r_build.iter_free_edges;
|
||||
HashMap<uint32_t, LocalVector<gd::Edge::Connection>> ®ion_external_connections = map_iteration->external_region_connections;
|
||||
|
||||
// Find the compatible near edges.
|
||||
//
|
||||
// Note:
|
||||
// Considering that the edges must be compatible (for obvious reasons)
|
||||
// to be connected, create new polygons to remove that small gap is
|
||||
// not really useful and would result in wasteful computation during
|
||||
// connection, integration and path finding.
|
||||
performance_data.pm_edge_free_count = free_edges.size();
|
||||
|
||||
const real_t edge_connection_margin_squared = edge_connection_margin * edge_connection_margin;
|
||||
|
||||
for (uint32_t i = 0; i < free_edges.size(); i++) {
|
||||
const gd::Edge::Connection &free_edge = free_edges[i];
|
||||
Vector3 edge_p1 = free_edge.polygon->points[free_edge.edge].pos;
|
||||
Vector3 edge_p2 = free_edge.polygon->points[(free_edge.edge + 1) % free_edge.polygon->points.size()].pos;
|
||||
|
||||
for (uint32_t j = 0; j < free_edges.size(); j++) {
|
||||
const gd::Edge::Connection &other_edge = free_edges[j];
|
||||
if (i == j || free_edge.polygon->owner == other_edge.polygon->owner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 other_edge_p1 = other_edge.polygon->points[other_edge.edge].pos;
|
||||
Vector3 other_edge_p2 = other_edge.polygon->points[(other_edge.edge + 1) % other_edge.polygon->points.size()].pos;
|
||||
|
||||
// Compute the projection of the opposite edge on the current one
|
||||
Vector3 edge_vector = edge_p2 - edge_p1;
|
||||
real_t projected_p1_ratio = edge_vector.dot(other_edge_p1 - edge_p1) / (edge_vector.length_squared());
|
||||
real_t projected_p2_ratio = edge_vector.dot(other_edge_p2 - edge_p1) / (edge_vector.length_squared());
|
||||
if ((projected_p1_ratio < 0.0 && projected_p2_ratio < 0.0) || (projected_p1_ratio > 1.0 && projected_p2_ratio > 1.0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the two edges are close to each other enough and compute a pathway between the two regions.
|
||||
Vector3 self1 = edge_vector * CLAMP(projected_p1_ratio, 0.0, 1.0) + edge_p1;
|
||||
Vector3 other1;
|
||||
if (projected_p1_ratio >= 0.0 && projected_p1_ratio <= 1.0) {
|
||||
other1 = other_edge_p1;
|
||||
} else {
|
||||
other1 = other_edge_p1.lerp(other_edge_p2, (1.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));
|
||||
}
|
||||
if (other1.distance_squared_to(self1) > edge_connection_margin_squared) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 self2 = edge_vector * CLAMP(projected_p2_ratio, 0.0, 1.0) + edge_p1;
|
||||
Vector3 other2;
|
||||
if (projected_p2_ratio >= 0.0 && projected_p2_ratio <= 1.0) {
|
||||
other2 = other_edge_p2;
|
||||
} else {
|
||||
other2 = other_edge_p1.lerp(other_edge_p2, (0.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));
|
||||
}
|
||||
if (other2.distance_squared_to(self2) > edge_connection_margin_squared) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The edges can now be connected.
|
||||
gd::Edge::Connection new_connection = other_edge;
|
||||
new_connection.pathway_start = (self1 + other1) / 2.0;
|
||||
new_connection.pathway_end = (self2 + other2) / 2.0;
|
||||
free_edge.polygon->edges[free_edge.edge].connections.push_back(new_connection);
|
||||
|
||||
// Add the connection to the region_connection map.
|
||||
region_external_connections[(uint32_t)free_edge.polygon->owner->id].push_back(new_connection);
|
||||
performance_data.pm_edge_connection_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavMapBuilder3D::_build_step_navlink_connections(NavMapIterationBuild &r_build) {
|
||||
NavMapIteration *map_iteration = r_build.map_iteration;
|
||||
|
||||
real_t link_connection_radius = r_build.link_connection_radius;
|
||||
Vector3 merge_rasterizer_cell_size = r_build.merge_rasterizer_cell_size;
|
||||
|
||||
LocalVector<gd::Polygon> &link_polygons = map_iteration->link_polygons;
|
||||
LocalVector<NavLinkIteration> &links = map_iteration->link_iterations;
|
||||
int polygon_count = r_build.polygon_count;
|
||||
|
||||
real_t link_connection_radius_sqr = link_connection_radius * link_connection_radius;
|
||||
uint32_t link_poly_idx = 0;
|
||||
link_polygons.resize(links.size());
|
||||
|
||||
// Search for polygons within range of a nav link.
|
||||
for (const NavLinkIteration &link : links) {
|
||||
if (!link.get_enabled()) {
|
||||
continue;
|
||||
}
|
||||
const Vector3 link_start_pos = link.get_start_position();
|
||||
const Vector3 link_end_pos = link.get_end_position();
|
||||
|
||||
gd::Polygon *closest_start_polygon = nullptr;
|
||||
real_t closest_start_sqr_dist = link_connection_radius_sqr;
|
||||
Vector3 closest_start_point;
|
||||
|
||||
gd::Polygon *closest_end_polygon = nullptr;
|
||||
real_t closest_end_sqr_dist = link_connection_radius_sqr;
|
||||
Vector3 closest_end_point;
|
||||
|
||||
for (NavRegionIteration ®ion : map_iteration->region_iterations) {
|
||||
if (!region.get_enabled()) {
|
||||
continue;
|
||||
}
|
||||
AABB region_bounds = region.get_bounds().grow(link_connection_radius);
|
||||
if (!region_bounds.has_point(link_start_pos) && !region_bounds.has_point(link_end_pos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (gd::Polygon &polyon : region.navmesh_polygons) {
|
||||
//for (gd::Polygon &polyon : polygons) {
|
||||
for (uint32_t point_id = 2; point_id < polyon.points.size(); point_id += 1) {
|
||||
const Face3 face(polyon.points[0].pos, polyon.points[point_id - 1].pos, polyon.points[point_id].pos);
|
||||
|
||||
{
|
||||
const Vector3 start_point = face.get_closest_point_to(link_start_pos);
|
||||
const real_t sqr_dist = start_point.distance_squared_to(link_start_pos);
|
||||
|
||||
// Pick the polygon that is within our radius and is closer than anything we've seen yet.
|
||||
if (sqr_dist < closest_start_sqr_dist) {
|
||||
closest_start_sqr_dist = sqr_dist;
|
||||
closest_start_point = start_point;
|
||||
closest_start_polygon = &polyon;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const Vector3 end_point = face.get_closest_point_to(link_end_pos);
|
||||
const real_t sqr_dist = end_point.distance_squared_to(link_end_pos);
|
||||
|
||||
// Pick the polygon that is within our radius and is closer than anything we've seen yet.
|
||||
if (sqr_dist < closest_end_sqr_dist) {
|
||||
closest_end_sqr_dist = sqr_dist;
|
||||
closest_end_point = end_point;
|
||||
closest_end_polygon = &polyon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have both a start and end point, then create a synthetic polygon to route through.
|
||||
if (closest_start_polygon && closest_end_polygon) {
|
||||
gd::Polygon &new_polygon = link_polygons[link_poly_idx++];
|
||||
new_polygon.id = polygon_count++;
|
||||
new_polygon.owner = &link;
|
||||
|
||||
new_polygon.edges.clear();
|
||||
new_polygon.edges.resize(4);
|
||||
new_polygon.points.resize(4);
|
||||
|
||||
// Build a set of vertices that create a thin polygon going from the start to the end point.
|
||||
new_polygon.points[0] = { closest_start_point, get_point_key(closest_start_point, merge_rasterizer_cell_size) };
|
||||
new_polygon.points[1] = { closest_start_point, get_point_key(closest_start_point, merge_rasterizer_cell_size) };
|
||||
new_polygon.points[2] = { closest_end_point, get_point_key(closest_end_point, merge_rasterizer_cell_size) };
|
||||
new_polygon.points[3] = { closest_end_point, get_point_key(closest_end_point, merge_rasterizer_cell_size) };
|
||||
|
||||
// Setup connections to go forward in the link.
|
||||
{
|
||||
gd::Edge::Connection entry_connection;
|
||||
entry_connection.polygon = &new_polygon;
|
||||
entry_connection.edge = -1;
|
||||
entry_connection.pathway_start = new_polygon.points[0].pos;
|
||||
entry_connection.pathway_end = new_polygon.points[1].pos;
|
||||
closest_start_polygon->edges[0].connections.push_back(entry_connection);
|
||||
|
||||
gd::Edge::Connection exit_connection;
|
||||
exit_connection.polygon = closest_end_polygon;
|
||||
exit_connection.edge = -1;
|
||||
exit_connection.pathway_start = new_polygon.points[2].pos;
|
||||
exit_connection.pathway_end = new_polygon.points[3].pos;
|
||||
new_polygon.edges[2].connections.push_back(exit_connection);
|
||||
}
|
||||
|
||||
// If the link is bi-directional, create connections from the end to the start.
|
||||
if (link.is_bidirectional()) {
|
||||
gd::Edge::Connection entry_connection;
|
||||
entry_connection.polygon = &new_polygon;
|
||||
entry_connection.edge = -1;
|
||||
entry_connection.pathway_start = new_polygon.points[2].pos;
|
||||
entry_connection.pathway_end = new_polygon.points[3].pos;
|
||||
closest_end_polygon->edges[0].connections.push_back(entry_connection);
|
||||
|
||||
gd::Edge::Connection exit_connection;
|
||||
exit_connection.polygon = closest_start_polygon;
|
||||
exit_connection.edge = -1;
|
||||
exit_connection.pathway_start = new_polygon.points[0].pos;
|
||||
exit_connection.pathway_end = new_polygon.points[1].pos;
|
||||
new_polygon.edges[0].connections.push_back(exit_connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavMapBuilder3D::_build_update_map_iteration(NavMapIterationBuild &r_build) {
|
||||
NavMapIteration *map_iteration = r_build.map_iteration;
|
||||
|
||||
LocalVector<gd::Polygon> &link_polygons = map_iteration->link_polygons;
|
||||
|
||||
map_iteration->navmesh_polygon_count = r_build.polygon_count;
|
||||
map_iteration->link_polygon_count = link_polygons.size();
|
||||
|
||||
map_iteration->path_query_slots_mutex.lock();
|
||||
for (NavMeshQueries3D::PathQuerySlot &p_path_query_slot : map_iteration->path_query_slots) {
|
||||
p_path_query_slot.traversable_polys.clear();
|
||||
p_path_query_slot.traversable_polys.reserve(map_iteration->navmesh_polygon_count * 0.25);
|
||||
p_path_query_slot.path_corridor.clear();
|
||||
p_path_query_slot.path_corridor.resize(map_iteration->navmesh_polygon_count + map_iteration->link_polygon_count);
|
||||
}
|
||||
map_iteration->path_query_slots_mutex.unlock();
|
||||
}
|
||||
|
||||
#endif // _3D_DISABLED
|
||||
52
engine/modules/navigation/3d/nav_map_builder_3d.h
Normal file
52
engine/modules/navigation/3d/nav_map_builder_3d.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/**************************************************************************/
|
||||
/* nav_map_builder_3d.h */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef NAV_MAP_BUILDER_3D_H
|
||||
#define NAV_MAP_BUILDER_3D_H
|
||||
|
||||
#include "../nav_utils.h"
|
||||
|
||||
struct NavMapIterationBuild;
|
||||
|
||||
class NavMapBuilder3D {
|
||||
static void _build_step_gather_region_polygons(NavMapIterationBuild &r_build);
|
||||
static void _build_step_find_edge_connection_pairs(NavMapIterationBuild &r_build);
|
||||
static void _build_step_merge_edge_connection_pairs(NavMapIterationBuild &r_build);
|
||||
static void _build_step_edge_connection_margin_connections(NavMapIterationBuild &r_build);
|
||||
static void _build_step_navlink_connections(NavMapIterationBuild &r_build);
|
||||
static void _build_update_map_iteration(NavMapIterationBuild &r_build);
|
||||
|
||||
public:
|
||||
static gd::PointKey get_point_key(const Vector3 &p_pos, const Vector3 &p_cell_size);
|
||||
|
||||
static void build_navmap_iteration(NavMapIterationBuild &r_build);
|
||||
};
|
||||
|
||||
#endif // NAV_MAP_BUILDER_3D_H
|
||||
114
engine/modules/navigation/3d/nav_map_iteration_3d.h
Normal file
114
engine/modules/navigation/3d/nav_map_iteration_3d.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/**************************************************************************/
|
||||
/* nav_map_iteration_3d.h */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef NAV_MAP_ITERATION_3D_H
|
||||
#define NAV_MAP_ITERATION_3D_H
|
||||
|
||||
#include "../nav_rid.h"
|
||||
#include "../nav_utils.h"
|
||||
#include "nav_mesh_queries_3d.h"
|
||||
|
||||
#include "core/math/math_defs.h"
|
||||
#include "core/os/semaphore.h"
|
||||
|
||||
struct NavLinkIteration;
|
||||
class NavRegion;
|
||||
struct NavRegionIteration;
|
||||
struct NavMapIteration;
|
||||
|
||||
struct NavMapIterationBuild {
|
||||
Vector3 merge_rasterizer_cell_size;
|
||||
bool use_edge_connections = true;
|
||||
real_t edge_connection_margin;
|
||||
real_t link_connection_radius;
|
||||
gd::PerformanceData performance_data;
|
||||
int polygon_count = 0;
|
||||
int free_edge_count = 0;
|
||||
|
||||
HashMap<gd::EdgeKey, gd::EdgeConnectionPair, gd::EdgeKey> iter_connection_pairs_map;
|
||||
LocalVector<gd::Edge::Connection> iter_free_edges;
|
||||
|
||||
NavMapIteration *map_iteration = nullptr;
|
||||
|
||||
int navmesh_polygon_count = 0;
|
||||
int link_polygon_count = 0;
|
||||
|
||||
void reset() {
|
||||
performance_data.reset();
|
||||
|
||||
iter_connection_pairs_map.clear();
|
||||
iter_free_edges.clear();
|
||||
polygon_count = 0;
|
||||
free_edge_count = 0;
|
||||
|
||||
navmesh_polygon_count = 0;
|
||||
link_polygon_count = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct NavMapIteration {
|
||||
mutable SafeNumeric<uint32_t> users;
|
||||
RWLock rwlock;
|
||||
|
||||
Vector3 map_up;
|
||||
LocalVector<gd::Polygon> link_polygons;
|
||||
|
||||
LocalVector<NavRegionIteration> region_iterations;
|
||||
LocalVector<NavLinkIteration> link_iterations;
|
||||
|
||||
int navmesh_polygon_count = 0;
|
||||
int link_polygon_count = 0;
|
||||
|
||||
// The edge connections that the map builds on top with the edge connection margin.
|
||||
HashMap<uint32_t, LocalVector<gd::Edge::Connection>> external_region_connections;
|
||||
|
||||
HashMap<NavRegion *, uint32_t> region_ptr_to_region_id;
|
||||
|
||||
LocalVector<NavMeshQueries3D::PathQuerySlot> path_query_slots;
|
||||
Mutex path_query_slots_mutex;
|
||||
Semaphore path_query_slots_semaphore;
|
||||
};
|
||||
|
||||
class NavMapIterationRead {
|
||||
const NavMapIteration &map_iteration;
|
||||
|
||||
public:
|
||||
_ALWAYS_INLINE_ NavMapIterationRead(const NavMapIteration &p_iteration) :
|
||||
map_iteration(p_iteration) {
|
||||
map_iteration.rwlock.read_lock();
|
||||
map_iteration.users.increment();
|
||||
}
|
||||
_ALWAYS_INLINE_ ~NavMapIterationRead() {
|
||||
map_iteration.users.decrement();
|
||||
map_iteration.rwlock.read_unlock();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // NAV_MAP_ITERATION_3D_H
|
||||
|
|
@ -33,47 +33,23 @@
|
|||
#include "nav_mesh_generator_3d.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/math/convex_hull.h"
|
||||
#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"
|
||||
#include "scene/resources/3d/concave_polygon_shape_3d.h"
|
||||
#include "scene/resources/3d/convex_polygon_shape_3d.h"
|
||||
#include "scene/resources/3d/cylinder_shape_3d.h"
|
||||
#include "scene/resources/3d/height_map_shape_3d.h"
|
||||
#include "scene/3d/node_3d.h"
|
||||
#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
|
||||
#include "scene/resources/3d/primitive_meshes.h"
|
||||
#include "scene/resources/3d/shape_3d.h"
|
||||
#include "scene/resources/3d/sphere_shape_3d.h"
|
||||
#include "scene/resources/3d/world_boundary_shape_3d.h"
|
||||
#include "scene/resources/navigation_mesh.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
|
||||
|
||||
#ifdef MODULE_CSG_ENABLED
|
||||
#include "modules/csg/csg_shape.h"
|
||||
#endif
|
||||
#ifdef MODULE_GRIDMAP_ENABLED
|
||||
#include "modules/gridmap/grid_map.h"
|
||||
#endif
|
||||
|
||||
#include <Recast.h>
|
||||
|
||||
NavMeshGenerator3D *NavMeshGenerator3D::singleton = nullptr;
|
||||
Mutex NavMeshGenerator3D::baking_navmesh_mutex;
|
||||
Mutex NavMeshGenerator3D::generator_task_mutex;
|
||||
RWLock NavMeshGenerator3D::generator_rid_rwlock;
|
||||
RWLock NavMeshGenerator3D::generator_parsers_rwlock;
|
||||
bool NavMeshGenerator3D::use_threads = true;
|
||||
bool NavMeshGenerator3D::baking_use_multiple_threads = true;
|
||||
bool NavMeshGenerator3D::baking_use_high_priority_threads = true;
|
||||
HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes;
|
||||
HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks;
|
||||
RID_Owner<NavMeshGenerator3D::NavMeshGeometryParser3D> NavMeshGenerator3D::generator_parser_owner;
|
||||
LocalVector<NavMeshGenerator3D::NavMeshGeometryParser3D *> NavMeshGenerator3D::generator_parsers;
|
||||
LocalVector<NavMeshGeometryParser3D *> NavMeshGenerator3D::generator_parsers;
|
||||
|
||||
NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
|
||||
return singleton;
|
||||
|
|
@ -100,57 +76,52 @@ void NavMeshGenerator3D::sync() {
|
|||
return;
|
||||
}
|
||||
|
||||
baking_navmesh_mutex.lock();
|
||||
generator_task_mutex.lock();
|
||||
MutexLock baking_navmesh_lock(baking_navmesh_mutex);
|
||||
{
|
||||
MutexLock generator_task_lock(generator_task_mutex);
|
||||
|
||||
LocalVector<WorkerThreadPool::TaskID> finished_task_ids;
|
||||
LocalVector<WorkerThreadPool::TaskID> finished_task_ids;
|
||||
|
||||
for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
|
||||
if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) {
|
||||
WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
|
||||
finished_task_ids.push_back(E.key);
|
||||
for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
|
||||
if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) {
|
||||
WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
|
||||
finished_task_ids.push_back(E.key);
|
||||
|
||||
NavMeshGeneratorTask3D *generator_task = E.value;
|
||||
DEV_ASSERT(generator_task->status == NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED);
|
||||
NavMeshGeneratorTask3D *generator_task = E.value;
|
||||
DEV_ASSERT(generator_task->status == NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED);
|
||||
|
||||
baking_navmeshes.erase(generator_task->navigation_mesh);
|
||||
if (generator_task->callback.is_valid()) {
|
||||
generator_emit_callback(generator_task->callback);
|
||||
baking_navmeshes.erase(generator_task->navigation_mesh);
|
||||
if (generator_task->callback.is_valid()) {
|
||||
generator_emit_callback(generator_task->callback);
|
||||
}
|
||||
memdelete(generator_task);
|
||||
}
|
||||
memdelete(generator_task);
|
||||
}
|
||||
|
||||
for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) {
|
||||
generator_tasks.erase(finished_task_id);
|
||||
}
|
||||
}
|
||||
|
||||
for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) {
|
||||
generator_tasks.erase(finished_task_id);
|
||||
}
|
||||
|
||||
generator_task_mutex.unlock();
|
||||
baking_navmesh_mutex.unlock();
|
||||
}
|
||||
|
||||
void NavMeshGenerator3D::cleanup() {
|
||||
baking_navmesh_mutex.lock();
|
||||
generator_task_mutex.lock();
|
||||
MutexLock baking_navmesh_lock(baking_navmesh_mutex);
|
||||
{
|
||||
MutexLock generator_task_lock(generator_task_mutex);
|
||||
|
||||
baking_navmeshes.clear();
|
||||
baking_navmeshes.clear();
|
||||
|
||||
for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
|
||||
WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
|
||||
NavMeshGeneratorTask3D *generator_task = E.value;
|
||||
memdelete(generator_task);
|
||||
for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
|
||||
WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
|
||||
NavMeshGeneratorTask3D *generator_task = E.value;
|
||||
memdelete(generator_task);
|
||||
}
|
||||
generator_tasks.clear();
|
||||
|
||||
generator_parsers_rwlock.write_lock();
|
||||
generator_parsers.clear();
|
||||
generator_parsers_rwlock.write_unlock();
|
||||
}
|
||||
generator_tasks.clear();
|
||||
|
||||
generator_rid_rwlock.write_lock();
|
||||
for (NavMeshGeometryParser3D *parser : generator_parsers) {
|
||||
generator_parser_owner.free(parser->self);
|
||||
}
|
||||
generator_parsers.clear();
|
||||
generator_rid_rwlock.write_unlock();
|
||||
|
||||
generator_task_mutex.unlock();
|
||||
baking_navmesh_mutex.unlock();
|
||||
}
|
||||
|
||||
void NavMeshGenerator3D::finish() {
|
||||
|
|
@ -159,10 +130,10 @@ void NavMeshGenerator3D::finish() {
|
|||
|
||||
void NavMeshGenerator3D::parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
|
||||
ERR_FAIL_COND(!Thread::is_main_thread());
|
||||
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
|
||||
ERR_FAIL_COND(p_navigation_mesh.is_null());
|
||||
ERR_FAIL_NULL(p_root_node);
|
||||
ERR_FAIL_COND(!p_root_node->is_inside_tree());
|
||||
ERR_FAIL_COND(!p_source_geometry_data.is_valid());
|
||||
ERR_FAIL_COND(p_source_geometry_data.is_null());
|
||||
|
||||
generator_parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node);
|
||||
|
||||
|
|
@ -172,8 +143,8 @@ void NavMeshGenerator3D::parse_source_geometry_data(Ref<NavigationMesh> p_naviga
|
|||
}
|
||||
|
||||
void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
|
||||
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
|
||||
ERR_FAIL_COND(!p_source_geometry_data.is_valid());
|
||||
ERR_FAIL_COND(p_navigation_mesh.is_null());
|
||||
ERR_FAIL_COND(p_source_geometry_data.is_null());
|
||||
|
||||
if (!p_source_geometry_data->has_data()) {
|
||||
p_navigation_mesh->clear();
|
||||
|
|
@ -202,8 +173,8 @@ void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_na
|
|||
}
|
||||
|
||||
void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
|
||||
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
|
||||
ERR_FAIL_COND(!p_source_geometry_data.is_valid());
|
||||
ERR_FAIL_COND(p_navigation_mesh.is_null());
|
||||
ERR_FAIL_COND(p_source_geometry_data.is_null());
|
||||
|
||||
if (!p_source_geometry_data->has_data()) {
|
||||
p_navigation_mesh->clear();
|
||||
|
|
@ -226,7 +197,7 @@ void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh
|
|||
baking_navmeshes.insert(p_navigation_mesh);
|
||||
baking_navmesh_mutex.unlock();
|
||||
|
||||
generator_task_mutex.lock();
|
||||
MutexLock generator_task_lock(generator_task_mutex);
|
||||
NavMeshGeneratorTask3D *generator_task = memnew(NavMeshGeneratorTask3D);
|
||||
generator_task->navigation_mesh = p_navigation_mesh;
|
||||
generator_task->source_geometry_data = p_source_geometry_data;
|
||||
|
|
@ -234,14 +205,11 @@ void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh
|
|||
generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
|
||||
generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator3D::generator_thread_bake, generator_task, NavMeshGenerator3D::baking_use_high_priority_threads, SNAME("NavMeshGeneratorBake3D"));
|
||||
generator_tasks.insert(generator_task->thread_task_id, generator_task);
|
||||
generator_task_mutex.unlock();
|
||||
}
|
||||
|
||||
bool NavMeshGenerator3D::is_baking(Ref<NavigationMesh> p_navigation_mesh) {
|
||||
baking_navmesh_mutex.lock();
|
||||
bool baking = baking_navmeshes.has(p_navigation_mesh);
|
||||
baking_navmesh_mutex.unlock();
|
||||
return baking;
|
||||
MutexLock baking_navmesh_lock(baking_navmesh_mutex);
|
||||
return baking_navmeshes.has(p_navigation_mesh);
|
||||
}
|
||||
|
||||
void NavMeshGenerator3D::generator_thread_bake(void *p_arg) {
|
||||
|
|
@ -253,25 +221,14 @@ void NavMeshGenerator3D::generator_thread_bake(void *p_arg) {
|
|||
}
|
||||
|
||||
void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children) {
|
||||
generator_parse_meshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
|
||||
generator_parse_multimeshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
|
||||
generator_parse_staticbody3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
|
||||
#ifdef MODULE_CSG_ENABLED
|
||||
generator_parse_csgshape3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
|
||||
#endif
|
||||
#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);
|
||||
|
||||
generator_rid_rwlock.read_lock();
|
||||
generator_parsers_rwlock.read_lock();
|
||||
for (const NavMeshGeometryParser3D *parser : generator_parsers) {
|
||||
if (!parser->callback.is_valid()) {
|
||||
continue;
|
||||
}
|
||||
parser->callback.call(p_navigation_mesh, p_source_geometry_data, p_node);
|
||||
}
|
||||
generator_rid_rwlock.read_unlock();
|
||||
generator_parsers_rwlock.read_unlock();
|
||||
|
||||
if (p_recurse_children) {
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
|
|
@ -280,367 +237,9 @@ void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh>
|
|||
}
|
||||
}
|
||||
|
||||
void NavMeshGenerator3D::generator_parse_meshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
|
||||
MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node);
|
||||
|
||||
if (mesh_instance) {
|
||||
NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
|
||||
if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) {
|
||||
Ref<Mesh> mesh = mesh_instance->get_mesh();
|
||||
if (mesh.is_valid()) {
|
||||
p_source_geometry_data->add_mesh(mesh, mesh_instance->get_global_transform());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavMeshGenerator3D::generator_parse_multimeshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
|
||||
MultiMeshInstance3D *multimesh_instance = Object::cast_to<MultiMeshInstance3D>(p_node);
|
||||
|
||||
if (multimesh_instance) {
|
||||
NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
|
||||
if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) {
|
||||
Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh();
|
||||
if (multimesh.is_valid()) {
|
||||
Ref<Mesh> mesh = multimesh->get_mesh();
|
||||
if (mesh.is_valid()) {
|
||||
int n = multimesh->get_visible_instance_count();
|
||||
if (n == -1) {
|
||||
n = multimesh->get_instance_count();
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
p_source_geometry_data->add_mesh(mesh, multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavMeshGenerator3D::generator_parse_staticbody3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
|
||||
StaticBody3D *static_body = Object::cast_to<StaticBody3D>(p_node);
|
||||
|
||||
if (static_body) {
|
||||
NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask();
|
||||
|
||||
if ((parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) && (static_body->get_collision_layer() & parsed_collision_mask)) {
|
||||
List<uint32_t> shape_owners;
|
||||
static_body->get_shape_owners(&shape_owners);
|
||||
for (uint32_t shape_owner : shape_owners) {
|
||||
if (static_body->is_shape_owner_disabled(shape_owner)) {
|
||||
continue;
|
||||
}
|
||||
const int shape_count = static_body->shape_owner_get_shape_count(shape_owner);
|
||||
for (int shape_index = 0; shape_index < shape_count; shape_index++) {
|
||||
Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, shape_index);
|
||||
if (s.is_null()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Transform3D transform = static_body->get_global_transform() * static_body->shape_owner_get_transform(shape_owner);
|
||||
|
||||
BoxShape3D *box = Object::cast_to<BoxShape3D>(*s);
|
||||
if (box) {
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
BoxMesh::create_mesh_array(arr, box->get_size());
|
||||
p_source_geometry_data->add_mesh_array(arr, transform);
|
||||
}
|
||||
|
||||
CapsuleShape3D *capsule = Object::cast_to<CapsuleShape3D>(*s);
|
||||
if (capsule) {
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
CapsuleMesh::create_mesh_array(arr, capsule->get_radius(), capsule->get_height());
|
||||
p_source_geometry_data->add_mesh_array(arr, transform);
|
||||
}
|
||||
|
||||
CylinderShape3D *cylinder = Object::cast_to<CylinderShape3D>(*s);
|
||||
if (cylinder) {
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
CylinderMesh::create_mesh_array(arr, cylinder->get_radius(), cylinder->get_radius(), cylinder->get_height());
|
||||
p_source_geometry_data->add_mesh_array(arr, transform);
|
||||
}
|
||||
|
||||
SphereShape3D *sphere = Object::cast_to<SphereShape3D>(*s);
|
||||
if (sphere) {
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
SphereMesh::create_mesh_array(arr, sphere->get_radius(), sphere->get_radius() * 2.0);
|
||||
p_source_geometry_data->add_mesh_array(arr, transform);
|
||||
}
|
||||
|
||||
ConcavePolygonShape3D *concave_polygon = Object::cast_to<ConcavePolygonShape3D>(*s);
|
||||
if (concave_polygon) {
|
||||
p_source_geometry_data->add_faces(concave_polygon->get_faces(), transform);
|
||||
}
|
||||
|
||||
ConvexPolygonShape3D *convex_polygon = Object::cast_to<ConvexPolygonShape3D>(*s);
|
||||
if (convex_polygon) {
|
||||
Vector<Vector3> varr = Variant(convex_polygon->get_points());
|
||||
Geometry3D::MeshData md;
|
||||
|
||||
Error err = ConvexHullComputer::convex_hull(varr, md);
|
||||
|
||||
if (err == OK) {
|
||||
PackedVector3Array faces;
|
||||
|
||||
for (const Geometry3D::MeshData::Face &face : md.faces) {
|
||||
for (uint32_t k = 2; k < face.indices.size(); ++k) {
|
||||
faces.push_back(md.vertices[face.indices[0]]);
|
||||
faces.push_back(md.vertices[face.indices[k - 1]]);
|
||||
faces.push_back(md.vertices[face.indices[k]]);
|
||||
}
|
||||
}
|
||||
|
||||
p_source_geometry_data->add_faces(faces, transform);
|
||||
}
|
||||
}
|
||||
|
||||
HeightMapShape3D *heightmap_shape = Object::cast_to<HeightMapShape3D>(*s);
|
||||
if (heightmap_shape) {
|
||||
int heightmap_depth = heightmap_shape->get_map_depth();
|
||||
int heightmap_width = heightmap_shape->get_map_width();
|
||||
|
||||
if (heightmap_depth >= 2 && heightmap_width >= 2) {
|
||||
const Vector<real_t> &map_data = heightmap_shape->get_map_data();
|
||||
|
||||
Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1);
|
||||
Vector3 start = Vector3(heightmap_gridsize.x, 0, heightmap_gridsize.y) * -0.5;
|
||||
|
||||
Vector<Vector3> vertex_array;
|
||||
vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6);
|
||||
Vector3 *vertex_array_ptrw = vertex_array.ptrw();
|
||||
const real_t *map_data_ptr = map_data.ptr();
|
||||
int vertex_index = 0;
|
||||
|
||||
for (int d = 0; d < heightmap_depth - 1; d++) {
|
||||
for (int w = 0; w < heightmap_width - 1; w++) {
|
||||
vertex_array_ptrw[vertex_index] = start + Vector3(w, map_data_ptr[(heightmap_width * d) + w], d);
|
||||
vertex_array_ptrw[vertex_index + 1] = start + Vector3(w + 1, map_data_ptr[(heightmap_width * d) + w + 1], d);
|
||||
vertex_array_ptrw[vertex_index + 2] = start + Vector3(w, map_data_ptr[(heightmap_width * d) + heightmap_width + w], d + 1);
|
||||
vertex_array_ptrw[vertex_index + 3] = start + Vector3(w + 1, map_data_ptr[(heightmap_width * d) + w + 1], d);
|
||||
vertex_array_ptrw[vertex_index + 4] = start + Vector3(w + 1, map_data_ptr[(heightmap_width * d) + heightmap_width + w + 1], d + 1);
|
||||
vertex_array_ptrw[vertex_index + 5] = start + Vector3(w, map_data_ptr[(heightmap_width * d) + heightmap_width + w], d + 1);
|
||||
vertex_index += 6;
|
||||
}
|
||||
}
|
||||
if (vertex_array.size() > 0) {
|
||||
p_source_geometry_data->add_faces(vertex_array, transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MODULE_CSG_ENABLED
|
||||
void NavMeshGenerator3D::generator_parse_csgshape3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
|
||||
CSGShape3D *csgshape3d = Object::cast_to<CSGShape3D>(p_node);
|
||||
|
||||
if (csgshape3d) {
|
||||
NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask();
|
||||
|
||||
if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS && csgshape3d->is_using_collision() && (csgshape3d->get_collision_layer() & parsed_collision_mask)) || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) {
|
||||
CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_node);
|
||||
Array meshes = csg_shape->get_meshes();
|
||||
if (!meshes.is_empty()) {
|
||||
Ref<Mesh> mesh = meshes[1];
|
||||
if (mesh.is_valid()) {
|
||||
p_source_geometry_data->add_mesh(mesh, csg_shape->get_global_transform());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // MODULE_CSG_ENABLED
|
||||
|
||||
#ifdef MODULE_GRIDMAP_ENABLED
|
||||
void NavMeshGenerator3D::generator_parse_gridmap_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
|
||||
GridMap *gridmap = Object::cast_to<GridMap>(p_node);
|
||||
|
||||
if (gridmap) {
|
||||
NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask();
|
||||
|
||||
if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) {
|
||||
Array meshes = gridmap->get_meshes();
|
||||
Transform3D xform = gridmap->get_global_transform();
|
||||
for (int i = 0; i < meshes.size(); i += 2) {
|
||||
Ref<Mesh> mesh = meshes[i + 1];
|
||||
if (mesh.is_valid()) {
|
||||
p_source_geometry_data->add_mesh(mesh, xform * (Transform3D)meshes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) && (gridmap->get_collision_layer() & parsed_collision_mask)) {
|
||||
Array shapes = gridmap->get_collision_shapes();
|
||||
for (int i = 0; i < shapes.size(); i += 2) {
|
||||
RID shape = shapes[i + 1];
|
||||
PhysicsServer3D::ShapeType type = PhysicsServer3D::get_singleton()->shape_get_type(shape);
|
||||
Variant data = PhysicsServer3D::get_singleton()->shape_get_data(shape);
|
||||
|
||||
switch (type) {
|
||||
case PhysicsServer3D::SHAPE_SPHERE: {
|
||||
real_t radius = data;
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
SphereMesh::create_mesh_array(arr, radius, radius * 2.0);
|
||||
p_source_geometry_data->add_mesh_array(arr, shapes[i]);
|
||||
} break;
|
||||
case PhysicsServer3D::SHAPE_BOX: {
|
||||
Vector3 extents = data;
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
BoxMesh::create_mesh_array(arr, extents * 2.0);
|
||||
p_source_geometry_data->add_mesh_array(arr, shapes[i]);
|
||||
} break;
|
||||
case PhysicsServer3D::SHAPE_CAPSULE: {
|
||||
Dictionary dict = data;
|
||||
real_t radius = dict["radius"];
|
||||
real_t height = dict["height"];
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
CapsuleMesh::create_mesh_array(arr, radius, height);
|
||||
p_source_geometry_data->add_mesh_array(arr, shapes[i]);
|
||||
} break;
|
||||
case PhysicsServer3D::SHAPE_CYLINDER: {
|
||||
Dictionary dict = data;
|
||||
real_t radius = dict["radius"];
|
||||
real_t height = dict["height"];
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
CylinderMesh::create_mesh_array(arr, radius, radius, height);
|
||||
p_source_geometry_data->add_mesh_array(arr, shapes[i]);
|
||||
} break;
|
||||
case PhysicsServer3D::SHAPE_CONVEX_POLYGON: {
|
||||
PackedVector3Array vertices = data;
|
||||
Geometry3D::MeshData md;
|
||||
|
||||
Error err = ConvexHullComputer::convex_hull(vertices, md);
|
||||
|
||||
if (err == OK) {
|
||||
PackedVector3Array faces;
|
||||
|
||||
for (const Geometry3D::MeshData::Face &face : md.faces) {
|
||||
for (uint32_t k = 2; k < face.indices.size(); ++k) {
|
||||
faces.push_back(md.vertices[face.indices[0]]);
|
||||
faces.push_back(md.vertices[face.indices[k - 1]]);
|
||||
faces.push_back(md.vertices[face.indices[k]]);
|
||||
}
|
||||
}
|
||||
|
||||
p_source_geometry_data->add_faces(faces, shapes[i]);
|
||||
}
|
||||
} break;
|
||||
case PhysicsServer3D::SHAPE_CONCAVE_POLYGON: {
|
||||
Dictionary dict = data;
|
||||
PackedVector3Array faces = Variant(dict["faces"]);
|
||||
p_source_geometry_data->add_faces(faces, shapes[i]);
|
||||
} break;
|
||||
case PhysicsServer3D::SHAPE_HEIGHTMAP: {
|
||||
Dictionary dict = data;
|
||||
///< dict( int:"width", int:"depth",float:"cell_size", float_array:"heights"
|
||||
int heightmap_depth = dict["depth"];
|
||||
int heightmap_width = dict["width"];
|
||||
|
||||
if (heightmap_depth >= 2 && heightmap_width >= 2) {
|
||||
const Vector<real_t> &map_data = dict["heights"];
|
||||
|
||||
Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1);
|
||||
Vector3 start = Vector3(heightmap_gridsize.x, 0, heightmap_gridsize.y) * -0.5;
|
||||
|
||||
Vector<Vector3> vertex_array;
|
||||
vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6);
|
||||
Vector3 *vertex_array_ptrw = vertex_array.ptrw();
|
||||
const real_t *map_data_ptr = map_data.ptr();
|
||||
int vertex_index = 0;
|
||||
|
||||
for (int d = 0; d < heightmap_depth - 1; d++) {
|
||||
for (int w = 0; w < heightmap_width - 1; w++) {
|
||||
vertex_array_ptrw[vertex_index] = start + Vector3(w, map_data_ptr[(heightmap_width * d) + w], d);
|
||||
vertex_array_ptrw[vertex_index + 1] = start + Vector3(w + 1, map_data_ptr[(heightmap_width * d) + w + 1], d);
|
||||
vertex_array_ptrw[vertex_index + 2] = start + Vector3(w, map_data_ptr[(heightmap_width * d) + heightmap_width + w], d + 1);
|
||||
vertex_array_ptrw[vertex_index + 3] = start + Vector3(w + 1, map_data_ptr[(heightmap_width * d) + w + 1], d);
|
||||
vertex_array_ptrw[vertex_index + 4] = start + Vector3(w + 1, map_data_ptr[(heightmap_width * d) + heightmap_width + w + 1], d + 1);
|
||||
vertex_array_ptrw[vertex_index + 5] = start + Vector3(w, map_data_ptr[(heightmap_width * d) + heightmap_width + w], d + 1);
|
||||
vertex_index += 6;
|
||||
}
|
||||
}
|
||||
if (vertex_array.size() > 0) {
|
||||
p_source_geometry_data->add_faces(vertex_array, shapes[i]);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
WARN_PRINT("Unsupported collision shape type.");
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#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::set_generator_parsers(LocalVector<NavMeshGeometryParser3D *> p_parsers) {
|
||||
RWLockWrite write_lock(generator_parsers_rwlock);
|
||||
generator_parsers = p_parsers;
|
||||
}
|
||||
|
||||
void NavMeshGenerator3D::generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node) {
|
||||
|
|
@ -665,7 +264,7 @@ void NavMeshGenerator3D::generator_parse_source_geometry_data(const Ref<Navigati
|
|||
for (Node *parse_node : parse_nodes) {
|
||||
generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, parse_node, recurse_children);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data) {
|
||||
if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) {
|
||||
|
|
@ -962,45 +561,4 @@ bool NavMeshGenerator3D::generator_emit_callback(const Callable &p_callback) {
|
|||
return ce.error == Callable::CallError::CALL_OK;
|
||||
}
|
||||
|
||||
RID NavMeshGenerator3D::source_geometry_parser_create() {
|
||||
RWLockWrite write_lock(generator_rid_rwlock);
|
||||
|
||||
RID rid = generator_parser_owner.make_rid();
|
||||
|
||||
NavMeshGeometryParser3D *parser = generator_parser_owner.get_or_null(rid);
|
||||
parser->self = rid;
|
||||
|
||||
generator_parsers.push_back(parser);
|
||||
|
||||
return rid;
|
||||
}
|
||||
|
||||
void NavMeshGenerator3D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) {
|
||||
RWLockWrite write_lock(generator_rid_rwlock);
|
||||
|
||||
NavMeshGeometryParser3D *parser = generator_parser_owner.get_or_null(p_parser);
|
||||
ERR_FAIL_NULL(parser);
|
||||
|
||||
parser->callback = p_callback;
|
||||
}
|
||||
|
||||
bool NavMeshGenerator3D::owns(RID p_object) {
|
||||
RWLockRead read_lock(generator_rid_rwlock);
|
||||
return generator_parser_owner.owns(p_object);
|
||||
}
|
||||
|
||||
void NavMeshGenerator3D::free(RID p_object) {
|
||||
RWLockWrite write_lock(generator_rid_rwlock);
|
||||
|
||||
if (generator_parser_owner.owns(p_object)) {
|
||||
NavMeshGeometryParser3D *parser = generator_parser_owner.get_or_null(p_object);
|
||||
|
||||
generator_parsers.erase(parser);
|
||||
|
||||
generator_parser_owner.free(p_object);
|
||||
} else {
|
||||
ERR_PRINT("Attempted to free a NavMeshGenerator3D RID that did not exist (or was already freed).");
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _3D_DISABLED
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
#include "core/object/class_db.h"
|
||||
#include "core/object/worker_thread_pool.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
|
||||
#include "servers/navigation_server_3d.h"
|
||||
|
||||
class Node;
|
||||
class NavigationMesh;
|
||||
|
|
@ -48,12 +48,7 @@ class NavMeshGenerator3D : public Object {
|
|||
static Mutex baking_navmesh_mutex;
|
||||
static Mutex generator_task_mutex;
|
||||
|
||||
static RWLock generator_rid_rwlock;
|
||||
struct NavMeshGeometryParser3D {
|
||||
RID self;
|
||||
Callable callback;
|
||||
};
|
||||
static RID_Owner<NavMeshGeometryParser3D> generator_parser_owner;
|
||||
static RWLock generator_parsers_rwlock;
|
||||
static LocalVector<NavMeshGeometryParser3D *> generator_parsers;
|
||||
|
||||
static bool use_threads;
|
||||
|
|
@ -86,17 +81,6 @@ class NavMeshGenerator3D : public Object {
|
|||
static void generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node);
|
||||
static void generator_bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data);
|
||||
|
||||
static void generator_parse_meshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
|
||||
static void generator_parse_multimeshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
|
||||
static void generator_parse_staticbody3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
|
||||
#ifdef MODULE_CSG_ENABLED
|
||||
static void generator_parse_csgshape3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
|
||||
#endif // MODULE_CSG_ENABLED
|
||||
#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);
|
||||
|
||||
public:
|
||||
|
|
@ -106,17 +90,13 @@ public:
|
|||
static void cleanup();
|
||||
static void finish();
|
||||
|
||||
static void set_generator_parsers(LocalVector<NavMeshGeometryParser3D *> p_parsers);
|
||||
|
||||
static void parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable());
|
||||
static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
|
||||
static void bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
|
||||
static bool is_baking(Ref<NavigationMesh> p_navigation_mesh);
|
||||
|
||||
static RID source_geometry_parser_create();
|
||||
static void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback);
|
||||
|
||||
static bool owns(RID p_object);
|
||||
static void free(RID p_object);
|
||||
|
||||
NavMeshGenerator3D();
|
||||
~NavMeshGenerator3D();
|
||||
};
|
||||
|
|
|
|||
1130
engine/modules/navigation/3d/nav_mesh_queries_3d.cpp
Normal file
1130
engine/modules/navigation/3d/nav_mesh_queries_3d.cpp
Normal file
File diff suppressed because it is too large
Load diff
148
engine/modules/navigation/3d/nav_mesh_queries_3d.h
Normal file
148
engine/modules/navigation/3d/nav_mesh_queries_3d.h
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/**************************************************************************/
|
||||
/* nav_mesh_queries_3d.h */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef NAV_MESH_QUERIES_3D_H
|
||||
#define NAV_MESH_QUERIES_3D_H
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
|
||||
#include "../nav_utils.h"
|
||||
|
||||
#include "servers/navigation/navigation_path_query_parameters_3d.h"
|
||||
#include "servers/navigation/navigation_path_query_result_3d.h"
|
||||
#include "servers/navigation/navigation_utilities.h"
|
||||
|
||||
using namespace NavigationUtilities;
|
||||
|
||||
class NavMap;
|
||||
struct NavMapIteration;
|
||||
|
||||
class NavMeshQueries3D {
|
||||
public:
|
||||
struct PathQuerySlot {
|
||||
LocalVector<gd::NavigationPoly> path_corridor;
|
||||
gd::Heap<gd::NavigationPoly *, gd::NavPolyTravelCostGreaterThan, gd::NavPolyHeapIndexer> traversable_polys;
|
||||
bool in_use = false;
|
||||
uint32_t slot_index = 0;
|
||||
};
|
||||
|
||||
struct NavMeshPathQueryTask3D {
|
||||
enum TaskStatus {
|
||||
QUERY_STARTED,
|
||||
QUERY_FINISHED,
|
||||
QUERY_FAILED,
|
||||
CALLBACK_DISPATCHED,
|
||||
CALLBACK_FAILED,
|
||||
};
|
||||
|
||||
// Parameters.
|
||||
Vector3 start_position;
|
||||
Vector3 target_position;
|
||||
uint32_t navigation_layers;
|
||||
BitField<PathMetadataFlags> metadata_flags = PathMetadataFlags::PATH_INCLUDE_ALL;
|
||||
PathfindingAlgorithm pathfinding_algorithm = PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR;
|
||||
PathPostProcessing path_postprocessing = PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL;
|
||||
bool simplify_path = false;
|
||||
real_t simplify_epsilon = 0.0;
|
||||
|
||||
// Path building.
|
||||
Vector3 begin_position;
|
||||
Vector3 end_position;
|
||||
const gd::Polygon *begin_polygon = nullptr;
|
||||
const gd::Polygon *end_polygon = nullptr;
|
||||
uint32_t least_cost_id = 0;
|
||||
|
||||
// Map.
|
||||
Vector3 map_up;
|
||||
NavMap *map = nullptr;
|
||||
PathQuerySlot *path_query_slot = nullptr;
|
||||
|
||||
// Path points.
|
||||
LocalVector<Vector3> path_points;
|
||||
LocalVector<int32_t> path_meta_point_types;
|
||||
LocalVector<RID> path_meta_point_rids;
|
||||
LocalVector<int64_t> path_meta_point_owners;
|
||||
|
||||
Ref<NavigationPathQueryParameters3D> query_parameters;
|
||||
Ref<NavigationPathQueryResult3D> query_result;
|
||||
Callable callback;
|
||||
NavMeshPathQueryTask3D::TaskStatus status = NavMeshPathQueryTask3D::TaskStatus::QUERY_STARTED;
|
||||
|
||||
void path_clear() {
|
||||
path_points.clear();
|
||||
path_meta_point_types.clear();
|
||||
path_meta_point_rids.clear();
|
||||
path_meta_point_owners.clear();
|
||||
}
|
||||
|
||||
void path_reverse() {
|
||||
path_points.invert();
|
||||
path_meta_point_types.invert();
|
||||
path_meta_point_rids.invert();
|
||||
path_meta_point_owners.invert();
|
||||
}
|
||||
};
|
||||
|
||||
static bool emit_callback(const Callable &p_callback);
|
||||
|
||||
static Vector3 polygons_get_random_point(const LocalVector<gd::Polygon> &p_polygons, uint32_t p_navigation_layers, bool p_uniformly);
|
||||
|
||||
static Vector3 polygons_get_closest_point_to_segment(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision);
|
||||
static Vector3 polygons_get_closest_point(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point);
|
||||
static Vector3 polygons_get_closest_point_normal(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point);
|
||||
static gd::ClosestPointQueryResult polygons_get_closest_point_info(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point);
|
||||
static RID polygons_get_closest_point_owner(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point);
|
||||
|
||||
static Vector3 map_iteration_get_closest_point_to_segment(const NavMapIteration &p_map_iteration, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision);
|
||||
static Vector3 map_iteration_get_closest_point(const NavMapIteration &p_map_iteration, const Vector3 &p_point);
|
||||
static Vector3 map_iteration_get_closest_point_normal(const NavMapIteration &p_map_iteration, const Vector3 &p_point);
|
||||
static RID map_iteration_get_closest_point_owner(const NavMapIteration &p_map_iteration, const Vector3 &p_point);
|
||||
static gd::ClosestPointQueryResult map_iteration_get_closest_point_info(const NavMapIteration &p_map_iteration, const Vector3 &p_point);
|
||||
static Vector3 map_iteration_get_random_point(const NavMapIteration &p_map_iteration, uint32_t p_navigation_layers, bool p_uniformly);
|
||||
|
||||
static void map_query_path(NavMap *map, const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result, const Callable &p_callback);
|
||||
|
||||
static void query_task_map_iteration_get_path(NavMeshPathQueryTask3D &p_query_task, const NavMapIteration &p_map_iteration);
|
||||
static void _query_task_push_back_point_with_metadata(NavMeshPathQueryTask3D &p_query_task, const Vector3 &p_point, const gd::Polygon *p_point_polygon);
|
||||
static void _query_task_find_start_end_positions(NavMeshPathQueryTask3D &p_query_task, const NavMapIteration &p_map_iteration);
|
||||
static void _query_task_build_path_corridor(NavMeshPathQueryTask3D &p_query_task);
|
||||
static void _query_task_post_process_corridorfunnel(NavMeshPathQueryTask3D &p_query_task);
|
||||
static void _query_task_post_process_edgecentered(NavMeshPathQueryTask3D &p_query_task);
|
||||
static void _query_task_post_process_nopostprocessing(NavMeshPathQueryTask3D &p_query_task);
|
||||
static void _query_task_clip_path(NavMeshPathQueryTask3D &p_query_task, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly);
|
||||
static void _query_task_simplified_path_points(NavMeshPathQueryTask3D &p_query_task);
|
||||
|
||||
static void simplify_path_segment(int p_start_inx, int p_end_inx, const LocalVector<Vector3> &p_points, real_t p_epsilon, LocalVector<uint32_t> &r_simplified_path_indices);
|
||||
static LocalVector<uint32_t> get_simplified_path_indices(const LocalVector<Vector3> &p_path, real_t p_epsilon);
|
||||
};
|
||||
|
||||
#endif // _3D_DISABLED
|
||||
|
||||
#endif // NAV_MESH_QUERIES_3D_H
|
||||
51
engine/modules/navigation/3d/nav_region_iteration_3d.h
Normal file
51
engine/modules/navigation/3d/nav_region_iteration_3d.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/**************************************************************************/
|
||||
/* nav_region_iteration_3d.h */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef NAV_REGION_ITERATION_3D_H
|
||||
#define NAV_REGION_ITERATION_3D_H
|
||||
|
||||
#include "../nav_utils.h"
|
||||
#include "nav_base_iteration_3d.h"
|
||||
|
||||
#include "core/math/aabb.h"
|
||||
|
||||
struct NavRegionIteration : NavBaseIteration {
|
||||
Transform3D transform;
|
||||
LocalVector<gd::Polygon> navmesh_polygons;
|
||||
real_t surface_area = 0.0;
|
||||
AABB bounds;
|
||||
|
||||
const Transform3D &get_transform() const { return transform; }
|
||||
const LocalVector<gd::Polygon> &get_navmesh_polygons() const { return navmesh_polygons; }
|
||||
real_t get_surface_area() const { return surface_area; }
|
||||
AABB get_bounds() const { return bounds; }
|
||||
};
|
||||
|
||||
#endif // NAV_REGION_ITERATION_3D_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue