[SOLVED] CCD Collision + collision group = problems?

Below is a small code showing the problem.

I used the TestCDD example and modified it a little bit to include collision groups.

As a refresher, from the source documentation (setCollisionGroup):

Groups are represented by integer bit masks with exactly 1 bit set.
 Pre-made variables are available in PhysicsCollisionObject. By default, physics objects are in COLLISION_GROUP_01.
 Two objects can collide only if one of them has the collisionGroup of the other in its collideWithGroups set.

In my example there is:

  • a wall and a ground that are in group 1 and collide with objects of group 2
  • a ball (made by makeBallCCD1): no collision group specified, so ball is in group 1 and collide with group 1
  • another ball (made by makeBallCCD2): collision group is 3, and collide with group 4 (nothing in that group)
  • both ball use CCD

what i would expect:

  • wall/ground should not collide with any ball (ball1 and ball2 are not in collision group 2)
  • ball1 should collide with wall/ground (wall/ground are in collision group 1) (how does it work ? which is taken into account ?)
  • ball2 should definitely not collide with wall/ground (nor with ball1)

What i see:

  • ball2 collide with wall/ground but do not roll as ball1 does …
  • if you shoot a ball1 so that the ball hit the wall straight ahead and without much velocity you can throw a ball2 at the first ball1 and the ball2 will collide with ball1 …
  • after 2 or 3 rebound, ball2 will go trough the ground as if it remembered it was not supposed to collide !
  • ball1 collide happily with ground and wall (as intended ?)

i used jme3-bullet-3.3.0-beta1, with 3.2.4-stable,ball2 will get through wall/ground most of the time, but at some angle there will still be some collision but with the wrong normal (which is abnormal :slight_smile: ), also ball2 will still collide with ball1.

(i also opened an issue on github)

FILE TestCCD.java:

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.scene.Geometry;

public class TestCCD extends SimpleApplication implements ActionListener {
   BulletAppState bas;
   @Override
   public void simpleInitApp() {
       bas = new BulletAppState();
       bas.setDebugEnabled(true);

      Tools.initGlobals(rootNode, bas, assetManager, getCamera());
       stateManager.attach(bas);

       Tools.makeWall();
       Tools.makeGround();

       flyCam.setEnabled(true);
       flyCam.setMoveSpeed(40f);

       inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
       inputManager.addMapping("shoot2", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
       inputManager.addListener(this, "shoot");
       inputManager.addListener(this, "shoot2");

   }

   @Override
   public void onAction(String binding, boolean value, float tpf) {
       if (binding.equals("shoot") && !value) {
           Tools.makeBallCCD1();
       } else if (binding.equals("shoot2") && !value) {
           Tools.makeBallCCD2();
       }
   }

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

File: Tools.java

import com.jme3.asset.AssetManager;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.collision.shapes.MeshCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;

public class Tools {

    static Node rootNode;
    static BulletAppState bullet;
    static AssetManager am;
    static Sphere ball;
    static SphereCollisionShape ballCS;
    static Camera cam;

    static void initGlobals(Node root, BulletAppState bas, AssetManager am, Camera camera) {
        rootNode = root;
        bullet = bas;
        Tools.am = am;
        cam = camera;


        ball = new Sphere(32, 32, 0.4f, true, false);
        ball.setTextureMode(Sphere.TextureMode.Projected);
        ballCS = new SphereCollisionShape(0.5f);

    }

    static Node createCollisionBox(String name, Vector3f position, Vector3f extend) {
        Node box = new Node();
        box.setLocalTranslation(position);

        RigidBodyControl boxRBC = new RigidBodyControl(new MeshCollisionShape(new Box(extend.x, extend.y, extend.z)), 0f);
        boxRBC.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_01);
        boxRBC.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);

        box.addControl(boxRBC);

        rootNode.attachChild(box);
        bullet.getPhysicsSpace().add(box);

        return box;
    }

    static Node makeWall() {
        return createCollisionBox("Wall", new Vector3f(2.5f, 0f, 0f), new Vector3f(4f, 5f, 0.0f));
    }

    static Node makeGround() {
        return createCollisionBox("Ground", new Vector3f(0f, -6f, 0f), new Vector3f(100f, 1f, 100f));
    }

    static void makeBallCCD1() {
        Geometry ballGeom = new Geometry("ball1", ball);

        Material mat = new Material(am, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.getAdditionalRenderState().setWireframe(true);
        mat.setColor("Color", ColorRGBA.Green);

        ballGeom.setMaterial(mat);
        ballGeom.setName("ball1");
        ballGeom.setLocalTranslation(cam.getLocation());
        ballGeom.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);

        ballGeom.addControl(new RigidBodyControl(ballCS, 1));
        ballGeom.getControl(RigidBodyControl.class).setCcdMotionThreshold(0.01f);
        ballGeom.getControl(RigidBodyControl.class).setLinearVelocity(cam.getDirection().mult(40));
        rootNode.attachChild(ballGeom);
        bullet.getPhysicsSpace().add(ballGeom);
    }

