#include "navmesh_query.h" #include "detour.h" #include 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 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 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 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 filter) { polyref_t pref; Vector3 point = random_point_(&pref, filter); return transform.xform(point); } Vector3 DetourNavigationQuery::random_point_in_circle_( const Vector3 ¢er, float radius, const Vector3 &extents, Ref filter, polyref_t *ppref) { if (!navmesh_query) return center; dtPolyRef pref; navmesh_query->findNearestPoly(¢er.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, ¢er.coord[0], radius, filter->get(), random, &dtppref, &point.coord[0]); *ppref = dtppref; return point; } Vector3 DetourNavigationQuery::random_point_in_circle( const Vector3 ¢er, float radius, const Vector3 &extents, Ref 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 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(hit_pos), reinterpret_cast(hit_normal)); return distance; } Dictionary DetourNavigationQuery::distance_to_wall_detailed( const Vector3 &point, float radius, const Vector3 &extents, Ref 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 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 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(hit_normal), query_data->polys, &poly_count, MAX_POLYS); if (r > 1.0f) r = 1.0f; return start.linear_interpolate(end, r); } Vector DetourNavigationQuery::raycast(const Vector3 &start, const Vector3 &end, const Vector3 &extents, Ref filter) { Vector 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 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 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 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 filter) { Vector points; Vector 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 filter) { Vector3 local_start = inverse.xform(start); Vector3 local_end = inverse.xform(end); Dictionary result = find_path_(local_start, local_end, extents, filter); Vector 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 filter, Vector &points, Vector &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); }