Navmesh not taking into account all geometries

Dear everybody,
I have the following problem:
I have two scenes, a street with a pedestrian crossing: and a street with a pedestrian crossing, a crossroads, pavement and some buildings.
I want to stress that the pedestrian crossing is its own geometry, as are the street, pavement, and so on.
I generate a Navmesh using jme3-ai.
On the smaller scene, the navmesh generates polygons that fit perfectly to the pedestrian crossing, meaning that there are two triangles that span precisely across the texture.

Like this:

On the larger scene, this is not happening. It looks like this:


(Obviously, there are more triangles in the mesh, I just restricted the example to the impoartant parts.)
It is of utmost importance to me that I can generate the polys/triangles in a way that they “follow” the textures/geometries.

I’ve tried so far to fiddle around with the settings in the navmesh generator (especially TraversableAreaBorderSize), but to no avail.
I know that the navmesh generation process takes the geometries and works on them.
I also know that the generator (findGeometries in NavMeshState.java) finds all relevant geometries (indeed, it finds the pedestrian crossing geometries).

Can anyone help me debug this problem? Meaning: Has anyone seen something like this and knows why the navmesh doesn’t take into account the crossing geometries?
I do not want to get any code (!), I just want to discuss the problem with you guys.

Best,
j

What does larger and smaller scene mean in your example? The mesh will only be the max size of the geometry its generated from.

Based off what you are saying then you have different navmeshes? street, pavement etc.?

All that does is pull the navmesh away from the sides, normally by the radius of the character using the mesh.

Have you read the explanation for the different parameters?

http://www.critterai.org/projects/nmgen_study/config.html

1 Like

First of all, let me apologize for the bad quality of my post.
Please consider the following scene:


You can see a streetcrossing, pavement, buildings (in blue) and a pedestrian crossing.
The navmesh I get is drawn in red. Its all over the scene.

What I would like to obtain is a navmesh like this:

Where the mesh follows the geometries (the pavement, the pedestrian crossing, and so on).

I played around with the parameters more (especially smoothing threshold, mergeRegionSize, edgeMAxDeviation, maxVolsPerPoly, contourSampleDistance and contourMaxDeviation), but to no avail so far.

I am pretty sure it should be possible to obtain what I would like to get, since the navmesh is generated using geometries and the things I want to consider are geometries.
I also know that the recast implementation in Unity can does this (funnily enough without any specific tweaks, it seems), so it should be possible in principle.
I am just not sure atm if this can be done by tweaking the settings or if I would need to tweak the actual navmesh generation.

Best,
j

You would have to pass the geometry for road as separate from sidewalk and the crossing to get this.

In recast you do the same thing but you also flag the geometry so for example the side walk would be flagged as maybe pedestrian, the road as pavement, the crossing as crossing.

Then when pathfinding you would set the flag in the filter for a vehicle as area_type_pavement, area_type_crossing and the characters as area_type_pedestrian, area_type_crossing.

So a vehicle would be able to move across pavement and crossing but not sidewalk and character sidewalk and crossing but not pavement.

You can implement recast4j, which is java version of recast, found here, GitHub - ppiastucki/recast4j: Java Port of Recast & Detour navigation mesh toolset. Its a great library and works well and has many features jme3ai doesn’t have like flags (at least not since last I looked).

jme3ai is generic and for basic pathfinding. You would have to implement your own system to determine what was a viable path and for whom after building the separate navmeshes.

Edit: When I say you do the same thing in recast, you pass in one geometry for the scene(i.e. road, crosswalk, sidewalk as one combined geometry) but you mark the area types based on the individual geometries of your choice. In jme3ai, you have to break things up into separate geometries to get the same effect.

2 Likes

On a side note, you can build navmeshes in blender also, they have recast implemented in the game engine. Jme will break up meshes when loading a model based off material. You can set whatever you want for material names in blender and grab the geometries when loading the models if you do things procedural.

2 Likes

Wonder if something like this could be easily automated in a JMEC script even.

Maybe we can start developing a standard set of general scripts that look for certain name/custom attributes and do this sort of thing.

1 Like

This is going to get deep into the weeds but I am unsure if it is needed with the converter.

The way I do it now is using SceneGraphVisitorAdapter.

        worldMap.depthFirstTraversal(new SceneGraphVisitorAdapter() {
            @Override
            public void visit(Geometry spat) {
                int geomLength = spat.getMesh().getTriangleCount() *3;
                
                String[] name = spat.getMaterial().getName().split("_");
                
                switch (name[0]) {

                    case "water":
                        geomProvider.addMod(new Modification(geomLength, AREAMOD_WATER));
                        break;
                    case "road":
                        geomProvider.addMod(new Modification(geomLength, AREAMOD_ROAD));
                        break;
                    case "grass":
                        geomProvider.addMod(new Modification(geomLength, AREAMOD_GRASS));
                        break;
                    case "door":
                        geomProvider.addMod(new Modification(geomLength, AREAMOD_DOOR));
                        break;
                    default:
                        geomProvider.addMod(new Modification(geomLength, AREAMOD_GROUND));
                }
            }
        });

The basic flow is worldmap is just a child of jme rootNode someplace so traverse that node looking for any geometries.

Grab any geometries found material. The material name has a delimiter in it so I can still name the materials but also target them as being used for the navMesh generation. I chose to use the first part of the name for this but it can be setup anyway you wish since using delimeter.

