Collision with dynamic BatchNode

Hi all.

I just encountered a trouble with Physics. I’ve gone through the tutorials twice, searched the forum, but I’ve not found any good solution for the moment.
Could anyone point me in the right direction?

Here is my problem : I have a dynamic set of small objects, all the same, I want to detect collisions with. Imagine a firing range, with hundreds of circle targets. Some of these objects may appear or disappear on a regular basis.

Adding a RigidBodyControl to each of them works nice, but the framerate suffers a lot as new target have been added. So I’ve read that we could batch these object (that don’t move) to enhance the whole performance.

The trick is that when I call the bach() function, it does not seem to add the new geometry in the batch. Removing an object works properly.
I found a protected function setNeedsFullRebatch() that JMP reports no one uses. Calling this solves the adding.

As Physics are concerned, I may add or remove objects to the BatchNode, the physical state seems not impacted. I’ve read in the tutorial that scaled was accounted for during the addition to the physical world, but that does not the trick here. The only thing I found working is removing the RigidBodyControl, rebatching the node, creating a brand new one, and then adding it back.

All of that seems to me extremely complicated. So I think I missed something. Could anyone give me some advice ?

What version of JME do you use? RC2? nightly?

You’re not supposed to call setNeedsFullRebatch, it’s used when a geometry is removed to the BatchNode. It’ shouldn’t be needed when you add a geometry.

You mention that you objects are not moving, so maybe you just need to use the GeomtryBatchFactory instead?

About physics. Just do everything as if the geometries were not batches. Add the rigidBodyControl to the geom, then add the geom to the physic psace, then add the geom to the batchNode.
Though after this you’ll have to call batch() again on the batchNode.

Look at TestBatchNodeTower in the test suite, it demonstrate how to make a batch made of plenty objects with individual physics controls.

Thanks for the advice. I will take a look at the tower demo.

As for GeometryBatchFactory, I don’t understand how you can add/remove a mesh from the batch. Should I remove the batch, the make a new one each time ?

@yang71 said: As for GeometryBatchFactory, I don't understand how you can add/remove a mesh from the batch. Should I remove the batch, the make a new one each time ?
mhh true... it's easier to manage add and remove through the batchNode.

So all your objects are static, but got added and removed continuously right?
Also you didn’t answer about what version you’re using?
The problem you have with adding geoms bothers me, so could you wrap up a test case so I check it works in nightly or fix it if it doesn’t?
Thanks

Thanks for your help @nehon. It works nicely this way.
Physics is handled very well once added to every object instead of to the batch node. It’s even slightly more efficient (well… it seems a little bit more fluid…) that my previous approach.

Objects are added / removed normally. I don’t know why it kept telling me 0 geometry batched. I will try and see if I can reproduce it on a small test-case. It’s maybe a mistake from me, as I mainly focused on the Physics debug shapes.

And… Sorry for your question about the version. I’m using the Nightly built.

As for your interpretation, it’s perfect. No move, but additions and removals all the time.

Edit : Removing worked only because I forgot removing the setNeedFullBatch from my test :((

Ok, Here is a test case.
It show really interesting things.

Let me describe how it works :

  • a big Yellow box is there as a reference : it’s not part of the batch
  • 2 small boxes at the center :
    • The pink is a node containing a box
    • The red is simply a box
      => both are batched at the init of the application
  • 2 horizontal boxes activated/deactivated by keyboard arrows left/right
    • these boxes are simple boxes
  • 2 vertical boxes activated/deactivated by keyboard arrows up/down
    • these boxes are nodes containing a box
  • Enter key calls for the batch() method

Each box has a RigidBodyControl with mass 0.

What we can see a few things :

  • Physics on boxes are lost during batching
  • Physics on nodes are kept during batching
  • Nodes cannot be removed from the batch
  • Boxes can be removed from the batch

Additionally, when we remove a box, previously removed nodes are also removed on batching…

Here is the code :
[java]
package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
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.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import java.util.logging.Level;
import java.util.logging.Logger;

public class PhysicBatchTest extends SimpleApplication {

public static void main(String[] args) {
Logger.getLogger("").setLevel(Level.WARNING);
Logger.getLogger(“com.jme3.bullet.PhysicsSpace”).setLevel(Level.ALL);
Logger.getLogger(“com.jme3.scene.BatchNode”).setLevel(Level.ALL);
PhysicBatchTest app = new PhysicBatchTest();
app.start(); // start the game
}
public BatchNode batch;
public BulletAppState physics;
protected Box b2;
protected Spatial h,b,g,d;

@Override
public void simpleInitApp() {
stateManager.attach(physics = new BulletAppState());
physics.getPhysicsSpace().enableDebug(assetManager);

Box box = new Box(1, 1, 1);
rootNode.attachChild(buildBox(ColorRGBA.Yellow, 0, 0, -3, box, false));
batch = new BatchNode();
b2 = new Box(0.1f, 0.1f, 0.1f);
batch.attachChild(buildBox(ColorRGBA.Red,-0.11f,0,0, b2, false));
batch.attachChild(buildBox(ColorRGBA.Pink,0.11f,0,0, b2, true));

rootNode.attachChild(batch);
batch.batch();

flyCam.setMoveSpeed(60);

inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_LEFT));
inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_RIGHT));
inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_UP));
inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_DOWN));
inputManager.addMapping("Enter", new KeyTrigger(KeyInput.KEY_RETURN));
inputManager.addListener(actionListener, new String[]{"Left", "Right", "Up", "Down", "Enter"});

}

