Recast AI - Devlog #5

So, it is like this. Recast and Detour module are mainly completed. Now, I’m working with @mifth on demo game for MonkeyBrains. It was supposed to be representative demo for MonkeyBrains and to have all modules that the average game has.

As soon as we fix the problem with bullet not colliding with the wall, and test if agent is working correctly, I will start to use this game for jNavigation demo (recast testing and detour testing), and to build appropriate debug tools while building jNavigation demo and testing recast.

For anybody who wants to use jNavigation, I wouldn’t recommend it yet. The code is not yet tested and it has yet to be consistent with itself. :slight_smile:

It would be very helpful if anybody can suggest some solution for collision problem, because @mifth and I are dumbfounded by this. It is supposed to work… :-?

5 Likes

Thanks for the update Tihomir, it’s going to be great to use this with @mifth’s demo game. As for the collision, I don’t know the issue you are running into but if it becomes a problem you can just default to basic manual ray collision: every frame check if it will hit something within the next frame (velocity * direction * tpf and if that is more than the distance to the nearest object then collide it). Don’t get too bogged down in it, the collisions can be simulated for now. Or have really fast bullets and have the collisions happen instantly! Make them laser beams instead =)

Cool, keep up the good progress :wink:

@Sploreg said: Make them laser beams instead =)

They work like laser, but animation comes later, but anyway we fixed it. We somehow translated raytest and because of it, it had gave no result.

@mifth said that this game is not very demonstration for Recast navigation. So I made some simple maze with stairs… I will work on that maze as test demo.

yeah, my demo is more for gameplay testing with ai behaviour. but there could be added second floor easily. just add some polygons in blender.

I am making visual debugging tools for each stage. I wanted to make mesh that would represent Recast structure. Is there any method for getting vertices and indices and adding them to another mesh, or do I have to make it myself?

@Tihomir said: I am making visual debugging tools for each stage. I wanted to make mesh that would represent Recast structure. Is there any method for getting vertices and indices and adding them to another mesh, or do I have to make it myself?

[java]geometry.getMesh().getBuffer(Type type);[/java]

A maze is a great idea!

for editing meshes, do what @haze said. You have to get the correct buffer and then manipulate it. Sometimes it is easier to hold references to triangles outside the mesh and then map the indexes in there and update the actual mesh when needed. Or if the nav mesh changes, regenerate the whole thing. I guess it depends if you can detect what triangles have changed and then map those changes over. If recast doesn’t tell you what areas updated, then you might have to regenerate the mesh. Prepare to do this on another thread and when the buffers have been re-buitl, then go back on the render thread and send in the mesh geometry. I can help you through this part as it can be a little tricky if you haven’t done it before.

I am having problem building mesh for the first phase.
For the first phase mesh should look something like this. I wanted to build it using mesh mode Lines.

I made in Heightfield class that represents the result of first phase, method that will return its data as mesh.

[java]public Mesh drawHeightfieldSolid() {
Mesh mesh = new Mesh();
mesh.setMode(Mesh.Mode.Lines);

    Vector3f origin = getMinBounds();
    float cellSize = getCellSize();
    float cellHeight = getCellHeight();
    int width = getWidth();
    int height = getHeight();

    List<int[]> indices = new LinkedList<>();
    List<Vector3f[]> vertices = new LinkedList<>();

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            float fx = origin.getX() + x * cellSize;
            float fz = origin.getZ() + y * cellSize;
            Span s = getSpans()[x + y * width];
            while (s != null) {
                Vector3f min = new Vector3f(fx, origin.getY() + s.getMinSpanLimit() * cellHeight, fz);
                Vector3f max = new Vector3f(fx + cellSize, origin.getY() + s.getMaxSpanLimit() * cellHeight, fz + cellSize);
                Mesh mesh1 = GraphicHelper.lineBox(min, max);
                indices.add(GraphicHelper.getIndices(mesh1));
                vertices.add(GraphicHelper.getVertices(mesh1));
                s = s.getNext();
            }
        }
    }
    for (Vector3f[] vector3fs : vertices) {
        mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vector3fs));
    }
    for (int[] ints : indices) {
        mesh.setBuffer(VertexBuffer.Type.Index, 2, BufferUtils.createIntBuffer(ints));
    }
    mesh.updateCounts();
    mesh.updateBound();
    return mesh;
}[/java] 

I have built class GraphicHelper to make easier conversions as I will be using them a lot.

