GeometryBatchFactory.optimize fails on Android Nougat (24+)


I recently came across what I think is a bug in GeometryBatchFactory.optimize() that applies only to Android Nougat (API 24+).
The following code shows a green cube on API <= 23 but only the red cube behind it on API 24+. It doesn’t crash, but the node that optimize() produces is not visible.
I was testing on two physical devices - a Nexus 7 running API 23 and a Nexus 6P running API 24. I also tried on emulators for API 23, 24, 25 with the same result - only API 23 works as expected.

public void simpleInitApp() {
    Geometry geom1 = new Geometry("Box", new Box(1, 1, 1));
    Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat1.setColor("Color", ColorRGBA.Red);

    Geometry geom2 = new Geometry("Box", new Box(1, 1, 1));
    Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat2.setColor("Color", ColorRGBA.Green);

    Node node1 = new Node();
    node1.setLocalTranslation(0f, 0f, -1f);

    Node node2 = new Node();


The fix for me was just to check the android SDK version and skip the call to optimize() if running on Nougat.
That seemed to work ok, so this isn’t really a request for troubleshooting so much as just general info in case someone else has the same problem.

I did try to poke around to find the root cause, and I think it’s something to do with the mesh from GeometryBatchFactory.mergeGeometries, but I didn’t get any further than that.


Are you sure that issue is in the batching? Green cube hides the red one with default camera location and view direction. Try to look at cubes from the side:

cam.setLocation(new Vector3f(-5f, 0, 0)); cam.lookAtDirection(Vector3f.UNIT_X, Vector3f.UNIT_Y);

But from his description, it’s the green cube that disappears on newer android… and only if he calls optimize().

I’m pretty sure camera placement has nothing to do with it.

You are right. But why I asked is that I noticed that both cubes intersected (visible z-fighting), and also it would be more logical to show cubes both visible from the start. Now I think OP created an example following next thinking: green cube is closer to the camera and hides the red one, which is behind the green one, and if optimize() doesn’t work, then green cube will disappear and a red cube will become visible.

As I understand optimize() gathers all the geometries in a node and creates a single cumulative batched geometry with a single mesh (or multiple, if geometries have different materials). This cumulative geometry will be added to the node, while original geometries will be removed (what OP sees). I agree with OP that there might be some issue inside GeometryBatchFactory.mergeGeometries, which is nice to refactor, as it is a quite big method.

Sorry, it’s maybe not the best example in that it’s more complex than necessary and the two cubes intersect.
To clarify, the second red cube is not really required - without it on Nougat you just get nothing at all.
I just included it when I was testing as a sanity check to make sure that a normal node could render as expected and it was definitely the call to optimize that was the cause.

What if you merge the geometry on the desktop, save the merged geom as j3O then use this on android?
It would help to narrow down the issue.

Ok I gave it a go, but I’m not really used to the SDK so I’m not sure I did it right. This is what I tried:

  1. Create a blender file with a single icosphere and nothing else.
  2. Create a new JME basic game project.
  3. Add the blender file to models, then right-click => convert to j3o binary
  4. Right-click Scenes => New => Empty JME3 Scene
  5. View scene in scene composer, add j3o to scene.
  6. Save the scene and copy the resulting j3o over to my template android app as ‘newScene.j3o’
  7. Right-click root node in scene explorer => tools => batch geometry
  8. Copy the resultant j3o over to my template android app as ‘newSceneBatched.j3o’

Then I ran the following on an emulator running API 25 and my Nexus6P running API 24.
Both worked fine, and both objects appeared.
I also then tried batching a scene with more than one object, and it still worked as expected.

public void simpleInitApp() {
    Spatial spatial1 = assetManager.loadModel("Models/newScene.j3o");
    Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat1.setColor("Color", ColorRGBA.Red);
    spatial1.setLocalTranslation(-1f, 0f, 0f);

    Spatial spatial2 = assetManager.loadModel("Models/newSceneBatched.j3o");
    Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat2.setColor("Color", ColorRGBA.Green);
    spatial2.setLocalTranslation(1f, 0f, 0f);