Batching layering

Hi,
I think I found another problem with batching. Here is simple test case, as always:

import com.jme3.app.SimpleApplication;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.BatchNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.shape.Quad;
import com.pixiebots.material.UnshadedMaterial;

public class TestBatching extends SimpleApplication {
    private static final String INPUT_MAPPING_DO_ACTION = "DO_ACTION";

    private final AppActionListener actionListener = new AppActionListener();
    private final BatchNode batchNode = new BatchNode("geomNode");

    public static void main(String[] args) {
        TestBatching app = new TestBatching();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        inputManager.addMapping(INPUT_MAPPING_DO_ACTION, new KeyTrigger(KeyInput.KEY_RETURN));
        inputManager.addListener(actionListener, INPUT_MAPPING_DO_ACTION);

        BitmapText bt = new BitmapText(guiFont);
        bt.setColor(ColorRGBA.White);
        bt.setLocalTranslation(cam.getWidth() / 2, cam.getHeight() / 2, 0);
        bt.setSize(50f);
        bt.setText("HELLO World!");

        Mesh mesh = new Quad(1, 1);
        Material mat = new UnshadedMaterial(assetManager);
        mat.setColor("Color", ColorRGBA.Green);
        Geometry geom = createPanel(mesh, mat);
        geom.setLocalTranslation(cam.getWidth() / 2, 0, -1);

        batchNode.attachChild(geom);
        batchNode.attachChild(bt);
        guiNode.attachChild(batchNode);
    }

    private Geometry createPanel(Mesh mesh, Material mat) {
        Geometry geom = new Geometry("geom", mesh);
        geom.setLocalScale(cam.getWidth() / 2, cam.getHeight(), 1f);
        geom.setMaterial(mat);
        return geom;
    }
    
    private void doAction() {
        batchNode.batch();
    }

    private class AppActionListener implements ActionListener {
        public void onAction(String name, boolean value, float tpf) {
            if (!value) {
                return;
            }

            if (name.equals(INPUT_MAPPING_DO_ACTION)) {
                doAction();
            }
        }
    }
}

So first, the test scene is prepared, where some text is displayed on top of a colored quad. Everything is fine:

When ENTER (RETURN) key is pressed, the node that contains the quad and text gets batched. Since both children use different materials, nothing actually gets batched, but the result is still bad:

Cannot explain why, but the children get messed up and the text is not displayed anymore over the quad. Note that the quad’s Z coordinate of local translation is explicitly set to be behind the text. Looks like a bug to me. Does anyone know what goes wrong?

Thanks!

Could it be linked to transparency sorting?

Did you try with:

  • another quad non transparent?
  • another quad transparent?
  • optimize see if it behaves differently?

I don’t think it has anything to do with transparency. The quad is non-transparent.
I think that it is a bug, because batch node imo shouldn’t affect the non-batched geometries.

Assuming it’s a sorting issue then you need to check if the buckets changed. Because if the green is in the opaque bucket and the text is in the transparent bucket then opaque is always drawn first.

…so somehow they ended up in the same bucket if the ordering has changed.

I put some printouts in the test case and I can see that both children are in GUI queue bucket before and after the batching. Also their world translation stays the same with batching, so no problem with that also.

Ah, didn’t realize they were in the GUI bucket.

Gui bucket sorts back to front by Z… but I don’t know which Z. Check the bounding shapes of the batched objects I guess.

I checked the bounding shapes and they are the same before and after batching.
I am not able to find out what is the problem at all.

If you look at the screenshots, you can see that “Shaders (S)” changes from 2 to 4. Not sure if this has anything to do with it.

Please help. :smile:

Can you do a dump of everything in the scene before and after batching using a traverser? Include the bucket and the bounding shape in your dump.

Batching for GUI elements has to work completely different because there’s no depth in the GUI node so it has to be sorted by Z.
So BatchNode cannot be applied to GUI unfortunately.

Sure… but these two things will have two different materials so shouldn’t (and aren’t) batched together.

The order is still arbitrary.

Fundamentally the issue is that if you have object A and C with the same material and object B with another material in between them – A and C will be batched together whereas B will not be, causing C to appear behind B when it really should be in front of it.

The only way to solve this is to either use aggressive texture atlassing / arrays so that all UI elements become a single batch, or to batch by Z order, which means you have to have a small number of layers in the UI, and each layer becomes its own batch.

Yes, I’m not arguing with you…

But in this case there are what I understood to be two objects before… and two objects after batching… and the ordering is somehow different. So I wanted to drill in on it.

Batching can work fine for some UIs that don’t use transparency and have proper Z values. I’d like to know what makes these sort badly, though, as that might be a bug. Especially since in some of my own code it was moving the origins of my batched stuff and I never tracked down what was happening.

I guess the batch object is not set in the transparent bucket…
Maybe the BatchNode could have the option to care about the render bucket when gathering geoms and would put the batches in the corresponding bucket when batched.

Mhh it’ smore than that. BitmapText is not a geometry… it’s not even batched… gonna looked into it

OK I get what’s going on… it’s not a bug to me.
First BitmapTextPage (the geom behing bitmapText) is explicitely marked to never be batched. The thing is each time you change the text the mesh is changed, so it wouldn’t work with batchNode. BatchNode can handle spatial transforms but not mesh changes.

Second, your green quad is set to z = -1 but once batched, the batch position is set to z = 0. Maybe soemthing clever could be done about that… like taking the center of all batched geoms as the batch position, but it would have bad side effects IMO.
The thing is the bitmapText z is 0 so the renderer renders the geom is whatever order it finds them… and seems the bitmapText is rendered first.

So, to solve the issue in the test case, you can set the bitmapText z to 1 for example and it is displayed in front of the green quad.

Batching text would require something special IMO… it won’t work out of the box. However, any other geom in the gui node can be batched

Hmm, I underestand that bitmap text cannot be batched and this is fine. But this is not the actual problem here.

I want to emphasize what is in my opinion erroneous behaviour here:
In the test case, no batching should actually happen, since both elements have different materials. So I would expect batch() function to not do anything here. But in this case obviously batch function still does some processing, which messes the ordering.

Maybe the solution is actually really simple, prevent batching if there is only one geometry to batch?

It’s because the batch remaps the origin… which actually prevented me from using batching in one example I had as the code to recalculate my offsets and place under another node to shift it was too painful.

Ok so if I understand you correctly, even if we solve this for two different objects, it won’t be solved when something actually gets batched (three or more objects where at least two get batched)?

So the solution for GUI would kinda be to manually set the Z coordinate for batched geometries?

For guis, it may be necessary to write a custom batcher that first sorts in Z order so that your sorting doesn’t get all messed up.

At that point, the batcher can do whatever it wants with the Z of the individual meshes… however, better to make them relative to the origin without moving that origin… ie: make sure that whatever happens, world x,y,z before is the same as world x,y,z after (I actually consider this a bug in the current batching code that it changes this.)

Well actually with real depth in the gui viewport we wouldn’t have this issue…