BatchNode in a blocky world with custom shader

Good Morning jMonkeys,

I’m having some questions regarding the BatchNode (actually probably SimpleBatchNode).
Short overview about my situation:
My GreedyMesher currently meshes all solid blocks in one chunk together, regardless of its texture.
For this I wrote a custom shader that uses an TextureArray. Every mesh created is feeded with an index buffer and position buffer ofc, texture coords buffer (3 floats, since I need the 3rd component as index for the TextureArray), size buffer (1 float that has no information about any size but has 4 color values (r g b and sun) and the ambient occlusion value packed into it) and normal buffer (1 float, though the wiki somewhere states it should consist of 3 floats (for x, y and z) I again pack these values together into one float, its blocky, it doesnt need that many possible ‘angles’).
Quick question about feeding the normal buffer with 1 component only: that is completly legit, isn’t it? Like I dont need to feed it 3 floats, as long as my shader only expects it to be 1 float, meaning the wiki information is for using the built in shaders only and you are not necessarily forced to put 3 components, right?

And onto the BatchNode thing:
In addition to the WorldData that holds all Chunks, the Chunk that holds all Blocks and the Blocks that hold the actual information, I wanted to introduce a ChunkPackNode class that can could hold several chunks, in order to batch groups of say 4x1x4 chunks together to further reduce the amount of drawcallls. (Not mixing the view up with the model, each ChunkNodePack would just be feeded with the all geometries created for one of the chunks it’s holding and once all its chunks have been loaded it could batch them (and as soon as the players position is inside one of these ChunkPacks, that nodes geometries are unbatched to quicker update individual chunks on block placement / removement, as the player moves on further that ChunkPack is batched again)).
I first felt like SimpleBatchNode is perfect for it as the documentation states “SimpleBatchNode is recommended when you have a large number of geometries using the same material that does not require a complex scene graph structure.” which is exactly my case for the chunk geometries.
However looking into the code showed me that it would expect 3 values for normals inside BatchNodes doTransform method. That already means the built in BatchNode won’t work for me and I at least got to adjust it to only copy one value per vertex for the normals instead of 3, correct?
Besides how does the LOD stuff work for the BatchNode? For me ofc all chunks got the same number of LODs, would it include them while batching and when calling setLodLevel(x) on the SimpleBatchNode it would show the expected result?
And the last question about the BatchNodes: How memory efficient is the BatchNode? In the source it says “TODO these arrays should be allocated by chunk instead to avoid recreating them each time the batch is changed.” inside the doBatch method. Is it actually really bad or is it more like “you shouldnt rebatch too often anyways, if you only do it rarely its’ ok”?.

Thanks for your attention and thanks in advance in case you can help me get that right.
Greetings,

Samwise

If your chunks are too small then make bigger chunks.

You may find that trying to get to fewer draw calls actually ends up being more work for the GPU… because now you are drawing geometry that would have been culled before. It’s a balancing act, sure… but to me the balance is on chunk size.

Hello pspeed,
thanks for your answer!
I’m afraid you’re right and I should rather get away from a chunk height of 256 blocks (which is the map height) to a smaller chunk size height so I actually got chunks that I can cull (caves and such) in addition to the ones that are out of camera frustum anyway. And for this I probably have to rethink the class that determines whether a chunk needs to be loaded atm or not because its getting slower with 16x256x16 chunk size already (simply because there is 4 times as many chunks to check compared to 32x256x32), cutting it into chunks of size 16x32x16 would make it even slower, probably by a factor of 8. (That class is super bad implemented, would need to be rewritten sooner or later anyway, especially with several players on the map)
So thanks again, for ponting me towards that direction

And just so I can mark this topic as solved, could you confirm feeding the normals buffer with only 1 component is no problem as long as I use my own shader? (just confuses me that the wiki states the normal buffer consists of 3 components)

You can also save a bit of work and remove chunks from the scene that aren’t even visible. For my game I load all chunks as normal in a grid-array, but filter them with a FOV method that determines if the chunk is actually visible.

For example if I have a camera FOV of 65, I’ll have a FOV of 80 for my FOV method so that it keeps some chunks on the side in the scene.

The chunks will remove themselves from the scene if they are not visible and add them if they are.

I realize completely that this is in effect the same as “culling”, but instead actually saves a lot of work because the scenegraph is less than half the size it has to compute.

The method below returns true if the position is inside the specified FOV, or false if not. Set the FOV higher than your camera FOV to create a “buffer” each side.

    /**
     * Checks whether a given intruder position is inside a theoretical cone.
     * The direction of the camera is used.
     * @param pos         the world position of the cone.
     * @param intruderPos this position of the object you want to check.
     * @param angle       the angle of the field of view.
     * @return whether or not the intruder is in the field of view.
     */
    public boolean fovtest(Vector3f pos, Vector3f intruderPos, int angle) {

        float[] angles = new float[3];
        getApplication().getCamera().getRotation().toAngles(angles);

        // we only want the Y angle. X rotation causes un-necessary clipping. Z rotation rarely occurs.
        // Only the Y axis is required for the paged grid.
        // we need to set this as a specification in the method.
        Quaternion rotation = new Quaternion().fromAngles(0, angles[1], 0); // getApplication().getCamera().getRotation();
        Vector3f look = rotation.mult(Vector3f.UNIT_Z);

        // Decide how fat the cone should be, this can be a constant.  Note:
        // the angle for cosine is relative to the side and not straight ahead
        // so I will show a 30 degree example so it's clearer.  90 degrees is
        // straight ahead.
        // float threshold = FastMath.cos((90 - 35) * FastMath.DEG_TO_RAD);
        float threshold = FastMath.cos((angle) * FastMath.DEG_TO_RAD);

        Vector3f relative = intruderPos.subtract(pos).normalizeLocal();
        float dot = relative.dot(look);

        if( dot > threshold ) {
            return true; // inside view.
        }

        return false;
    }

You can test the results yourself to determine if it’s worth it. For me personally it makes a difference significant enough that I use it.

Hi jayfella,
Thanks for your idea, sounds promising and I’ll give it a try today or tomorrow!
I’m just wondering isn’t attaching and detaching quite costly? Would it be a good idea to give my scenegraph sort of an octree design so i can do that fovtest for bigger areas and only if they are inside then also check its subnodes recursively, if they are not inside, detach the whole pack. A little like the ChunkPacks I mentioned, but for fovtests instead of batching. I’m also wondering if it’s more efficient to attach / detach the ChunkPacks, but if a ChunkPack is considered inside and all its Children are checked, just set the cullmode of the children instead of attaching / detaching them too (simply because there is not too many ChunkPacks (they got a lot chunk-‘children’ though, i can attach and detach the ChunkPacks, but there is a lot of smaller chunks so i dont want to attach/detach them but just set their cullmode to always/never). Just that SafeArrayList used for a nodes children seems to copy the content once its getting changed, not sure about the buffer though.
I guess there is a lot to find out the next days :smiley: