Files
UnrealEngine/Engine/Source/Runtime/Navmesh/Public/Detour/DetourNavLinkBuilder.h
2025-05-18 13:04:45 +08:00

201 lines
5.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreTypes.h"
#include "DetourAlloc.h"
#include "Detour/DetourLargeWorldCoordinates.h"
#include "Detour/DetourNavLinkBuilderConfig.h"
//@UE BEGIN
enum dtNavLinkAction
{
DT_LINK_ACTION_UNSET,
DT_LINK_ACTION_JUMP_DOWN,
DT_LINK_ACTION_JUMP_OVER,
};
struct dtLinkBuilderConfig
{
dtNavLinkBuilderJumpDownConfig jumpDownConfig;
dtNavLinkBuilderJumpOverConfig jumpOverConfig;
dtReal agentRadius = 0;
dtReal agentHeight = 0;
dtReal agentClimb = 0;
dtReal cellSize = 0;
dtReal cellHeight = 0;
};
struct rcHeightfield;
struct rcCompactHeightfield;
struct dtLinkBuilderData
{
bool generatingLinks = false;
};
class rcContext;
struct rcConfig;
class dtNavLinkBuilder
{
dtLinkBuilderConfig m_linkBuilderConfig;
dtReal m_cs = 0;
dtReal m_csSquared = 0;
dtReal m_ch = 0;
dtReal m_invCs = 0;
const rcHeightfield* m_solid = nullptr;
const rcCompactHeightfield* m_chf = nullptr;
struct Edge
{
dtReal sp[3], sq[3];
};
TArray<Edge, TInlineAllocator<32>> m_edges;
public:
static constexpr int MAX_SPINE = 8;
int getEdgeCount() const { return m_edges.Num(); }
struct TrajectorySample
{
// Min and max heights relative to the height on the segment between the start and the end point (at the sampling locations).
float ymin = 0.f;
float ymax = 0.f;
bool floorStart = false;
bool floorEnd = false;
};
struct Trajectory2D
{
float spine[2*MAX_SPINE]; // [x,y] relative points representing the desired trajectory (one spine with MAX_SPINE points)
float radiusOverflow = 0.f; // extra distance at the start and end of the spine to lookup for samples
TArray<TrajectorySample, TInlineAllocator<8>> samples; // samples along trajectory slices to check for collision
unsigned char nspine = 0; // @todo: remove (use direclty MAX_SPINE) or make relative to trajectory type and config
};
private:
enum GroundSampleFlag : unsigned char
{
UNSET = 0,
HAS_GROUND = 1,
UNRESTRICTED = 4,
};
struct GroundSample
{
dtReal height;
GroundSampleFlag flags;
};
struct PotentialSeg
{
unsigned char mark;
int idx;
float umin, umax;
float dmin, dmax;
float sp[3], sq[3];
};
struct GroundSegment
{
GroundSegment() : ngsamples(0) {}
NAVMESH_API ~GroundSegment();
dtReal p[3], q[3];
TArray<GroundSample, TInlineAllocator<16>> gsamples;
unsigned short ngsamples;
unsigned short npass;
};
public:
struct EdgeSampler
{
Trajectory2D trajectory;
GroundSegment start;
GroundSegment end;
dtReal rigp[3], rigq[3]; // edge
dtReal ax[3], ay[3], az[3]; // axis along edge
float groundRange;
dtNavLinkAction action = DT_LINK_ACTION_UNSET;
};
enum JumpLinkFlag : unsigned char
{
FILTERED = 0,
VALID = 1,
};
struct JumpLink
{
dtReal spine0[MAX_SPINE*3];
dtReal spine1[MAX_SPINE*3];
int nspine = 0;
JumpLinkFlag flags = VALID;
dtNavLinkAction action = DT_LINK_ACTION_UNSET;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
short debugSourceEdge = -1;
#endif
};
TArray<JumpLink, TInlineAllocator<16>> m_links;
private:
int m_debugSelectedEdge = -1;
friend NAVMESH_API void duDebugDrawNavLinkBuilder(struct duDebugDraw* dd, const dtNavLinkBuilder& linkBuilder, unsigned int drawFlags, const EdgeSampler* es);
friend NAVMESH_API void duDebugDrawTrajectorySamples(struct duDebugDraw* dd, const dtNavLinkBuilder& linkBuilder, const dtReal* pa, const dtReal* pb,
const dtNavLinkBuilder::Trajectory2D* trajectory, const dtReal* trajectoryDir);
public:
// Loops through contours to store edge points in world coordinates.
NAVMESH_API bool findEdges(rcContext& ctx, const rcConfig& cfg, const dtLinkBuilderConfig& builderConfig,
const struct dtTileCacheContourSet& lcset, const dtReal* orig,
const rcHeightfield* solidHF, const rcCompactHeightfield* compactHF);
// For all edges, sample edges (sampleEdge) and add links to m_links
NAVMESH_API void buildForAllEdges(rcContext& ctx, const dtLinkBuilderConfig& acfg, dtNavLinkAction action);
NAVMESH_API void debugBuildEdge(const dtLinkBuilderConfig& acfg, dtNavLinkAction action, int edgeIndex, EdgeSampler& sampler);
private:
void initTrajectorySamples(const float groundRange, Trajectory2D* trajectory) const;
bool isTrajectoryClear(const dtReal* pa, const dtReal* pb, const Trajectory2D* trajectory, const dtReal* trajectoryDir) const;
int findPotentialJumpOverEdges(const dtReal* sp, const dtReal* sq,
const float depthRange, const float heightRange,
dtReal* outSegs, const int maxOutSegs) const;
void initJumpDownRig(EdgeSampler* es, const dtReal* sp, const dtReal* sq,
const dtNavLinkBuilderJumpDownConfig& config) const;
static void initJumpOverRig(EdgeSampler* es, const dtReal* sp, const dtReal* sq,
const float jumpStartDist, const float jumpEndDist,
const float jumpHeight, const float groundRange);
bool getCompactHeightfieldHeight(const dtReal* pt, const dtReal hrange, dtReal* height) const;
bool checkHeightfieldCollision(const dtReal x, const dtReal ymin, const dtReal ymax, const dtReal z) const;
void sampleGroundSegment(GroundSegment* seg, const int nsamples, const float groundRange) const;
// Update the min height of the trajectory samples from the height of the ground at the start and the end of the trajectories.
// This method must be called after the segments have been sampled for ground.
void updateTrajectorySamples(EdgeSampler* es) const;
void sampleAction(EdgeSampler* es) const;
void filterOverlappingLinks(const float edgeDistanceThreshold);
bool sampleEdge(const dtLinkBuilderConfig& builderConfig, dtNavLinkAction desiredAction, const dtReal* sp, const dtReal* sq, dtNavLinkBuilder::EdgeSampler* sampler) const;
void addEdgeLinks(const dtLinkBuilderConfig& builderConfig, const EdgeSampler* es, const int edgeIndex);
};
//@UE END