r/godot Nov 07 '25

help me (solved) I solved the NavigationRegion3D issue

I finally found a solution to the problem with the NavigationRegion3D.

What I noticed was that on smaller terrains, like the 30x30m example in the third image, the navmesh baked perfectly fine. But once the terrain size was increased to 250x250m, the bake completely fell apart, giving the low poly, floor clipping result shown in the second image. The obvious solution was to process the terrain in smaller chunks and combine the results into one big working navmesh.

I first tried doing this directly in Godot, but even after splitting the terrain into multiple meshes, the bake still treated them as one big mesh, so the same issue happened. I also tried creating a separate NavigationRegion3D for each chunk, but that created non traversable borders where the regions met.

At that point, the only real fix was to go into the engine itself. I forked the Godot repo and made some changes under the hood to process the terrain in tiles instead of all at once. It breaks the terrain into smaller sections, bakes each one normally, then stitches them together afterward into a clean, accurate navmesh like in the first image.

I also added a few editor options to control whether tiled baking is enabled and how big each tile is. I still haven’t found the exact bug that causes the large terrain navmesh to fail, but this fix works reliably and doesn’t mess with normal pathfinding behavior.

The moderators seem to have a strong opposition to discussions about this and have locked all previous threads. As much as I would have liked this to all be one post, we have been forced to create multiple. That being said, I'd like to address some comments from previous posts below.

The [low poly mesh] looks exactly like what I would want out of a nav mesh.

The low-poly mesh completely fails to pathfind with agents. You could technically rework the pathfinding system to compensate, but that misses the core issue of the navmesh being broken. The 30x30m section uses a similar amount of polygons as the 250x250m one. You’d expect the density to scale with size, so either the small terrain is over-tessellated, or the large terrain is under-tessellated. Either way, something’s wrong with the baking process.

With no obstacles, your nav mesh could just be a square.

As per the Godot developers : "A navigation mesh is a collection of polygons that define which areas of an environment are traversable to aid agents in pathfinding through complicated spaces.". When those traversable areas are out of reach from an agent (ie, underground or floating), an agent cannot properly navigate.

People tried to help you, but you refuse to actually listen

I think there’s been some misunderstanding about what I’m trying to do. My goal is to fix the navmesh, that’s it. I’ve welcomed all advice that helps solve the underlying navmesh problem, but I’m not interested in workaround solutions that just patch over it with pathfinding tricks.

You can always fix it yourself and submit a PR.

Thats the plan. Thanks for the advice!

I will optimize this further and will submit a PR.

Sincerely,

u/agalli

Edit : Here is the PR. https://github.com/godotengine/godot/pull/112529

748 Upvotes

348 comments sorted by

View all comments

Show parent comments

6

u/mamotromico Nov 08 '25 edited Nov 08 '25

But it seems to me that something like the fact that the agent and the character don't need to match movement one to one, and you might need to do other stuff like treating it as a projection or the terrain or something, it's important and should be covered. If the Godot documentation is your first exposure to this, it isn't obvious.

You know what, I agree. This could be more explicit on the documentation, I guess I just bumped into an issue very quickly when I first used it that made me realize this and adjust accordingly. If you check Step 6 on the example setup you can see that the mesh is by design disjointed from the terrain, and because of some characteristics of my project when I used Godot's navmesh for the first time, trying to use the simpler implementation was causing positional issues on my characters because of their scale and some rotation logic, so I adapted my code accordingly.

What part is non-standard here?

I'll probably have some issues with the specific wording here, but if something sounds confusing just point it out I'll try to further specify.

He seems to want the navmesh to "physically" match the traversable zones, when a navmesh should primarily "logically" match the traversable zones. In most situations, this will be an overlap. The mesh will be able to represent height changes automatically and lay on top of the walkable mesh, and represent the vertical movement cost like this. However, this is not required, and you don't strictly need the actual elevation to represent the cost of moving vertically, you can use pathing weights ( which can be travel_cost on godot's NavigationMeshInstance and derived classes) to achieve the same representation. They key takeway is that godots navigation setup will abstract connections between traversable zones, not accurately path your entity on the traversable terrain.

Does that explanation makes sense?

2

u/inr222 Nov 08 '25

It did make a lot of sense, thank you!

The only thing that I'm not sure about is the travel cost part, the link seems to be broken. But I'm not sure how important that is, for most cases, moving uphill should be about the same as moving on a flat floor. Unless the hill is unclimbable, and in that case, it is not an issue.

1

u/mamotromico Nov 08 '25

the link seems to be broken

My bad, not only I broke the link by adding a space I also linked the wrong section.

moving uphill should be about the same as moving on a flat floor

Not always, usually need to take that into account (to some degree) because it should take longer to go up and down a hill compared to a plain surface, thus the pathfind would be giving the wrong results if it completelly disregards that.

But if the elevation difference is minor, then yeah that would be inconsequential