private ActionListener actionListener = new ActionListener() {
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals(“Left”) && keyPressed) {
if (g == null)
batch.attachChild(g = buildBox(ColorRGBA.Red,-1.7f,0,0, b2, false));
else {
g.removeFromParent();
g = null;
}
}
if (name.equals(“Right”) && keyPressed) {
if (d == null)
batch.attachChild(d = buildBox(ColorRGBA.Red,+1.7f,0,0, b2, false));
else {
d.removeFromParent();
d = null;
}
}
if (name.equals(“Up”) && keyPressed) {
if (h == null)
batch.attachChild(h = buildBox(ColorRGBA.Red,0,+1.7f,0, b2, true));
else {
h.removeFromParent();
h = null;
}
}
if (name.equals(“Down”) && keyPressed) {
if (b == null)
batch.attachChild(b = buildBox(ColorRGBA.Red,0,-1.7f,0, b2, true));
else {
b.removeFromParent();
b = null;
}
}
if (name.equals(“Enter”) && keyPressed) {
batch.batch();
}
}
};

protected Spatial buildBox(ColorRGBA col, float x, float y, float z, Box shape, boolean dummy) {
RigidBodyControl phy;
Geometry geom = new Geometry(“Box”, shape);
Material mat2 = new Material(assetManager,“Common/MatDefs/Misc/Unshaded.j3md”);
mat2.setColor(“Color”, col);
geom.setMaterial(mat2);
geom.setLocalTranslation(x,y,z);
if (dummy) {
Node n = new Node(“dummy”);
n.attachChild(geom);
n.addControl(phy = new RigidBodyControl(0));
physics.getPhysicsSpace().add(phy);
return n;
} else {
geom.addControl(phy = new RigidBodyControl(0));
physics.getPhysicsSpace().add(phy);
return geom;
}
}
}
[/java]

Ok, I think I have found the reason why Node are not unbatched.
The trick is that Node children are batched individually. The node itself is never batched.
If we check the setParent method, we can see it’s very different if we are in Geomerty or in Node.

To solve this problem I propose adding the following to BatchNode :
[java]
@Override
public Spatial detachChildAt(int index) {
Spatial s = super.detachChildAt(index);
if (s instanceof Node) {
if (isBatchedNode((Node)s)) {
setNeedsFullRebatch(true);
}
}
return s;
}

protected boolean isBatchedNode(Node n) {
  Spatial s = children.get(0);
  while (s instanceof Node) {
    s = ((Node)s).getChild(0); // descend into the child node
  }
  if (s instanceof Geometry) {
    return ((Geometry)s).isBatched();
  } else
    return false;
}

[/java]

The isBatchedNode utility function is only there to check if the node contains some batched geometry.
If a node can contain something which is not a Node nor a Geometry, descending into first child may be not enough… We should then iterate over all children until a geometry is found.

I’m still looking into the physics trouble… No idea on why it doesn’t work on Geometries yet.

I’m totally lost in physics. What I have discovered :

  • if I manually set batched geometries to Inherit CullHint, the physics debug shapes appear
    (Nodes are not set to Never, but their children are… But the node has the control… so the physics occur)
  • In PhysicsSpace.update : all the objects are there, even if culled out.
    => it calls for DiscreteDynamicsWorld.stepSimulation

@normen : you are registered as the author of the PhysicsSpace. Could you indicate me where I could find the source code of DiscreteDynamicsWorld ? I cannot find it in the SDK jars, nor in the svn. If its access is restricted, do you know who could investigate further into this ?

My gratuitous expectation is that there is somewhere a test “if ! culled do physics”. If it exists, it should be replaced by something like “if ! culled nor (batched && batchNode ! culled) do physics”.