Navigation Mesh and crowd Agents

Yo @Cedric … I saw that functionality in the demo as well. But the demo source for handleBuild implies that there is a way to handle a scene of meshes and mark each one with area type:

// Find triangles which are walkable based on their slope and rasterize them.
// If your input data is multiple meshes, you can transform them here, calculate
// the area type for each of the meshes and rasterize them.
memset(m_triareas, 0, ntris*sizeof(unsigned char));
rcMarkWalkableTriangles(m_ctx, m_cfg.walkableSlopeAngle, verts, nverts, tris, ntris, m_triareas);
if (!rcRasterizeTriangles(m_ctx, verts, nverts, tris, m_triareas, ntris, *m_solid, m_cfg.walkableClimb))
{
	m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not rasterize triangles.");
	return false;
}

But i cant see how you would do that… They don’t give any notion as to how one iterate the meshes and assign an area type. Do have any idea how they mean for use to do that at that point in the build process ?

I also see the Convex Volume Tool … Which seems to allow you to set the area type within a extra shape volume… like a box (I don’t see different shape types). Thats a bit different than marking your meshes in Unity Editor and assigning Walkable or Non Walkable or even custom areas and its cost. But it would work pretty good for exporting content from unity where you use the new NavMeshComponents and the NavMeshModifierVolume component which does basically the same thing as the RecastDemo ConvexVolumeTool . So i will definitely look into that.

But it would be really kool if i could somehow mark the original input meshes with area types. Imagine a scene where i have geometry representing a lake or something and i want the exact layout of the lake to be marked with the WATER area and have a higher cost like 10 something :slight_smile:

Hey @Cedric … Can you check out this demo you made: Babylon.js Playground

If you just constantly click around, especially longer paths, when the cube is following the path, you can see a slight jitter of the cube as it moves along the path… Any way to smooth that out ???

Yo @Cedric … You mentioned that Dynamic obstacles are possible, just need ot be exposed … Can we do that ???

Can we also add a way to set our own path of points like agent.FollowPath(points) or something ???

Pretty please :slight_smile:

I’m adding obstacle support this week and will look at the custom path computation at the same time.

4 Likes

Still working on it and it takes more time than anticipated. I don’t think I can make a PR by tomorrow but who knows … :slight_smile:

4 Likes

Thanks @Cedric for the update on your progress, i was wondering how you doing with that

PR is live obstacles and corners by CedricGuillemet · Pull Request #9813 · BabylonJS/Babylon.js · GitHub
Documentation is in progress…

7 Likes

Thank you so much @Cedric

Really appreciate this :slight_smile:

1 Like

Hey @Cedric … The previous version of recastjs.cpp … (That i actually use in native code from the Unity editor to generate the navmesh at design time). I created a saveNavMeshData(const char path)* that works great:

// Mackey Kinard
int NavMesh::saveNavmeshData(const char* path)
{
    FILE* fp = fopen(path, "wb");
    if (!fp) return -1;
    
    // Setup header.
    NavMeshSetHeader header;
    header.magic = NAVMESHSET_MAGIC;
    header.version = NAVMESHSET_VERSION;
    header.numTiles = -1;

    if (m_navMesh)
    {
        // Get Navmesh
        const dtNavMesh* mesh = m_navMesh;

        // Store header.
        header.numTiles = 0;
        for (int i = 0; i < mesh->getMaxTiles(); ++i)
        {
            const dtMeshTile* tile = mesh->getTile(i);
            if (!tile || !tile->header || !tile->dataSize) continue;
            header.numTiles++;
        }
        memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams));
        fwrite(&header, sizeof(NavMeshSetHeader), 1, fp);
        
        // Store tiles.
        for (int i = 0; i < mesh->getMaxTiles(); ++i)
        {
            const dtMeshTile* tile = mesh->getTile(i);
            if (!tile || !tile->header || !tile->dataSize) continue;
            
            NavMeshTileHeader tileHeader;
            tileHeader.tileRef = mesh->getTileRef(tile);
            tileHeader.dataSize = tile->dataSize;
            fwrite(&tileHeader, sizeof(tileHeader), 1, fp);
            
            fwrite(tile->data, tile->dataSize, 1, fp);
        }
    }
    
    fclose(fp);
    
    return header.numTiles;
}

