Recast4J: Print the Navmesh

Dear everybody,
I (prolly) was able to create a Navmesh using Recast4J (GitHub - MeFisto94/jme3-recast4j: Abstractional Bindings to use recast4j (a java only port of recast+detour) in jMonkeyEngine 3). Yay!
Now, I would like to print that into the scene, so I see what the mesh looks like.
The problem is that the navmesh R4J creates is not of a (sub)type mesh.
I can access the meshData (see example here: jme3-recast4j/RecastTest.java at master · MeFisto94/jme3-recast4j · GitHub )
just fine.

I can use the meshdata to get a list of ints that looks like this:

/**
     * The detail mesh's triangles. [(vertA, vertB, vertC) * MeshHeader::detailTriCount] See DetailTriEdgeFlags and
     * NavMesh::getDetailTriEdgeFlags.
     */
    public int[] detailTris;

But I get 376 ints.
Observe that 376 mod 3 != 0, so there is something wrong there.

I would like to print the NavMesh to debug.
I tried printing a line like so:

//how to draw a line taken from here:https://gamedev.stackexchange.com/questions/81505/how-do-i-draw-a-line-using-jme3-scene-shape-line-in-jmonkeyengine-3-0/81510#81510
		Line l = new Line(new Vector3f(1,2,3),new Vector3f(4,5,6));
		Geometry  g = new Geometry("lol", l);
		rootNode.attachChild(g);

However, no line appears.
Can anyone tell me

  • what I did wrong to prevent the line from appearing
  • what I got wrong for the list of triangle coords not to be divisible by three
  • if I’m conceptually missing something major here

Best,
j

you probably need material for it.

about below: its made based on recast4j not jme-recast4j

but maybe it will help you somehow (ugly code but works):

