Files
academy2/modules/detour/navmesh_query.cpp
2021-07-31 03:37:28 +03:00

379 lines
12 KiB
C++

#include "navmesh_query.h"
#include "detour.h"
#include <DetourNavMeshQuery.h>
DetourNavigationQueryFilter::DetourNavigationQueryFilter() :
Reference() {
query_filter = memnew(dtQueryFilter());
}
DetourNavigationQueryFilter::~DetourNavigationQueryFilter() {
memdelete(query_filter);
}
void DetourNavigationQueryFilter::set_area_cost(int area_id, float cost) {
if (query_filter)
query_filter->setAreaCost(area_id, cost);
}
float DetourNavigationQueryFilter::get_area_cost(int area_id) {
if (query_filter)
return query_filter->getAreaCost(area_id);
return 1.0f;
}
void DetourNavigationQueryFilter::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_area_cost", "area_id", "cost"),
&DetourNavigationQueryFilter::set_area_cost);
ClassDB::bind_method(D_METHOD("get_area_cost", "area_id"),
&DetourNavigationQueryFilter::get_area_cost);
}
class DetourNavigationQuery::QueryData {
public:
Vector3 path_points[MAX_POLYS];
unsigned char path_flags[MAX_POLYS];
dtPolyRef polys[MAX_POLYS];
dtPolyRef path_polys[MAX_POLYS];
};
DetourNavigationQuery::DetourNavigationQuery() :
Object(),
query_data(memnew(QueryData)) {}
DetourNavigationQuery::~DetourNavigationQuery() {}
void DetourNavigationQuery::init(Ref<DetourNavigationMesh> mesh,
const Transform &xform) {
navmesh_query = dtAllocNavMeshQuery();
if (!navmesh_query) {
ERR_PRINT("failed to create navigation query");
return;
}
if (dtStatusFailed(navmesh_query->init(mesh->get_navmesh(), MAX_POLYS))) {
ERR_PRINT("failed to initialize navigation query");
return;
}
transform = xform;
inverse = xform.inverse();
}
float DetourNavigationQuery::random() {
return (float)Math::randf();
}
Vector3 DetourNavigationQuery::nearest_point_(const Vector3 &point,
const Vector3 &extents,
const dtQueryFilter *filter,
uint64_t *ppref) {
if (!navmesh_query)
return point;
Vector3 nearest_point;
dtPolyRef dtppref = ppref ? (dtPolyRef)(*ppref) : 0;
navmesh_query->findNearestPoly(&point.coord[0], &extents.coord[0], filter,
&dtppref, &nearest_point.coord[0]);
*ppref = dtppref;
if (*ppref)
return nearest_point;
else
return point;
}
Vector3
DetourNavigationQuery::nearest_point(const Vector3 &point,
const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter) {
if (!navmesh_query)
return point;
Vector3 local_point = inverse.xform(point);
Vector3 nearest_point;
polyref_t pref = 0;
nearest_point = nearest_point_(local_point, extents, filter, &pref);
// what is dtQueryFilter and how to work with it?
if (pref)
return transform.xform(nearest_point);
else
return point;
}
Vector3
DetourNavigationQuery::random_point_(polyref_t *pref,
Ref<DetourNavigationQueryFilter> filter) {
if (!navmesh_query)
return Vector3();
Vector3 point;
dtPolyRef dtpref = pref ? *pref : 0;
navmesh_query->findRandomPoint(filter->get(), random, &dtpref,
&point.coord[0]);
*pref = dtpref;
return point;
}
Vector3
DetourNavigationQuery::random_point(Ref<DetourNavigationQueryFilter> filter) {
polyref_t pref;
Vector3 point = random_point_(&pref, filter);
return transform.xform(point);
}
Vector3 DetourNavigationQuery::random_point_in_circle_(
const Vector3 &center, float radius, const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter, polyref_t *ppref) {
if (!navmesh_query)
return center;
dtPolyRef pref;
navmesh_query->findNearestPoly(&center.coord[0], &extents.coord[0],
filter->get(), &pref, NULL);
if (!pref)
return center;
Vector3 point = center;
dtPolyRef dtppref = ppref ? *ppref : 0;
navmesh_query->findRandomPointAroundCircle(pref, &center.coord[0], radius,
filter->get(), random, &dtppref,
&point.coord[0]);
*ppref = dtppref;
return point;
}
Vector3 DetourNavigationQuery::random_point_in_circle(
const Vector3 &center, float radius, const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter) {
Vector3 local_center = inverse.xform(center);
polyref_t pref2;
Vector3 point =
random_point_in_circle_(local_center, radius, extents, filter, &pref2);
return transform.xform(point);
}
float DetourNavigationQuery::distance_to_wall_(
const Vector3 &point, float radius, const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter, Vector3 *hit_pos,
Vector3 *hit_normal) {
if (hit_pos)
*hit_pos = Vector3();
if (hit_normal)
*hit_normal = Vector3(0.0, -1.0, 0.0);
float distance = radius;
dtPolyRef pref;
if (!navmesh_query)
return distance;
navmesh_query->findNearestPoly(&point.coord[0], &extents.coord[0],
filter->get(), &pref, NULL);
if (!pref)
return distance;
navmesh_query->findDistanceToWall(pref, &point.coord[0], radius,
filter->get(), &distance,
reinterpret_cast<float *>(hit_pos),
reinterpret_cast<float *>(hit_normal));
return distance;
}
Dictionary DetourNavigationQuery::distance_to_wall_detailed(
const Vector3 &point, float radius, const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter) {
Dictionary ret;
Vector3 hit_pos = Vector3(), hit_normal = Vector3(0.0, -1.0, 0.0);
ret["position"] = hit_pos;
ret["normal"] = hit_normal;
ret["distance"] = radius;
Vector3 local_point = inverse.xform(point);
float dist = distance_to_wall_(local_point, radius, extents, filter, &hit_pos,
&hit_normal);
ret["position"] = hit_pos;
ret["normal"] = hit_normal;
ret["distance"] = dist;
return ret;
}
float DetourNavigationQuery::distance_to_wall(
const Vector3 &point, float radius, const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter) {
Vector3 local_point = inverse.xform(point);
return distance_to_wall_(local_point, radius, extents, filter);
}
Vector3 DetourNavigationQuery::raycast_(const Vector3 &start,
const Vector3 &end,
const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter,
Vector3 *hit_normal) {
dtPolyRef pref;
float r;
if (hit_normal)
*hit_normal = Vector3();
navmesh_query->findNearestPoly(&start.coord[0], &extents.coord[0],
filter->get(), &pref, NULL);
if (!pref)
return end;
int poly_count = 0;
navmesh_query->raycast(pref, &start.coord[0], &end.coord[0], filter->get(),
&r, reinterpret_cast<float *>(hit_normal),
query_data->polys, &poly_count, MAX_POLYS);
if (r > 1.0f)
r = 1.0f;
return start.linear_interpolate(end, r);
}
Vector<Vector3>
DetourNavigationQuery::raycast(const Vector3 &start, const Vector3 &end,
const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter) {
Vector<Vector3> ret;
Vector3 normal(0.0, -1.0, 0.0);
if (!navmesh_query) {
ret.push_back(end);
ret.push_back(normal);
return ret;
}
Vector3 local_start = inverse.xform(start);
Vector3 local_end = inverse.xform(end);
Vector3 result = raycast_(local_start, local_end, extents, filter, &normal);
ret.push_back(transform.xform(result));
ret.push_back(normal);
return ret;
}
Vector3 DetourNavigationQuery::move_along_surface_(
const Vector3 &start, const Vector3 &end, const Vector3 &extents,
int max_visited, Ref<DetourNavigationQueryFilter> filter) {
dtPolyRef pstart;
navmesh_query->findNearestPoly(&start.coord[0], &extents.coord[0],
filter->get(), &pstart, NULL);
if (!pstart)
return end;
Vector3 result;
int visited = 0;
Vector<dtPolyRef> visited_ref;
visited_ref.resize(max_visited);
navmesh_query->moveAlongSurface(
pstart, &start.coord[0], &end.coord[0], filter->get(), &result.coord[0],
max_visited > 0 ? &visited_ref.write[0] : NULL, &visited, max_visited);
return result;
}
Vector3 DetourNavigationQuery::move_along_surface(
const Vector3 &start, const Vector3 &end, const Vector3 &extents,
int max_visited, Ref<DetourNavigationQueryFilter> filter) {
if (!navmesh_query)
return end;
#if 0
/* TODO: are these necessary? */
Vector3 local_start = inverse.xform(start);
Vector3 local_end = inverse.xform(end);
#endif
Vector3 result =
move_along_surface_(start, end, extents, max_visited, filter);
return transform.xform(result);
}
Dictionary
DetourNavigationQuery::find_path_(const Vector3 &start, const Vector3 &end,
const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter) {
Vector<Vector3> points;
Vector<int> flags;
Dictionary ret;
if (!navmesh_query)
return ret;
dtPolyRef pstart;
dtPolyRef pend;
navmesh_query->findNearestPoly(&start.coord[0], &extents.coord[0],
filter->get(), &pstart, NULL);
navmesh_query->findNearestPoly(&end.coord[0], &extents.coord[0],
filter->get(), &pend, NULL);
if (!pstart || !pend)
return ret;
int num_polys = 0;
int num_path_points = 0;
navmesh_query->findPath(pstart, pend, &start.coord[0], &end.coord[0],
filter->get(), query_data->polys, &num_polys,
MAX_POLYS);
if (!num_polys)
return ret;
Vector3 actual_end = end;
if (query_data->polys[num_polys - 1] != pend) {
Vector3 tmp;
navmesh_query->closestPointOnPoly(query_data->polys[num_polys - 1],
&end.coord[0], &tmp.coord[0], NULL);
actual_end = tmp;
}
navmesh_query->findStraightPath(
&start.coord[0], &actual_end.coord[0], query_data->polys, num_polys,
&query_data->path_points[0].coord[0], &query_data->path_flags[0],
query_data->path_polys, &num_path_points, MAX_POLYS);
for (int i = 0; i < num_path_points; i++) {
points.push_back(query_data->path_points[i]);
flags.push_back(query_data->path_flags[i]);
}
ret["points"] = points;
ret["flags"] = flags;
return ret;
}
Dictionary
DetourNavigationQuery::find_path(const Vector3 &start, const Vector3 &end,
const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter) {
Vector3 local_start = inverse.xform(start);
Vector3 local_end = inverse.xform(end);
Dictionary result = find_path_(local_start, local_end, extents, filter);
Vector<Vector3> points = result["points"];
for (int i = 0; i < points.size(); i++)
points.write[i] = transform.xform(points[i]);
result["points"] = points;
return result;
}
void DetourNavigationQuery::find_path_array(const Vector3 &start, const Vector3 &end,
const Vector3 &extents,
Ref<DetourNavigationQueryFilter> filter,
Vector<Vector3> &points,
Vector<int> &flags) {
Dictionary ret;
if (!navmesh_query)
return;
dtPolyRef pstart;
dtPolyRef pend;
navmesh_query->findNearestPoly(&start.coord[0], &extents.coord[0],
filter->get(), &pstart, NULL);
navmesh_query->findNearestPoly(&end.coord[0], &extents.coord[0],
filter->get(), &pend, NULL);
if (!pstart || !pend)
return;
int num_polys = 0;
int num_path_points = 0;
navmesh_query->findPath(pstart, pend, &start.coord[0], &end.coord[0],
filter->get(), query_data->polys, &num_polys,
MAX_POLYS);
if (!num_polys)
return;
Vector3 actual_end = end;
if (query_data->polys[num_polys - 1] != pend) {
Vector3 tmp;
navmesh_query->closestPointOnPoly(query_data->polys[num_polys - 1],
&end.coord[0], &tmp.coord[0], NULL);
actual_end = tmp;
}
navmesh_query->findStraightPath(
&start.coord[0], &actual_end.coord[0], query_data->polys, num_polys,
&query_data->path_points[0].coord[0], &query_data->path_flags[0],
query_data->path_polys, &num_path_points, MAX_POLYS);
points.resize(num_path_points);
flags.resize(num_path_points);
for (int i = 0; i < num_path_points; i++) {
points.write[i] = transform.xform(query_data->path_points[i]);
flags.write[i] = query_data->path_flags[i];
}
}
void DetourNavigationQuery::_bind_methods() {
ClassDB::bind_method(D_METHOD("init", "navmesh", "xform"),
&DetourNavigationQuery::init);
ClassDB::bind_method(D_METHOD("nearest_point", "point", "extents", "filter"),
&DetourNavigationQuery::nearest_point);
ClassDB::bind_method(D_METHOD("random_point", "filter"),
&DetourNavigationQuery::random_point);
ClassDB::bind_method(D_METHOD("random_point_in_circle", "center", "radius",
"extents", "filter"),
&DetourNavigationQuery::random_point_in_circle);
ClassDB::bind_method(
D_METHOD("distance_to_wall", "point", "radius", "extents", "filter"),
&DetourNavigationQuery::distance_to_wall);
ClassDB::bind_method(D_METHOD("distance_to_wall_detailed", "point", "radius",
"extents", "filter"),
&DetourNavigationQuery::distance_to_wall_detailed);
ClassDB::bind_method(D_METHOD("raycast", "start", "end", "extents", "filter"),
&DetourNavigationQuery::raycast);
ClassDB::bind_method(D_METHOD("move_along_surface", "start", "end", "extents",
"max_visited", "filter"),
&DetourNavigationQuery::move_along_surface);
ClassDB::bind_method(
D_METHOD("find_path", "start", "end", "extents", "filter"),
&DetourNavigationQuery::find_path);
}