But now that dynamic object require tiles, i need to tweak to support tiles… so i can generate the nav mesh bin file at design time in the editor. Can you please help me fill in the blanks here so save the tiles nav mesh data to disk file so i can reload later using babylon js at runtime.

Her is the skeleton code i created to handle both single mesh and tiles

int NavMesh::saveNavmeshData(const char* path)
{
    FILE* fp = fopen(path, "wb");
    if (!fp) return -1;
    
    // Setup header.
    NavMeshSetHeader header;
    header.magic = NAVMESHSET_MAGIC;
    header.version = NAVMESHSET_VERSION;
    header.numTiles = -1;

    if (m_navMesh)
    {
        // Get Navmesh
        const dtNavMesh* mesh = m_navMesh;

        if (m_tileCache)
        {

            // TODO: Save Tiled Nav Mesh

        }
        else
        {
            // Store header.
            header.numTiles = 0;
            for (int i = 0; i < mesh->getMaxTiles(); ++i)
            {
                const dtMeshTile* tile = mesh->getTile(i);
                if (!tile || !tile->header || !tile->dataSize) continue;
                header.numTiles++;
            }
            memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams));
            fwrite(&header, sizeof(NavMeshSetHeader), 1, fp);
            
            // Store tiles.
            for (int i = 0; i < mesh->getMaxTiles(); ++i)
            {
                const dtMeshTile* tile = mesh->getTile(i);
                if (!tile || !tile->header || !tile->dataSize) continue;
                
                NavMeshTileHeader tileHeader;
                tileHeader.tileRef = mesh->getTileRef(tile);
                tileHeader.dataSize = tile->dataSize;
                fwrite(&tileHeader, sizeof(tileHeader), 1, fp);
                
                fwrite(tile->data, tile->dataSize, 1, fp);
            }
        }
    }
    
    fclose(fp);
    
    return header.numTiles;
}

Can please help out when you get a chance :slight_smile:

Yep, here is the code I use to save tiled data or single mesh:

and the part to determine if loaded data is tiled or not is here:

The first bytes of header is the same for both

then a chunk is written to make the difference

Yo @Cedric … Are you using a different recastnavigation base project??? I can seem to compile recast as a native project anymore …


Showing Recent Issues
Undefined symbol: rcCreateChunkyTriMesh(float const*, int const*, int, int, rcChunkyTriMesh*)

Undefined symbol: rcGetChunksOverlappingRect(rcChunkyTriMesh const*, float*, float*, int*, int)

Undefined symbol: _fastlz_compress

Undefined symbol: _fastlz_decompress


yes, 2 new files added to the solution:

Shit… didnt even look for a cmake file… I created Xcode and Visual Studio projects so i could create RecastTools.dll and RecastTools.bundle that is a pure native project i use to build the nav mesh at desgin time. So i was not using cmake

Thanks bro :slight_smile:

1 Like

@Cedric

i noticed the single sheet nav mesh save code is a little different… it seems you are now adding some sort of recast header BEFORE you write the NavMeshSetHeader.

What is

        bits = (unsigned char*)realloc(bits, bitsSize + sizeof(RecastHeader));
        memcpy(&bits[bitsSize], &recastHeader, sizeof(RecastHeader));
        bitsSize += sizeof(RecastHeader);

Just curious how come it worked before without the addition recast header. Was i missing something before when serializing the nav mesh ?

Yo @Cedric … Does Off Mesh Links NOT work when using dynamic obstacles (which in turn apparently required cached tiles) ???

Hi @MackeyK24
yes, I wanted to preserve backward compatibility with previously saved data.
There is an header named RecastHeader that’s common for tiled or simple navmesh.
It contains the magic number, the version and the number of tiles.
Then, there is the TileCacheSetHeader or the TileCacheTileHeader depending on the magic.
The magic number is NAVMESHSET_MAGIC for simple navmesh or TILECACHESET_MAGIC for tiled ones.
In RecastJS, there is no direct file access, data is read in a buffer (or written) and then processed. That’s what the snippet you posted does.
it copies the data to a buffer and resizes it on the fly. that buffer is returned to JS when everything is in.

For offmesh links, I don’t know. I guess that if it works with simple navmesh, it should work with tiled.