// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Platform/CriticalSection.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "NavMeshData.h"
#include "NavigationTypes.h"
class dtNavMesh;
class dtNavMeshQuery;
class NavMesh;
///
/// The navigation mesh tile data.
///
class FLAXENGINE_API NavMeshTile
{
public:
int32 X;
int32 Y;
int32 Layer;
NavMesh* NavMesh;
BytesContainer Data;
};
///
/// The navigation mesh path flags.
///
enum class NavMeshPathFlags
{
// Nothing.
None = 0,
// Path is only partially generated, goal is unreachable so path represents the best guess.
PartialPath = 1,
};
DECLARE_ENUM_OPERATORS(NavMeshPathFlags);
///
/// The navigation mesh runtime object that builds the navmesh from all loaded scenes.
///
API_CLASS(NoSpawn) class FLAXENGINE_API NavMeshRuntime : public ScriptingObject
{
DECLARE_SCRIPTING_TYPE_MINIMAL(NavMeshRuntime);
public:
// Gets the first valid navigation mesh runtime. Return null if none created.
API_FUNCTION() static NavMeshRuntime* Get();
// Gets the navigation mesh runtime for a given navmesh name. Return null if missing.
API_FUNCTION() static NavMeshRuntime* Get(const StringView& navMeshName);
// Gets the navigation mesh runtime for a given agent properties trying to pick the best matching navmesh.
API_FUNCTION() static NavMeshRuntime* Get(API_PARAM(Ref) const NavAgentProperties& agentProperties);
// Gets the navigation mesh runtime for a given navmesh properties.
API_FUNCTION() static NavMeshRuntime* Get(API_PARAM(Ref) const NavMeshProperties& navMeshProperties, bool createIfMissing = false);
// The lookup table that maps areaId of the navmesh to the current properties (applied by the NavigationSettings). Cached to improve runtime performance.
static float NavAreasCosts[64];
#if COMPILE_WITH_DEBUG_DRAW
static Color NavAreasColors[64];
#endif
private:
dtNavMesh* _navMesh;
dtNavMeshQuery* _navMeshQuery;
float _tileSize;
Array _tiles;
public:
NavMeshRuntime(const NavMeshProperties& properties);
~NavMeshRuntime();
public:
///
/// The object locker.
///
CriticalSection Locker;
///
/// The navigation mesh properties.
///
API_FIELD(ReadOnly) NavMeshProperties Properties;
///
/// Gets the size of the tile (in world-units). Returns zero if not initialized yet.
///
API_PROPERTY() FORCE_INLINE float GetTileSize() const
{
return _tileSize;
}
dtNavMesh* GetNavMesh() const
{
return _navMesh;
}
dtNavMeshQuery* GetNavMeshQuery() const
{
return _navMeshQuery;
}
int32 GetTilesCapacity() const;
public:
///
/// Finds the distance from the specified start position to the nearest polygon wall.
///
/// The start position.
/// The result hit information. Valid only when query succeed.
/// The maximum distance to search for wall (search radius).
/// True if ray hits an matching object, otherwise false.
API_FUNCTION() bool FindDistanceToWall(const Vector3& startPosition, NavMeshHit& hitInfo, float maxDistance = MAX_float) const;
///
/// Finds the path between the two positions presented as a list of waypoints stored in the corners array.
///
/// The start position.
/// The end position.
/// The result path.
/// True if found valid path between given two points (it may be partial), otherwise false if failed.
API_FUNCTION() bool FindPath(const Vector3& startPosition, const Vector3& endPosition, API_PARAM(Out) Array& resultPath) const
{
NavMeshPathFlags flags;
return FindPath(startPosition, endPosition, resultPath, flags);
}
///
/// Finds the path between the two positions presented as a list of waypoints stored in the corners array.
///
/// The start position.
/// The end position.
/// The result path.
/// The result path flags.
/// True if found valid path between given two points (it may be partial), otherwise false if failed.
bool FindPath(const Vector3& startPosition, const Vector3& endPosition, API_PARAM(Out) Array& resultPath, NavMeshPathFlags& resultFlags) const;
///
/// Tests the path between the two positions (non-partial).
///
/// The start position.
/// The end position.
/// True if found valid path between given two points, otherwise false if failed.
API_FUNCTION() bool TestPath(const Vector3& startPosition, const Vector3& endPosition) const;
///
/// Projects the point to nav mesh surface (finds the nearest polygon).
///
/// The source point.
/// The result position on the navmesh (valid only if method returns true).
/// True if found valid location on the navmesh, otherwise false.
API_FUNCTION() bool ProjectPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const;
///
/// Finds random location on nav mesh.
///
/// The result position on the navmesh (valid only if method returns true).
/// True if found valid location on the navmesh, otherwise false.
API_FUNCTION() bool FindRandomPoint(API_PARAM(Out) Vector3& result) const;
///
/// Finds random location on nav mesh within the reach of specified location.
///
/// The source point to find random location around it.
/// The search distance for a random point. Maximum distance for a result point from the center of the circle.
/// The result position on the navmesh (valid only if method returns true).
/// True if found valid location on the navmesh, otherwise false.
API_FUNCTION() bool FindRandomPointAroundCircle(const Vector3& center, float radius, API_PARAM(Out) Vector3& result) const;
///
/// Casts a 'walkability' ray along the surface of the navigation mesh from the start position toward the end position.
///
/// The start position.
/// The end position.
/// The result hit information. Valid only when query succeed.
/// True if ray hits an matching object, otherwise false.
API_FUNCTION() bool RayCast(const Vector3& startPosition, const Vector3& endPosition, API_PARAM(Out) NavMeshHit& hitInfo) const;
public:
///
/// Sets the size of the tile (if not assigned). Disposes the mesh if added tiles have different size.
///
/// The size of the tile.
void SetTileSize(float tileSize);
///
/// Ensures the navmesh capacity for adding new tiles. Performs resizing if needed.
///
/// The new tiles amount.
void EnsureCapacity(int32 tilesToAddCount);
///
/// Adds the tiles from the given scene to the runtime navmesh.
///
/// The navigation mesh.
void AddTiles(NavMesh* navMesh);
///
/// Adds the tile from the given scene to the runtime navmesh.
///
/// The navigation mesh.
/// The tile data.
void AddTile(NavMesh* navMesh, NavMeshTileData& tileData);
///
/// Removes all the tiles from the navmesh that has been added from the given navigation scene.
///
/// The navigation mesh.
void RemoveTiles(NavMesh* navMesh);
///
/// Removes the tile from the navmesh.
///
/// The tile X coordinate.
/// The tile Y coordinate.
/// The tile layer.
void RemoveTile(int32 x, int32 y, int32 layer);
///
/// Removes all the tiles that custom prediction callback marks.
///
/// The prediction callback, returns true for tiles to remove and false for tiles to preserve.
/// The user data passed to the callback method.
void RemoveTiles(bool (*prediction)(const NavMeshRuntime* navMesh, const NavMeshTile& tile, void* customData), void* userData);
#if COMPILE_WITH_DEBUG_DRAW
void DebugDraw();
#endif
///
/// Releases the navmesh.
///
void Dispose();
private:
void AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData);
};