mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Recast: implement traverse portal alignment
Shift portal mins and maxs together to try and align the portals. The system is dynamic and will take portal span into consideration. Also slightly changed the API to implement an optimization by switching the directional vector of the edges to their normals as we only need the normals in traverseLinkInLOS, this avoids having to recalculate them.
This commit is contained in:
parent
8d5bc23cf6
commit
bb140317c7
@ -295,6 +295,7 @@ void Editor::resetCommonSettings()
|
||||
// lower slope to clip into geometry unless this is being set very high (>40.0).
|
||||
m_traverseRayExtraOffset = 20.0f;
|
||||
m_traverseEdgeMinOverlap = RD_EPS;
|
||||
m_traversePortalMaxAlign = 0.5f;
|
||||
|
||||
m_regionMinSize = 4;
|
||||
m_regionMergeSize = 20;
|
||||
@ -458,6 +459,8 @@ void Editor::handleCommonSettings()
|
||||
|
||||
ImGui::SliderFloat("Min Overlap", &m_traverseEdgeMinOverlap, 0.0f, m_tileSize*m_cellSize, "%g");
|
||||
|
||||
ImGui::SliderFloat("Max Align", &m_traversePortalMaxAlign, 0.0f, 0.5f, "%g", ImGuiSliderFlags_AlwaysClamp);
|
||||
|
||||
if (ImGui::Button("Rebuild Static Pathing Data"))
|
||||
createStaticPathingData();
|
||||
|
||||
@ -629,8 +632,8 @@ static bool traverseLinkOffsetIntersectsGeom(const InputGeom* geom, const float*
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool traverseLinkInLOS(void* userData, const float* lowPos, const float* highPos, const float* lowDir,
|
||||
const float* highDir, const float walkableRadius, const float slopeAngle)
|
||||
static bool traverseLinkInLOS(void* userData, const float* lowPos, const float* highPos, const float* lowNorm,
|
||||
const float* highNorm, const float walkableRadius, const float slopeAngle)
|
||||
{
|
||||
Editor* editor = (Editor*)userData;
|
||||
InputGeom* geom = editor->getInputGeom();
|
||||
@ -649,12 +652,6 @@ static bool traverseLinkInLOS(void* userData, const float* lowPos, const float*
|
||||
else
|
||||
offsetAmount = walkableRadius + extraOffset;
|
||||
|
||||
float lowNormal[3];
|
||||
rdCalcEdgeNormal2D(lowDir, lowNormal);
|
||||
|
||||
float highNormal[3];
|
||||
rdCalcEdgeNormal2D(highDir, highNormal);
|
||||
|
||||
// Detect overhangs to avoid links like these:
|
||||
//
|
||||
// geom upper navmesh
|
||||
@ -678,7 +675,7 @@ static bool traverseLinkInLOS(void* userData, const float* lowPos, const float*
|
||||
// The AI would otherwise attempt to initiate
|
||||
// the jump from the lower navmesh, causing it
|
||||
// to clip through geometry.
|
||||
if (!polyEdgeFaceAgainst(lowPos, highPos, lowNormal, highNormal))
|
||||
if (!polyEdgeFaceAgainst(lowPos, highPos, lowNorm, highNorm))
|
||||
return false;
|
||||
|
||||
const float* targetRayPos = highPos;
|
||||
@ -715,8 +712,8 @@ static bool traverseLinkInLOS(void* userData, const float* lowPos, const float*
|
||||
|
||||
if (hasOffset)
|
||||
{
|
||||
offsetRayPos[0] = highPos[0] + highNormal[0] * offsetAmount;
|
||||
offsetRayPos[1] = highPos[1] + highNormal[1] * offsetAmount;
|
||||
offsetRayPos[0] = highPos[0] + highNorm[0] * offsetAmount;
|
||||
offsetRayPos[1] = highPos[1] + highNorm[1] * offsetAmount;
|
||||
offsetRayPos[2] = highPos[2];
|
||||
|
||||
if (traverseLinkOffsetIntersectsGeom(geom, highPos, offsetRayPos))
|
||||
@ -787,6 +784,7 @@ void Editor::createTraverseLinkParams(dtTraverseLinkConnectParams& params)
|
||||
|
||||
params.userData = this;
|
||||
params.minEdgeOverlap = m_traverseEdgeMinOverlap;
|
||||
params.maxPortalAlign = m_traversePortalMaxAlign;
|
||||
}
|
||||
|
||||
bool Editor::createTraverseLinks()
|
||||
|
@ -255,6 +255,7 @@ protected:
|
||||
float m_agentMaxSlope;
|
||||
float m_traverseRayExtraOffset;
|
||||
float m_traverseEdgeMinOverlap;
|
||||
float m_traversePortalMaxAlign;
|
||||
int m_regionMinSize;
|
||||
int m_regionMergeSize;
|
||||
int m_edgeMaxLen;
|
||||
|
@ -647,8 +647,8 @@ struct dtTraverseLinkConnectParams
|
||||
/// @param[in] userData Pointer to user defined data.
|
||||
/// @param[in] lowerEdgeMid The mid point of the lower edge from which the link starts. [(x, y, z)] [Unit: wu]
|
||||
/// @param[in] higherEdgeMid The mid point of the higher edge to which the link ends. [(x, y, z)] [Unit: wu]
|
||||
/// @param[in] lowerEdgeDir The vector direction of the lower edge. [(x, y, z)] [Unit: wu]
|
||||
/// @param[in] higherEdgeDir The vector direction of the higher edge. [(x, y, z)] [Unit: wu]
|
||||
/// @param[in] lowerEdgeNorm The edge normal of the lower edge. [(x, y, z)] [Unit: wu]
|
||||
/// @param[in] higherEdgeNorm The edge normal of the higher edge. [(x, y, z)] [Unit: wu]
|
||||
/// @param[in] walkableRadius The walkable radius defined by the tile hosting the link. [Unit: wu]
|
||||
/// @param[in] slopeAngle The slope angle from lower to higher edge mid points. [Unit: Degrees]
|
||||
/// @return True if the link between the lower and higher edge mid points don't collide with anything.
|
||||
@ -675,7 +675,8 @@ struct dtTraverseLinkConnectParams
|
||||
int(*addPolyLink)(void* userData, const dtPolyRef basePolyRef, const dtPolyRef landPolyRef, const unsigned int traverseTypeBit);
|
||||
|
||||
void* userData; ///< The user defined data that will be provided to all callbacks, for example: your editor's class instance.
|
||||
float minEdgeOverlap; ///< The minimum amount of projection overlap required between the 2 edges before they are considered overlapping.
|
||||
float minEdgeOverlap; ///< The minimum amount of projection overlap required between the 2 edges before they are considered overlapping. [Unit: wu]
|
||||
float maxPortalAlign; ///< The maximum amount of portal alignment the system will apply. [Limit: 0 >= align < 0.5]
|
||||
bool linkToNeighbor; ///< Whether to link to polygons in neighboring tiles. Limits linkage to internal polygons if false.
|
||||
};
|
||||
|
||||
|
@ -107,6 +107,47 @@ static void calcSlabEndPoints(const float* va, const float* vb, float* bmin, flo
|
||||
}
|
||||
}
|
||||
|
||||
static void alignPortalLimits(const float* portal1Pos, const float* portal1Norm, const float* portal2Pos,
|
||||
const float portalTmin, const float portalTmax, float& outPortalTmin, float& outPortalTmax, const float maxAlign)
|
||||
{
|
||||
// note(amos): if we take an extreme scenario, where:
|
||||
// - span = portalTmax(1.0f) - portalTmin(0.99f)
|
||||
// - shiftAmount = maxAlign(1.0f) * abs(crossZ(1.0f)) * span(0.1f)
|
||||
// the portal will collapse as we will shift portalTmin(0.99f) with
|
||||
// shiftAmount(0.1f)! Also, any value above 0.5 will cause too much
|
||||
// shifting; to avoid rounding mins into maxs or the other way around
|
||||
// during quantization, maxAlign should never be greater than 0.5.
|
||||
rdAssert(maxAlign <= 0.5f);
|
||||
|
||||
float delta[3];
|
||||
delta[0] = portal2Pos[0]-portal1Pos[0];
|
||||
delta[1] = portal2Pos[1]-portal1Pos[1];
|
||||
delta[2] = 0.0f;
|
||||
rdVnormalize2D(delta);
|
||||
|
||||
float cross[3];
|
||||
rdVcross(cross, delta, portal1Norm);
|
||||
|
||||
const float span = portalTmax-portalTmin;
|
||||
const float shiftAmount = maxAlign * rdMathFabsf(cross[2]) * span;
|
||||
|
||||
if (cross[2] < 0)
|
||||
{
|
||||
outPortalTmin = rdMin(portalTmax, portalTmin+shiftAmount);
|
||||
outPortalTmax = rdMin(1.0f, portalTmax+shiftAmount);
|
||||
}
|
||||
else if (cross[2] > 0)
|
||||
{
|
||||
outPortalTmin = rdMax(0.0f, portalTmin-shiftAmount);
|
||||
outPortalTmax = rdMax(portalTmin, portalTmax-shiftAmount);
|
||||
}
|
||||
else
|
||||
{
|
||||
outPortalTmin = portalTmin;
|
||||
outPortalTmax = portalTmax;
|
||||
}
|
||||
}
|
||||
|
||||
inline int computeTileHash(int x, int y, const int mask)
|
||||
{
|
||||
const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
|
||||
@ -824,6 +865,9 @@ dtStatus dtNavMesh::connectTraverseLinks(const dtTileRef tileRef, const dtTraver
|
||||
float baseEdgeDir[3];
|
||||
rdVsub(baseEdgeDir, baseDetailPolyEdgeEpos, baseDetailPolyEdgeSpos);
|
||||
|
||||
float baseEdgeNorm[3];
|
||||
rdCalcEdgeNormal2D(baseEdgeDir, baseEdgeNorm);
|
||||
|
||||
const unsigned char baseSide = rdClassifyDirection(baseEdgeDir, baseHeader->bmin, baseHeader->bmax);
|
||||
|
||||
const int MAX_NEIS = 32; // Max neighbors
|
||||
@ -955,6 +999,9 @@ dtStatus dtNavMesh::connectTraverseLinks(const dtTileRef tileRef, const dtTraver
|
||||
float landEdgeDir[3];
|
||||
rdVsub(landEdgeDir, landDetailPolyEdgeEpos, landDetailPolyEdgeSpos);
|
||||
|
||||
float landEdgeNorm[3];
|
||||
rdCalcEdgeNormal2D(landEdgeDir, landEdgeNorm);
|
||||
|
||||
const float elevation = rdMathFabsf(basePolyEdgeMid[2] - landPolyEdgeMid[2]);
|
||||
const float slopeAngle = rdMathFabsf(rdCalcSlopeAngle(basePolyEdgeMid, landPolyEdgeMid));
|
||||
const bool baseOverlaps = rdCalcEdgeOverlap2D(baseDetailPolyEdgeSpos, baseDetailPolyEdgeEpos, landDetailPolyEdgeSpos, landDetailPolyEdgeEpos, baseEdgeDir) > params.minEdgeOverlap;
|
||||
@ -977,18 +1024,26 @@ dtStatus dtNavMesh::connectTraverseLinks(const dtTileRef tileRef, const dtTraver
|
||||
const bool basePolyHigher = basePolyEdgeMid[2] > landPolyEdgeMid[2];
|
||||
const float* const lowerEdgeMid = basePolyHigher ? landPolyEdgeMid : basePolyEdgeMid;
|
||||
const float* const higherEdgeMid = basePolyHigher ? basePolyEdgeMid : landPolyEdgeMid;
|
||||
const float* const lowerEdgeDir = basePolyHigher ? landEdgeDir : baseEdgeDir;
|
||||
const float* const higherEdgeDir = basePolyHigher ? baseEdgeDir : landEdgeDir;
|
||||
const float* const lowerEdgeNorm = basePolyHigher ? landEdgeNorm : baseEdgeNorm;
|
||||
const float* const higherEdgeNorm = basePolyHigher ? baseEdgeNorm : landEdgeNorm;
|
||||
|
||||
const float walkableRadius = basePolyHigher ? baseHeader->walkableRadius : landHeader->walkableRadius;
|
||||
|
||||
if (!params.traverseLinkInLOS(params.userData, lowerEdgeMid, higherEdgeMid, lowerEdgeDir, higherEdgeDir, walkableRadius, slopeAngle))
|
||||
if (!params.traverseLinkInLOS(params.userData, lowerEdgeMid, higherEdgeMid, lowerEdgeNorm, higherEdgeNorm, walkableRadius, slopeAngle))
|
||||
continue;
|
||||
|
||||
const unsigned char landSide = params.linkToNeighbor
|
||||
? rdClassifyPointOutsideBounds(landPolyEdgeMid, landHeader->bmin, landHeader->bmax)
|
||||
: rdClassifyPointInsideBounds(landPolyEdgeMid, landHeader->bmin, landHeader->bmax);
|
||||
|
||||
float newBaseTmin;
|
||||
float newBaseTmax;
|
||||
alignPortalLimits(basePolyEdgeMid, baseEdgeNorm, landPolyEdgeMid, baseTmin, baseTmax, newBaseTmin, newBaseTmax, params.maxPortalAlign);
|
||||
|
||||
float newLandTmin;
|
||||
float newLandTmax;
|
||||
alignPortalLimits(landPolyEdgeMid, landEdgeNorm, basePolyEdgeMid, landTmin, landTmax, newLandTmin, newLandTmax, params.maxPortalAlign);
|
||||
|
||||
const unsigned int forwardIdx = baseTile->allocLink();
|
||||
const unsigned int reverseIdx = landTile->allocLink();
|
||||
|
||||
@ -1003,8 +1058,8 @@ dtStatus dtNavMesh::connectTraverseLinks(const dtTileRef tileRef, const dtTraver
|
||||
forwardLink->ref = landPolyRef;
|
||||
forwardLink->edge = (unsigned char)j;
|
||||
forwardLink->side = landSide;
|
||||
forwardLink->bmin = (unsigned char)rdMathRoundf(baseTmin*255.f);
|
||||
forwardLink->bmax = (unsigned char)rdMathRoundf(baseTmax*255.f);
|
||||
forwardLink->bmin = (unsigned char)rdMathRoundf(newBaseTmin*255.f);
|
||||
forwardLink->bmax = (unsigned char)rdMathRoundf(newBaseTmax*255.f);
|
||||
forwardLink->next = basePoly->firstLink;
|
||||
basePoly->firstLink = forwardIdx;
|
||||
forwardLink->traverseType = (unsigned char)traverseType;
|
||||
@ -1016,8 +1071,8 @@ dtStatus dtNavMesh::connectTraverseLinks(const dtTileRef tileRef, const dtTraver
|
||||
reverseLink->ref = basePolyRef;
|
||||
reverseLink->edge = (unsigned char)p;
|
||||
reverseLink->side = baseSide;
|
||||
reverseLink->bmin = (unsigned char)rdMathRoundf(landTmin*255.f);
|
||||
reverseLink->bmax = (unsigned char)rdMathRoundf(landTmax*255.f);
|
||||
reverseLink->bmin = (unsigned char)rdMathRoundf(newLandTmin*255.f);
|
||||
reverseLink->bmax = (unsigned char)rdMathRoundf(newLandTmax*255.f);
|
||||
reverseLink->next = landPoly->firstLink;
|
||||
landPoly->firstLink = reverseIdx;
|
||||
reverseLink->traverseType = (unsigned char)traverseType;
|
||||
|
Loading…
x
Reference in New Issue
Block a user