[java]public class GraphicHelper {

public static Vector3f[] getVertices(Mesh mesh) {
    VertexBuffer buffer = mesh.getBuffer(VertexBuffer.Type.Position);
    Vector3f[] array = new Vector3f[buffer.getNumElements()];
    for (int i = 0; i < buffer.getNumElements(); i++) {
        array[i] = new Vector3f();
        array[i].setX((Float) buffer.getElementComponent(i, 0));
        array[i].setY((Float) buffer.getElementComponent(i, 1));
        array[i].setZ((Float) buffer.getElementComponent(i, 2));
    }
    return array;
}

public static int[] getIndices(Mesh mesh) {
    int[] indices = new int[3];
    int[] triangles = new int[mesh.getTriangleCount() * 3];
    for (int i = 0; i < triangles.length; i += 3) {
        mesh.getTriangle(i / 3, indices);
        triangles[i] = indices[0];
        triangles[i + 1] = indices[1];
        triangles[i + 2] = indices[2];
    }
    return triangles;
}

public static Mesh lineBox(Vector3f min, Vector3f max) {
    Mesh mesh = new Mesh();
    mesh.setMode(Mesh.Mode.Lines);
    Vector3f[] vertices = new Vector3f[]{
        min,
        new Vector3f(min.x, min.y, max.z),
        new Vector3f(min.x, max.y, min.z),
        new Vector3f(max.x, min.y, min.z),
        new Vector3f(max.x, max.y, min.z),
        new Vector3f(max.x, min.y, max.z),
        new Vector3f(min.x, max.y, max.z),
        max
    };
    int[] indices = new int[]{
        0, 1, 0, 2, 0, 3,
        1, 5, 1, 6,
        2, 4, 2, 6,
        3, 4, 3, 5,
        4, 7, 5, 7, 6, 7
    };

    mesh.setBuffer(VertexBuffer.Type.Position,
            3, BufferUtils.createFloatBuffer(vertices));
    mesh.setBuffer(VertexBuffer.Type.Index, 2, BufferUtils.createIntBuffer(indices));
    return mesh;
}

}[/java]

I have this problem with getIndices():
[java]
Jul 21, 2014 11:23:21 AM com.jme3.app.Application handleError
SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
java.lang.IndexOutOfBoundsException
at java.nio.Buffer.checkIndex(Buffer.java:532)
at java.nio.DirectIntBufferU.get(DirectIntBufferU.java:253)
at com.jme3.scene.mesh.IndexIntBuffer.get(IndexIntBuffer.java:53)
at com.jme3.scene.Mesh.getTriangle(Mesh.java:855)
at com.jme3.ai.navigation.utils.GraphicHelper.getIndices(GraphicHelper.java:45)
[/java]

Also, if you have any idea how to improve these methods, let me know. :slight_smile:

Don’t draw lines, stick with triangles and just use the wireframe material setting. Or you can make it transparent and see the world through it.

If you really want to use lines you can. Just remember that each line needs two of its own vertices. So if you had lines going from 0,0 -> 1,0 -> 2,0 -> 3,0
The vertex position buffer would look like:
0,0,1,0,1,0,2,0,2,0,3,0
and the index buffer would be:
0,1,2,3,4,5

Your error indicates that you are trying to grab more indices than you have.

Can you paste some sample output data that recast returns for this 1st phase mesh?

I see now that this will be more difficult than I have anticipated. In 1st phase, size of cube is 0.3 x 0.2 so the number of them in my test case is 270x567x Z. Where Z is not constant. Just writing all data on console, takes some time.

It seems also that I don’t have enough space on heap to be able to get all info from native.

I followed the instructions how to recreate them from recast, so I don’t know what to do exactly.

That is quite a few cubes, but not too many for a modern video card to handle. You will have to make one mesh or many meshes and then batch them. But if you are building the mesh anyways then no need for batching. Console writing is very slow.

Is this step in visualizing the dubugging helpful to see what is wrong in a scene? The end-result path might be enough for the user and you could skip visualizing this step, unless it is critical to see where recast fumbled on the scene.

You can always increase your heap space. When building levels the user needs a lot of heap space anyways for all the tools and debugging output.

I don’t see it as very useful, but there is in Recast Navigation method that writes it like I have been doing. That is the reason I started to make visualization for this.

I will make visualization for last phase of navmesh building, and if there is problem in between, I’ll make visualization for that to, but not before. :slight_smile:

Sound like a good plan =)