Thank You
Yo @Cedric … what do you think about this approach … I use unity to bake out the navmesh from the scene geometry… that way I can define walkable and non walkable surfaces and get the cut outs …
Then call the createNavMesh with the single pre baked navmesh geometry instead of feeding all the scene meshes
How would I adjust the navmesh properties according since I’m not use all the scene geometry but instead re baking the navmesh at runtime with a single pre baked mesh ???
You have to use the pre baked mesh. Set the parameters to allow full mesh to be walkable (as the walkable surface has already been processed in Unity). Then, get the binary array that you can reuse. It will always be faster to use the binary stream than recomputed a simple navmesh from a pre baked .gltf.
What are the parameters to set the full mesh walkable ???
You can try with such parameters:
var navmeshParameters = {
cs: 0.0,
ch: 0.0,
walkableSlopeAngle: 90,
walkableHeight: 0,
walkableClimb: 10,
walkableRadius: 0,
maxEdgeLen: 12.,
maxSimplificationError: 1.3,
minRegionArea: 8,
mergeRegionArea: 20,
maxVertsPerPoly: 6,
detailSampleDist: 6,
detailSampleMaxError: 1,
};
if i try CS or CH with a value of ZERO… Its says cannot build detour navmesh.
can you provide a PG?
Yo @Cedric
Got a question about the file format you are using.
Did you just make up that file format ???
or is it the RecastDemo file format ???
I found some libs for exporting navmesh.bin from unity baked navmesh triangles…
Can you test this file:
navmesh.bin.zip (4.1 KB)
I am trying to use a third party recast library in C# unity code and would to save that binary file at design time using C# recast api then load binary in Babylon js at runtime.
If the c# recast is already saving some sort of binary json … can the Babylon recast lib be updated to read that binary json format or do I have to make the c# version save the mesh data exactly like your recast.cpp getNavmeshData function ???
It’s the recastdemo file format.
You should be able to generate it from .obj using that demo as well.
Yo @Cedric … I think the coordinate system is throwing things off in BabylonJS when loading a navmesh.bin
Attached in the zip file is the navmesh.obj used by Solo RecastDemo to generate navmesh.bin.
Navmesh_Test.zip (7.6 KB)
But when rendering is navmesh.bin using the new binary load feature… The navmesh in babylon is flipped the wrong way
In babylon (runtime baked)
In babylon (load binary)
But is looks good in RecastDemo because RecastDemo is using different coordinate system… As a matter a fact i had to INVERT the vertices.x = -xdata when exporting the input mesh from Left Handed Unity to make compatible with recast.
Anyways i think the render is off in babylon.js when loaded a saved binary navmesh from RecastDemo
Can you please take a look… The zip file has bothe the input obj mesh and the baked navmesh.bin which is render flipped the wrong way in babylon.js
Thanks
Yo @Cedric … I forgot to mention that if i save the navmesh binary data in babylon after creating the navmash mesh data using a native left handed babylob mesh object… And then re-load that navmesh.bin
into bablon… it work great…
But if i create the navmesh using recastdemo and try to load that in babylon… its flipped wrong…
So your recast.cpp code for baking navmeshes using native babylon meshes MUST be taking into account the babylon left handed coordinate system
Here is a sample navmesh saved from babylonjs getNavmeshData() function (Which works fine)
navmesh2.bin.zip (10.2 KB)
Thank you @MackeyK24 for the clarification.
I’ll take a look at the binary file to see if there is smth about coordinates handness. AFAIK, I don’t remember anything about that in the file export binary.
The binary export code is the same between babylon and recast demo. Only the provided geometry makes a difference.
Maybe adding a flag to the loading function in babylon is the best I can do? What do you think?
Yo @Cedric … Your right… Its in the Geometry Provided… In RecastJSPlugin you are transforming the positions coordinates using the the world matrix of the mesh and putting the value into the positions array and then passing that transformed positions array directly to the recastjs.cpp build function.
I am creating a native xcode project that using Recast/Detour and your recastjs.cpp files. I then create DLLIMPORT so i can call a BuildNavMesh(float* positions …) functions directly from Unity Editor… i will create the positions array in C# unity code from the Unity Mesh object… and transform the positions with the mesh localToWorldMatrix and see if that keep things in the right coordinate mode when saving and re-loading the binary nav mesh data… Fingers Crossed
Yo @Cedric … Just wanna let you know the progress i made in creating a native recast tools project that i can call from Unity Editor at design time to create binary navmesh data from the built-in unity navigation baking system.
First i added a native saveNavmeshData(filename) function to recastjs.cpp
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;
}
Then i have a extern c function that gets exposed in C# via the [DllImport]
EXPORT_API int GenerateNavigationData(const float* positions, const int positionCount, const int* indices, const int indexCount,
const float cs, const float ch, const int walkHeight, const int walkRadius, const int walkClimb, const float walkSlope,
const int maxEdgeLen, const float maxSimplificationError, const int minRegionArea, const int mergeRegionArea,
const int maxVertsPerPoly, const float detailSampleDist, const float detailSampleMaxError, const char* outputFilename) {
// Set config navmesh properties
rcConfig m_cfg;
memset(&m_cfg, 0, sizeof(m_cfg));
m_cfg.cs = cs;
m_cfg.ch = ch;
m_cfg.walkableHeight = walkHeight;
m_cfg.walkableRadius = walkRadius;
m_cfg.walkableClimb = walkClimb;
m_cfg.walkableSlopeAngle = walkSlope;
m_cfg.maxEdgeLen = maxEdgeLen;
m_cfg.maxSimplificationError = maxSimplificationError;
m_cfg.minRegionArea = minRegionArea;
m_cfg.mergeRegionArea = mergeRegionArea;
m_cfg.maxVertsPerPoly = maxVertsPerPoly;
m_cfg.detailSampleDist = detailSampleDist;
m_cfg.detailSampleMaxError = detailSampleMaxError;
// Build and save navmesh data
NavMesh navmesh;
navmesh.build(positions, positionCount, indices, indexCount, m_cfg);
return navmesh.saveNavmeshData(outputFilename);
}
So i basically use your recastjs code on native mac and windows platform editor and call
navmesh.build(…)
then
navmesh.saveNavmeshData(…)
that navmesh.bin saved file can then be loaded directly at runtime in babylon js for optimum performance
Oh Yeah… My Code above is working beautifully… If you need generate pre baked navmesh data using native code… That GenerateNavigationData function i wrote above works like a charm. I use is to pre bake the navmesh binary data at design time from native unity navigation system.
Unity design time navigation setup
Babylon.js navigation using unity export info (binary navmesh file)
Thanks @Cedric for all your good workin on recast api for babylon. I was able to build on top of that and provide Unity Style Navigation including Nav Mesh Agent support … All out-the-box from the Babylon Toolkit - Unity Exporter … Thanks again
BTW… I need to holla at you about off mesh connections … Can we get this implemented … Pretty Please ???
Nice work! This looks awesome!
I need to take a look at how to support it but anything supported by recast should be supported by babylonjs
Yo @Cedric … Hey man… You got any idea how to setup areas and cost. I cant really tell from the RecastDemo how to make different items in the seen with a different area, like water that would have alot higher cost than 1.0.
I cant tell during the build process how you mark polygons with a specific area, especially since we pass all the vertices and triangles for the entire scene as a float array.
Any help here would be appreciated.
Yo @Cedric … BTW… I figured out off mesh connections. You are probably wanna gonna put this in for runtime nav mesh generation.
Now for me, i bake all the navigation data at design time using unity then i simple load the binary navmesh data (that has all the off mesh links info baked in).
First… I PREBAKE the scene using the Built-in Unity API’s and NavMeshComponents. I can use the Unity Editor to setup all the Walkable and Non Walkable areas as well as the basic bake params like agent radius, height, etc… I can use the smaller voxel sizes on this prebaked unity navmesh to get more accurate navmeshes (for the scene geometry)… This can take longer for really high resolution navmeshes (But thats why we do it design time)
Second… On scene export… I RE-BAKE the output nav mesh triangles (With any walkable areas already defined and cutouts for non walkable areas) using cell sizes (0.1 thru 0.2) climp = 10 and max slope 90… Basically the pre-baked navmesh is already setup from unity and we use recast to rasterize native detour navigation mesh binary data.
To do this i modded the recastjs.cpp build function to pass the all the off mesh connection arrays at build time.
void build(const float* positions, const int positionCount, const int* indices, const int indexCount, const rcConfig& config, int offMeshConCount = 0, const float* offMeshConVerts = 0, const float* offMeshConRads = 0, const unsigned short* offMeshConFlags = 0, const unsigned char* offMeshConAreas = 0, const unsigned char* offMeshConDirs = 0, const unsigned int* offMeshConUserIds = 0);
Then dtNavMeshCreateParams setup
dtNavMeshCreateParams params;
memset(¶ms, 0, sizeof(params));
params.verts = pmesh->verts;
params.vertCount = pmesh->nverts;
params.polys = pmesh->polys;
params.polyAreas = pmesh->areas;
params.polyFlags = pmesh->flags;
params.polyCount = pmesh->npolys;
params.nvp = pmesh->nvp;
params.detailMeshes = dmesh->meshes;
params.detailVerts = dmesh->verts;
params.detailVertsCount = dmesh->nverts;
params.detailTris = dmesh->tris;
params.detailTriCount = dmesh->ntris;
/////////////////////////////////////////////////////////
/// Mackey Kinard - Optional connection between areas ///
/////////////////////////////////////////////////////////
params.offMeshConCount = offMeshConCount;
params.offMeshConVerts = offMeshConVerts;
params.offMeshConRad = offMeshConRads;
params.offMeshConDir = offMeshConDirs;
params.offMeshConAreas = offMeshConAreas;
params.offMeshConFlags = offMeshConFlags;
params.offMeshConUserID = offMeshConUserIds;
/////////////////////////////////////////////////////////
params.walkableHeight = config.walkableHeight;
params.walkableRadius = config.walkableRadius;
params.walkableClimb = config.walkableClimb;
rcVcopy(params.bmin, pmesh->bmin);
rcVcopy(params.bmax, pmesh->bmax);
params.cs = cfg.cs;
params.ch = cfg.ch;
params.buildBvTree = true;
if (!dtCreateNavMeshData(¶ms, &m_navData, &navDataSize))
{
Log("Could not build Detour navmesh.");
return ;
}
And for my Global C# Extern C function
extern "C"
{
EXPORT_API int GenerateNavigationData(const float* positions, const int positionCount, const int* indices, const int indexCount,
const float cs, const float ch, const int walkHeight, const int walkRadius, const int walkClimb, const float walkSlope,
const int maxEdgeLen, const float maxSimplificationError, const int minRegionArea, const int mergeRegionArea,
const int maxVertsPerPoly, const float detailSampleDist, const float detailSampleMaxError,
int offMeshConCount, const float* offMeshConVerts, const float* offMeshConRads, const unsigned short* offMeshConFlags,
const unsigned char* offMeshConAreas, const unsigned char* offMeshConDirs, const unsigned int* offMeshConUserIds,
const char* outputFilename) {
// Set config navmesh properties
rcConfig m_cfg;
memset(&m_cfg, 0, sizeof(m_cfg));
m_cfg.cs = cs;
m_cfg.ch = ch;
m_cfg.walkableHeight = walkHeight;
m_cfg.walkableRadius = walkRadius;
m_cfg.walkableClimb = walkClimb;
m_cfg.walkableSlopeAngle = walkSlope;
m_cfg.maxEdgeLen = maxEdgeLen;
m_cfg.maxSimplificationError = maxSimplificationError;
m_cfg.minRegionArea = minRegionArea;
m_cfg.mergeRegionArea = mergeRegionArea;
m_cfg.maxVertsPerPoly = maxVertsPerPoly;
m_cfg.detailSampleDist = detailSampleDist;
m_cfg.detailSampleMaxError = detailSampleMaxError;
// Build and save navmesh data
NavMesh navmesh;
navmesh.build(positions, positionCount, indices, indexCount, m_cfg, offMeshConCount, offMeshConVerts, offMeshConRads, offMeshConFlags, offMeshConAreas, offMeshConDirs, offMeshConUserIds);
return navmesh.saveNavmeshData(outputFilename);
}
}
and a example C# Unity Design time usage:
// Setup Test Off Mesh Link
offMeshConCount = 1;
offMeshConVerts = new float[] { -19.58f, -1.55f, -23.56f, -27.32f, -1.55f, -23.56f };
offMeshConRads = new float[] { 0.5f };
offMeshConFlags = new ushort[] { 0x01 };
offMeshConAreas = new byte[] { (byte)0 };
offMeshConDirs = new byte[] { (byte)1 }; // Bi-Directional Link
offMeshConUserIds = new uint[] { 0 }; // Use Index As ID
Note: the offMeshConVerts float array is basically the start and end points for the link (6 floats per connection)
It works beautifully in babylon js right now because its all in the bake/build that generated at design time and RecastJSPlugin just load the binary data… But if you want folks to be able to specify off mesh connections when baking the navmesh data at runtime (RecastJSPlugin.CreateNavMesh)… Your gonna need to pass the arrays above describing the off mesh connections.
Thanks again for kicking all this recast stuff off. I was able to build upon that and make a really kool navigation export system for unity… Out the box… The only code needed is to set the NavigationAgent properties like agent.setDestination or agent.teleport … The mechanics are just like you would use in unity… Freaking Sweet
Just need to handle Area Cost to really finish it off
Yo @Cedric … I noticed a slight issues with computePath vs agentGoto when using more complex input geometry. Attached is the nav_test.obj sample input mesh from demo. when you click a point and call agentGoto to send agent to that point and then call computePath to draw dashed lines… the dashed lines shows a different route to that same point. Its like the agentGoto is using a slight more optimized path to the point than computePath does.
Example Input Mesh
nav_test.obj.zip (26.9 KB)
Where did you put together that recasejs.cpp NavMesh::computePath function from… (What part of recast demo did you use to a guide to put that function together) ???
Hey @MackeyK24
I managed to set the off-mesh connections and area types in the recast demo. I’ll do some doc/tutorial on how to set it soon!
Ideally, I’d like to edit that in babylon as well.
For the computePath/navigation, I used old source code I did some years ago but didn’t notice any difference. I’m testing with your nav mesh now…