    static void makeBallCCD2() {
        Geometry ballGeom = new Geometry("ball2", ball);

        Material mat2 = new Material(am, "Common/MatDefs/Misc/Unshaded.j3md");
        mat2.getAdditionalRenderState().setWireframe(true);
        mat2.setColor("Color", ColorRGBA.Red);

        ballGeom.setMaterial(mat2);
        ballGeom.setName("ball2");
        ballGeom.setLocalTranslation(cam.getLocation());
        ballGeom.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);

        RigidBodyControl rbc = new RigidBodyControl(ballCS, 1);
        rbc.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_03);
        rbc.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_04);

        ballGeom.addControl(rbc);

        ballGeom.getControl(RigidBodyControl.class).setCcdMotionThreshold(0.01f);

        ballGeom.getControl(RigidBodyControl.class).setLinearVelocity(cam.getDirection().mult(40));
        rootNode.attachChild(ballGeom);
        bullet.getPhysicsSpace().add(ballGeom);
    }
}
1 Like

There’s a lot going on here. The first thing I noticed is that CCD is disabled since the balls all have sweptSphereRadius=0. That might explain why fast-moving balls behave strangely during collisions.

No, from my tests and from the documentation, the CCD is activated by setCcdMotionThreshold:

From the documentation:

setCcdMotionThreshold(0f)

Sets the amount of motion that has to happen in one physics tick to trigger the continuous motion detection in moving objects that push one another. This avoids the problem of fast objects moving through other objects. Set to zero to disable (default).

And you can see that on the TestCCD.java included as an example in the jmonkey sources: https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestCcd.java

In fact in the link above, no swept sphere size is even set (which should have been set for better documentation at least ?)

Because i wanted to make a smallish example to the problem i reused parts of the code above and end up forgetting the swept sphere radius exactly as it was done in the example :slight_smile:

So yes, you are right that i should have set it to a positive value for better physics result, but without it the CCD is still active and the collision group are still not working as intended.

To be sure, I just tried to set swept sphere radius to 0.5f in my code, and saw no effect.

ballGeom.getControl(RigidBodyControl.class).setCcdMotionThreshold(0.01f);
ballGeom.getControl(RigidBodyControl.class).setCcdSweptSphereRadius(0.5f);

I also deactivated the line:

ballGeom.getControl(RigidBodyControl.class).setCcdMotionThreshold(0.01f);

and there the CCD is deactivated and the collision groups work as intended…

I could be wrong here, but doesn’t the RigidBodyControl require that it be added to the PhysicsSpace before setting things like CCD and CollisionGroups?
I would imagine that in the case where one RBC was being moved from one PhysicsSpace to another (say from global to local, like when jumping onto a moving train) that the PhysicsSpace would reset these values on the RBC in question, rather than rely on values set in a previous (uncoupled) PhysicsSpace.

Again, I could be wrong here… but the ordering of these calls would be something easy to rule out.

Good thinking …

First, i tried to add ballGeom to physicsSpace before creating the RigidBodyControl:

        bullet.getPhysicsSpace().add(ballGeom);

        RigidBodyControl rbc = new RigidBodyControl(ballCS, 1);
        ballGeom.addControl(rbc);

       [... rest of CCD and collision group initialization ... ]

This is clearly wrong: the physics engine suppose the ball geometry is the collision shape and won’t move the ball at all, indicating that adding the control
after adding the ball to the space means the space have no idea about the control.

So i tried adding the ball to the physics space after creating the rigid body control and attaching it to the ball:

        RigidBodyControl rbc = new RigidBodyControl(ballCS, 1);
        ballGeom.addControl(rbc);

        bullet.getPhysicsSpace().add(ballGeom);

       [... rest of CCD and collision group initialization ... ]

This time the physics recognize the rigid body control, but the CDD and collision group are still not working well together: i get the same result as before.

I’ve come up with a fix for issue 1283 that involves a 16-line patch to the Bullet C++ code. I’m testing it in Minie (my alternative physics library) right now.

3 Likes

I believe switching to MInie v1.4.1 will resolve your issue. Please try it and let me know the outcome:

https://store.jmonkeyengine.org/38308161-c3cf-4e23-8754-528ca8387c11

The fix should eventually reach jme3-bullet, but that may take months or years.

2 Likes

Is your trouble resolved?

Sorry, I didn’t have much time to test it.

This seems to work as intended with your library.

A small note: your page on the jmonkey store says: implementation 'jme3utilities:Minie:1.4.1for32' as the gradle dependencies, but really it should be implementation 'com.github.stephengold:Minie:1.4.1for32' (or at least that is what worked for me …)

I will consider using your library instead of bullet in my big game project :slight_smile:

2 Likes

Thanks for informing me of the error in my store page. I’ve submitted a correction.

And thanks again for submitting issue 1283.

1 Like