Point key = entry.getKey();
                Material lineMat = new Material(Inversion.app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
                if(closeTilePos != null && key.x == closeTilePos[0] && key.y == closeTilePos[1]){
                    lineMat.setColor("Color",ColorRGBA.Blue);
                } else {
                    lineMat.setColor("Color",ColorRGBA.White);
                }
                //System.out.println("Tile["+key.x+","+key.y+"]");
                RecastBuilder.RecastBuilderResult tile = entry.getValue();
                if(navMeshTileRefs.containsKey(key)){
                    PolyMesh pmesh = tile.getMesh();
                    PolyMeshDetail dmesh = tile.getMeshDetail();
                    if (pmesh.npolys != 0) {
                        System.out.println("debugRecastMesh verts" + pmesh.nverts);
                        Vector3f[] vectors = new Vector3f[dmesh.nverts];
                        for (int v = 0; v < pmesh.nverts; v++) {
                            vectors[v] = new Vector3f((pmesh.bmin[0] + pmesh.verts[v * 3] * pmesh.cs), (pmesh.bmin[1] + pmesh.verts[v * 3 + 1] * pmesh.ch), (pmesh.bmin[2] + pmesh.verts[v * 3 + 2] * pmesh.cs));
                        }
                        for (int i = 0; i < pmesh.npolys; i++) {
                            int p = i * pmesh.nvp * 2;
                            //face
                            Vector3f firstVector = null;
                            Vector3f lastVector = null;
                            for (int j = 0; j < pmesh.nvp; ++j) {
                                int v = pmesh.polys[p + j];
                                if (v == RecastConstants.RC_MESH_NULL_IDX) {
                                    break;
                                }
                                if(firstVector == null){
                                    firstVector = vectors[v];
                                }
                                if(lastVector != null){
                                    Line line = new Line(lastVector.add(offset), vectors[v].add(offset));
                                    Geometry lineGeom = new Geometry("debugLine", line);
                                    lineGeom.setMaterial(lineMat);
                                    //lineGeom.setQueueBucket(RenderQueue.Bucket.Translucent);
                                    debugNode.attachChild(lineGeom);
                                }
                                lastVector = vectors[v];
                            }
                            if(firstVector != null && lastVector != null){
                                Line line = new Line(lastVector.add(offset), firstVector.add(offset));
//                                System.out.println("line");
//                                System.out.println(lastVector.add(offset));
//                                System.out.println(firstVector.add(offset));
                                Geometry lineGeom = new Geometry("debugLine", line);
                                lineGeom.setMaterial(lineMat);
                                //lineGeom.setQueueBucket(RenderQueue.Bucket.Translucent);
                                debugNode.attachChild(lineGeom);
                            }
                        }
                    }
                }

This is something that will work for both.

    /**
     * Displays a debug mesh based off the area type of the poly.
     * 
     * @param meshData MeshData to process.
     * @param wireFrame display as solid or wire frame. 
     */
    private void showDebugByArea(MeshData meshData, boolean wireFrame) {
        sortVertsByArea(meshData, POLYAREA_TYPE_GROUND, wireFrame);
        sortVertsByArea(meshData, POLYAREA_TYPE_WATER, wireFrame);
        sortVertsByArea(meshData, POLYAREA_TYPE_ROAD, wireFrame);        
        sortVertsByArea(meshData, POLYAREA_TYPE_DOOR, wireFrame);       
        sortVertsByArea(meshData, POLYAREA_TYPE_GRASS, wireFrame);       
        sortVertsByArea(meshData, POLYAREA_TYPE_JUMP, wireFrame);        
    }
    
    /**
     * Sorts the vertices of MeshData, based off the area type of a polygon, and 
     * creates one mesh with geometry and material and adds it to the root node.
     * 
     * @param meshData MeshData to parse.
     * @param areaType The area type to sort the vertices by.
     * @param wireFrame Display mesh as solid or wire frame.
     */
    private void sortVertsByArea(MeshData meshData, int areaType, boolean wireFrame) {
        
        ArrayList<Float> listVerts = new ArrayList<>();
        
        /**
         * If the poly area type equals the supplied area type, add vertice to 
         * listVerts. 
         */
        for (Poly p: meshData.polys) {            
            if (p.getArea()== areaType) {
                for (int idx: p.verts) {
                    //Triangle so idx + 0-2.
                    float vertX = meshData.verts[idx * 3];
                    listVerts.add(vertX);
                    float vertY = meshData.verts[idx * 3 + 1];
                    listVerts.add(vertY);
                    float vertZ = meshData.verts[idx * 3 + 2];
                    listVerts.add(vertZ);
                }
            }
        }
        
        // If the list is empty, do nothing.
        if (!listVerts.isEmpty()) {
            //Prepare to add found verts from listVerts.
            float[] verts = new float[listVerts.size()];
            
            //Populate the verts array.
            for (int i = 0; i < verts.length; i++) {
                verts[i] = listVerts.get(i);
            }
            
            //Create the mesh FloatBuffer.
            FloatBuffer floatBuffer = BufferUtils.createFloatBuffer(verts);

            /**
             * As always, there are three vertices per index so set size 
             * accordingly.
             */
            int[] indexes = new int[verts.length/3];
            
            /**
             * Since we populated the listVerts by order found, indices will be
             * in order from 0 to verts.length -1. 
             */
            for(int i = 0; i < indexes.length; i++) {
                indexes[i] = i;
            }  
            
            //Create the index buffer.
            IntBuffer indexBuffer = BufferUtils.createIntBuffer(indexes);

            //Prepare to set vertex colors based off area type.
            int colorIndex = 0;
            //Create the float array for the color buffer.
            float[] colorArray = new float[indexes.length * 4];             

            //Populate the colorArray based off area type.
            for (int i = 0; i < indexes.length; i++) {
                colorArray[colorIndex++]= areaToCol(areaType).getRed();
                colorArray[colorIndex++]= areaToCol(areaType).getGreen();
                colorArray[colorIndex++]= areaToCol(areaType).getBlue();
                colorArray[colorIndex++]= 1.0f;
            }
            
            //Set the buffers for the mesh.
            Mesh mesh = new Mesh();
            mesh.setBuffer(VertexBuffer.Type.Position, 3, floatBuffer);
            mesh.setBuffer(VertexBuffer.Type.Index, 3, indexBuffer);
            mesh.setBuffer(VertexBuffer.Type.Color, 4, colorArray);
            mesh.updateBound();

            //Build the geometry for the mesh.
            Geometry geo = new Geometry ("ColoredMesh", mesh); 
            Material mat = new Material(getApplication().getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
            mat.setBoolean("VertexColor", true);
        
            //Set wireframe or solid.
            mat.getAdditionalRenderState().setWireframe(wireFrame);
            geo.setMaterial(mat);
            //Move to just above surface.
            geo.move(0f, 0.125f, 0f);

            //Add to root node.
            ((SimpleApplication) getApplication()).getRootNode().attachChild(geo);
        } 
        
    } 
        
    /**
     * Creates a color based off the area type.
     * 
     * @param area The area color desired.
     * @return A RGBA color based off the supplied area type.
     */
    private ColorRGBA areaToCol(int area) {
        
        //Ground (1): light blue
        if (area == POLYAREA_TYPE_GROUND) {
            return new ColorRGBA(0.0f, 0.75f, 1.0f, 1.0f);
        }
        //Water (2): blue
        else if (area == POLYAREA_TYPE_WATER) {
            return ColorRGBA.Blue;
        }
        //Road (3): brown
        else if (area == POLYAREA_TYPE_ROAD) {
            return new ColorRGBA(0.2f, 0.08f, 0.05f, 1);
        }
        //Door (4): cyan
        else if (area == POLYAREA_TYPE_DOOR) {
            return ColorRGBA.Magenta;
        }
        //Grass (5): green
        else if (area == POLYAREA_TYPE_GRASS) {
            return ColorRGBA.Green;
        }
        //Jump (6): yellow
        else if (area == POLYAREA_TYPE_JUMP) {
            return ColorRGBA.Yellow;
        }
        //Unexpected : red
        else {
            return ColorRGBA.Red;
        }
}