BatchNode, IndexOutOfBoundsException

Hello Everyone,

Been having some problems with BatchNode, first the stack trace.

java.lang.IndexOutOfBoundsException
	at java.nio.Buffer.checkBounds(Buffer.java:567) ~[?:1.8.0_111]
	at java.nio.DirectFloatBufferU.get(DirectFloatBufferU.java:265) ~[?:1.8.0_111]
	at com.jme3.scene.BatchNode.doTransforms(BatchNode.java:547) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.BatchNode.updateSubBatch(BatchNode.java:152) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.BatchNode.onTransformChange(BatchNode.java:104) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.Geometry.updateWorldTransforms(Geometry.java:326) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.Spatial.updateGeometricState(Spatial.java:905) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.Node.updateGeometricState(Node.java:271) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.Node.updateGeometricState(Node.java:271) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.Node.updateGeometricState(Node.java:271) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.Node.updateGeometricState(Node.java:271) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.Node.updateGeometricState(Node.java:271) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.Node.updateGeometricState(Node.java:271) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.scene.Node.updateGeometricState(Node.java:271) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:245) ~[jme3-core-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151) ~[jme3-lwjgl-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:197) ~[jme3-lwjgl-3.3.0-SNAPSHOT.jar:3.3-6587]
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:232) ~[jme3-lwjgl-3.3.0-SNAPSHOT.jar:3.3-6587]
	at java.lang.Thread.run(Thread.java:745) [?:1.8.0_111]

Background:

I have multiple different tree geometries that are batched together in a batch node. Trees are added and removed fairly regularly. There is also multiple materials being used, so there is a minimum of 2 subbatchs in the tree BatchNode.

The trees all have vertex paint on them rather than textures so they batch together nicely. Some of the trees have more vertices than others do. Which has caused a problem.

The issue:

        bindBufPos.get(tmpFloat, 0, length);

tmpFloat is too small, when this is executed it throws an exception because its too small.

In the function doBatch() what happens is that when a full rebatch is not required, the vertex count is reset to zero using maxVertCount, however when its a partial rebatch this means that there has to be a geometry in the incoming group that has more vertices than whats already existing. Sometimes in my case this does not occur and then tmpFloat does not get set to a large enough size.

I have fixed my problem by changing line 200 to this

        if (matMap.size() > 0 && needsFullRebatch) {
            maxVertCount = 0;
        }

however I am not sure if this will have a bad effect on other things.

I tried to recreate my problem in a single class for you guys to look at however for some reason the tmpFloat size was not being reduced when setting it at line 249, I’m not sure why that was happening and gave up trying to replicate my problem that way.

If you have any questions please let me know.

Thanks

I have a suggestion: Take your debugger and conditional breakpoints (that way you can only make it stop when the case is really faulty) and then you may be able to spot something.
Apart from that you could always use the “Find Usage” Tool to see where it might be getting wrong.

I just copied BatchNode into my project, renamed it and made the edits I noted above. Game doesn’t crash anymore.

Thanks for the suggestions though.

It’s good that your change works in your project but if you ever wonder why we can’t apply it to JME directly, the above is a good reason.

There is a high probability that there is something your code is doing that is causing this and it may not really be a problem in JME (just bad error checking on our part). And even if that’s not true, we don’t understand the specific setup that triggers it and so can’t annotate the change properly. Someone will come along later and remove it as unnecessary.

I wasn’t curious about why you can’t apply it to JME directly. I know you guys well enough to know how you behave. I have been a part of this community for long enough, and have lurked for a very long time before that.

It is a problem in JME. I know that. After many hours of debugging, I simply applied the fix.

I would love your expertise on a related problem, you have a much deeper understanding of how the JVM/java works than I do.

tmpFloat = new float[2700]; <-- array will be able to hold 2700 items

// some time later

tmpFloat = new float[900]; <-- array will still be able to hold 2700 items

// only sometimes it actually sets it to hold 900 items. 
// I assume this is because of some kind of optimization or garbage collection.

if you can explain the above, I can give you a single class test case.

This post was so that when someone else eventually has the same problem (which they will, I can guarantee you that), they will find the solution on the forums.

I mean of course the simple solution is to never batch items with different vertex counts. Or don’t batch items that have different materials in the same batchnode. Or don’t use batchnode at all I guess?

Thanks

The array code you posted is not ambiguous at all, the first array will always have 2700 elements and the second array will always have 900 elements. I would be surprised if you can provide a test case that proves otherwise. What is likely happening is you are keeping a reference to the original array and using that when you think you aren’t.

@8Keep123 wish granted!!!

The debugger is not necessarily your ally in these cases as it can lie, I guess. The underlying byte code can be reordered for a variety of reasons. At least that’s my only explanation for your screen shot.

I guarantee you that after:
tmpFloat = new float[900];
…that the array will only hold 900 values.

Put a System.out.println(“test:” + tmpFloat.length) after it and you will see.

Note: that in 20 years of Java development, I’ve opened the debugger probably 4 times… and each time I was disappointed.

Also in your screenshot maxVertCount in the bottom shows a value of 36864. 36864*3 = 110592, so it’s clear that’s what the value is when it does the calculation. I’m not sure what the discrepancy is bettwen the mouse hover and that value is, because I don’t use that debugger

Can you put some printlns in for the length of the arrays and maxVertCount and see what it says?

Regarding this, hopefully someone can come up with a test case someday that exhibits the problem… else it will always look app-specific and will be tough to fix.

It may be a problem earlier on in JME code, for example.