Get GraphNode's normal?

I am clamping the character’s position using the following function, however it would be nice to actually pull character towards the node (it might be heavily inclined). How do I get GraphNode’s normal?

// keeps the vector on the same y-level as the navmesh's surface, and corrects it 
// if it's too far (within horizontal-plane) from the nearest position on the navMesh
protected void ClampToNavMesh(ref Vector3 wanted_nextAi_pos, int graphMask=-1, int tagMask=-1) {
    
    NNConstraint constraint = NNConstraint.Default;

    if(graphMask != -1) {
        constraint.graphMask = graphMask;
    }

    if(tagMask != -1) {
        constraint.constrainTags = true;
        constraint.tags = tagMask;
    }
    //glue to the surface:
    NNInfo info = AstarPath.active.GetNearest(wanted_nextAi_pos, constraint);
    wanted_nextAi_pos.y = info.position.y;

    //discard the user's vector if it's too far, even after the y-correct:
    if((wanted_nextAi_pos - info.position).sqrMagnitude > 0.01f) {  
        wanted_nextAi_pos = info.position;
    }
}

Hi

The normal is not explicitly stored for any node, however if you have a navmesh/recast graph you can calculate the normal like this:

TriangleMeshNode triNode = node as TriangleMeshNode;
var normal = Vector3.Cross((Vector3)(triNode.GetVertex(1) - triNode.GetVertex(0)), (Vector3)(triNode.GetVertex(2) - triNode.GetVertex(0)));

You may also be interested in some methods on the TriangleMeshNode class, such as ClosestPointOnNode, ClosestPointOnNodeXZ and ContainsPoint.

1 Like

How do I get the node of a particular agent in a situation with multiple navmesh in the same space? for example small medium giant agents are baked

var info = AstarPath.active.GetNearest(position);
TriangleMeshNode triNode = info.node as TriangleMeshNode;
var normal = Vector3.Cross((Vector3) (triNode.GetVertex(1)-triNode.GetVertex(0)),(Vector3) (triNode.GetVertex(2)-triNode.GetVertex(0)));

GetNearest will get the nearest node regardless of the agent traversable graph

also this math
Vector3.Cross((Vector3) (triNode.GetVertex(1)-triNode.GetVertex(0)),(Vector3) (triNode.GetVertex(2)-triNode.GetVertex(0)));
is isobarycentric and will pop when moving to another node.
it needs to barycenter smoothly to another node with regard to position within the triangle

btw, here is the caching code

public static Vector3 GetCachedNormal(int agentTypeId,Vector3 position)
{
Vector3Int spatialHash = GetSpatialHash(position);
if(_normalCache.TryGetValue(agentTypeId,out var typeCache) && typeCache.TryGetValue(spatialHash,out var cachedNormal)) { return cachedNormal; }
//astar
var info = AstarPath.active.GetNearest(position);
TriangleMeshNode triNode = info.node as TriangleMeshNode;
var normal = Vector3.Cross((Vector3) (triNode.GetVertex(1)-triNode.GetVertex(0)),(Vector3) (triNode.GetVertex(2)-triNode.GetVertex(0)));
//
UpdateCache(agentTypeId,spatialHash,normal);
return normal;
}

You can pass a graphMask to the GetNearest call.

See NearestNodeConstraint - A* Pathfinding Project

There’s no built-in code to interpolate the normal, I’m afraid. You’ll have to do that yourself.