The converter could do this but I don’t see the practicality of doing it when converting. You would always have to convert to get the data then.

JMEC will do the traversal for you. Names could continue to be used or maybe even better: custom attributes. So maybe in Blender’s object properties you could add “walkType”=“water”, “door”, etc.

But otherwise, in a jmec script, you could do something like:

import com.jme3.scene.Geometry;

model.findAll(Geometry.class).each { geom ->
    // collect the stuff
}

I don’t know what this means.

I guess I thought that nav meshes could be generated and saved with the j3o. If that’ not the case then yeah, this won’t work.

Update:
I did manage to create nice navmeshes for the crossings and the pavements (so I think the other stuff should work as well).
What I wanted to do is to take the separate Meshes, merge them into one, and hand that to NavMesh.java’s loadFromMesh().

I tried to combine PositionBuffer A and B and IndexBuffer A and B and set them to a new Buffer of a new Mesh.

This does something, but the resulting mesh does not look at all like the meshes from which it was created. (In fact, the mesh looks completely broken.)

Just on a high level: Is my thinking correct that I can merge the meshes like I intended to do, or do I have a wrong understanding on how to merge meshes?
Does it suffice to merge the PositionBuffers into one and the IndexBuffers into one, or does this break the mesh?

Yes they are. Recast has its own format for storing meshes once generated, they are not an object, just a optimized data file so you can load areas on the fly real fast.

https://github.com/ppiastucki/recast4j/search?q=meshdatawriter&unscoped_q=meshdatawriter

You have to run the converter, even if its just for a script to get the data is all I was saying. I guess it could just be a part of the navmesh build process if people wanted. I am unsure just how to integrate it though.

By custom attributes do you mean properties? If so, I don’t think you can set properties on individual vertex groups or parts of a mesh. I think they are specific to an object itself?

Ideally, you could go into the object data panel in blender and assign vertex groups with a custom property. This would make things so much cleaner but I don’t think it does that either.

Edit: Heck, even just reading in a vertex group name and using that to grab vertices coulld be useful. I do not know how to get blender data though so I am of no help on that.

This class may help you understand merging. Never tried to manually merge a mesh so not sure.

Yeah, but then you can’t set spatial names for them either, eh?

If it’s a spatial, it’s an object. If it’s an object, it can have custom properties. If it has custom properties then these will be set as UserData on the spatial when JMEC converts the original format to j3o.

That’s where the “always convert” thing confused me… because you will already always be converting. No one in their right mind should be loading .blend files directly in their games. :slight_smile:

So the workflow I imagined, was something like:

  1. make your blender scene
  2. create a mesh for your walk mesh by extracting parts of other meshes.
  3. tag this with a custom property or give it a special name.
  4. export gltf
  5. run jmec to convert it to j3o, include a navmesh.groovy script that will automatically convert your tagged mesh(es) to walk meshes. (And remove that geometry.)

There is where the problem is though. A recast navmesh is for the whole area. You set area types for indiviual parts of the geometry. That’s what is messing @jay up. You have to combine the separate geometries into one larger mesh.

With recast you can mark each triangle in the mesh with an area type just by passing in triangle information. The only way I could figure out to get the right triangles to mark was by using a material.

Unless you have pieces with a single property and combine all the pieces back into a larger mesh. Maybe I am missing something in the conversation when you say tag it with a custom property because I think properties only affect the entire object. There is no way to say this part is road, or grass, etc. when using properties.

The only other way is to be able to get vertex group information I believe, like in the image I attached below.

Now that something I never thought about. Creating a disposable mesh like that. That is absolutely the way to go. I have just been using the final j3o for all the data but this would be a hugely better idea. It keeps the original model clean.

No, looks like only one spatial name and one vertex data per object.

https://imgur.com/a/0k6Dr5I

You can set groups though, but then you have to extract that information. I went with material since jme already does all the work for you. The big drawback for that is no atlas use but your comment about creating a disposable geometry fixes that.

Right, with navmesh, the vertices are the same but you may run the method over and over while tweaking the build parameters. So typically, you build the mesh against an already converted object. Running through jmec wouldn’t be a problem either way.

And the new JmecNode would let you create an app to tweak these settings in the script and have it reload automatically until you got them right.

I still think you could use separate meshes somehow and then batch them or something in the script. Just separated for tagging purposes.

Heh, yes, I didnt want to say that. If you were to use jmec, it really only makes sense that the script does it all. I just didnt know how much work the script should undertake.

Yes.

There is one huge caveat though. Recast as is was intended to be used with a gui. I rewrote the build method to allow for procedural use instead. There is a way that can be used to mark triangles after mesh creation but they never implemented it. After all, it was supposed to be used with the gui. Piotr, the owner of the recast4j project keeps his project tied to the original version since its a port . As such users have to do things their selves.

This being the case, people would have to figure out how to get this code to work.

Or, I could donate the builder class that I modified to allow for marking of triangles during the build of the navmesh.

So, its like diving into the rabbit hole if you try to build a script for it. The library is still having contributions and fixes all the time but its in maintenance mode.

Just an update:

  • Merging the meshes works perfectly, does exactly what I want.
  • For my specific use case, I might have to do it differently still, but that doesn’t involve JME so much: You can generate a Navmesh using other software (Blender, Unity, Unreal, whathaveyou) and feed it to jme3-ai. Works like a charm.

As far as I am concerned, this is solved. I do not flag it as such because there is a discussion going on still.

Thank you guys very much for the help!

1 Like