Best way to check collision between two meshes

Hi !

I have to check collision between two meshes BEFORE creating them in the scene. I’ve searched for a collision code for spatials but jMokey will manage it only between a mesh and a simple object.

So I’ve looked into jBullet, especially with GhostControl that seems to be perfect for the job.

The principle I tried was :

  • create a compoud/hull collision shape for each spatial,
  • create ghost controls with these shapes,
  • attach controls to the spatials and to a newlly created physic space,
  • update the space immediatly for it to compute collisions,
  • finally check the collision and destroy the space.

But i’ve failed. It results in a collision whatever the spatial I create. I’ve olso a warning :

warning CollisionDispatcher.needsCollision: static-static collision!

Here is the code :

public boolean areColliding(Spatial s1, Spatial s2){
	PhysicsSpace space = new PhysicsSpace();
	
	GhostControl ghost1 = new GhostControl(getCollisionShape(s1));
	ghost1.setSpatial(s1);
	space.add(ghost1);

	GhostControl ghost2 = new GhostControl(getCollisionShape(s2));
	ghost2.setSpatial(s2);
	ghost2.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
	space.add(ghost2);

	space.update(0);
	
	boolean collision = ghost1.getOverlappingCount() > 0;
	
	space.remove(ghost1);
	space.remove(ghost2);
	return collision; 
}

private CompoundCollisionShape getCollisionShape(Spatial s){
    // Here we are searching for geometries inside the spatial, create a hull
    // shape for each, and add thoses to a compound shape.
	CompoundCollisionShape res = new CompoundCollisionShape();
	if(s instanceof Node)
		for(Spatial child : ((Node)s).getChildren())
			if(child instanceof Geometry)
				res.addChildShape(new HullCollisionShape(((Geometry)child).getMesh()), Vector3f.ZERO);
	return res;
}

Thanks for your time !

Ben

If I weren’t doing a physics-based game, I would stay away from ghostControl & Co.

I’d rather use http://wiki.jmonkeyengine.org/doku.php/jme3:advanced:collision_and_intersection

Remember that you can put collidable Geometry and cull it away so that they aren’t rendered, thus getting the same functionality of GhostControl.

Thanks for the anwer @pesegato. It was my first try but as you may read on the page you linked :

An important restriction is that you can only collide geometry vs bounding volumes or rays. (This means for example that a must be of Type Node or Geometry and b respectively of Type BoundingBox, BoundingSphere or Ray.)

Note: I don’t think bullet does mesh->mesh collisions either. It’s sloooooow.

Others can correct me if I’m wrong, but I thought it only did simple-shapes/composite-simple-shapes against static meshes.

I confirm :

MeshCollisionShape […] Limitations: Collisions between two mesh-accurate shapes cannot be detected, only non-mesh shapes can collide with this shape.

But If i’m correctly understanding the wiki page talking about collision shapes, I would be able to find collisions between hull collision shapes (or compound shapes made of many hulls in may case), which is what I’m trying to do.

Yes, hull vs static meshes (and ofc hull vs hull) works.

In fact bullet can do mesh vs mesh dynamic with gimpactshapes, but it is really not useable for games as it is extremly slow (and hence not added to the binding for jme).

Splitting you mesh into a few hullshapes and putting all into a compoundshape is way better.

Static meshes are another thing, they have internal acceleration structures and are pretty fast.

So, anybody know what’s wrong with my code?

Maybe the second parameter in the HullCollisionShape init?

new HullCollisionShape(mySubGeometry.getMesh()), Vector3f.ZERO);

Maybe the ephemeral physic space?

I’ve checked visually that the spatials where not colliding. But I haven’t found any way to ckeck the ghosts or the collision shapes. Is there a way to create a mesh from a collsion shape to draw it? The physic debug mode seems depreciated and didn’t work.

The physics debug mode is enabled via BulletAppState.setDebugEnabled(), if you never give the physics access to the rendering loop it can obviously not render anything. You could try and use the sweep test to get immediate collision results but you need to actually sweep the testing shape and have the other shape in the physics space as a RigidBody - ghost collisions only work for the axis aligned bounding box (aabb).

What do you actually want to do? I am wondering if simply using bounding box collisions (from jME’s collision system) won’t be enough to e.g. place NPCs or whatever so they don’t slip into each other.

What I am trying to achieve is to place assets on the scene with a procedural engine (more info). I want to enhance the placing by testing collision between candidate asset and already placed ones. So for each candidate, I create two spatials and I want to check the collision between meshes.

Note : the engine has no access to the scene drawing and the assets are managed by jBullet only for this test. After that, assets have a very simple bounding circle to manage collision. So this test must be ephemeral.

Indeed, I’ve been heckled by this point while testing it, but I haven’t found more information. How exactly do you give access to the rendering loop?

I know nothing about the “sweep test”. Is my approach of creating a new local and ephemeral physics space wrong?

Thank you very much for your time.

Take a look at PhysicsSpace.sweepTest and the BulletDebugAppState

Thanks ! Digging into this, I have found the pretty efficient, simple and easy to implement DebugShapeFactory, which can create a mesh from a collision shape.

This allowed me to understand the issue : it was in the creation of the compound collision shape.

private CompoundCollisionShape getCollisionShape(Spatial s){
    CompoundCollisionShape res = new CompoundCollisionShape();
    if(s instanceof Node)
	for(Spatial child : ((Node)s).getChildren())
		if(child instanceof Geometry)
			res.addChildShape(new HullCollisionShape(((Geometry)child).getMesh()), Vector3f.ZERO);
    return res;
}

The hull collision shape seems to be created from the spatial whatever its current transform. In my case, the scale was creating giant shapes that where obviously colliding.

Now I have to understand Why I have so much collisions that are not. @Normen you talked about a limitation to AABB for the Ghost Control but I can’t find precise documentation. Could you tell me more? I will try the sweep test approach with rigid body control tomorrow.

Thanks for the help !

Ghost objects can only check collisions with their AABB, whats unclear about that?

Hi !

Still stuck to my collision problem. I’ve used a RigidBodyControl and a sweep test and it’s very fine… most of the time !

I have undetected collisions and I can’t understand why. For exemple here (the blue lines ensure that the connected objects were sweep tested) the two shapes are collinding but jBullet doesn’t seem to detect it.

Strange think is that it is perfectly working most of the time, avoiding collisions and placing things closely from each other, as desired. I don’t understand theses cases. Any idea? Thanks in advance.

Here is the code of the collision tester.

public static boolean areColliding(Asset asset1, Asset asset2){
	Spatial s1 = getSpatialFromAsset(asset1); 
	Spatial s2 = getSpatialFromAsset(asset2);

	// Transformations for physics don't include the scale, which is set at
	// collision shape creation  
	Transform body1Transform = new Transform();
	body1Transform.setRotation(s1.getLocalRotation());
	body1Transform.setTranslation(s1.getLocalTranslation());
	Transform body2Transform = new Transform();
	body2Transform.setRotation(s2.getLocalRotation());
	body2Transform.setTranslation(s2.getLocalTranslation());

	// We create a physic space and add the body of the first asset
	PhysicsSpace space = new PhysicsSpace();
	RigidBodyControl body1 = new RigidBodyControl(getCollisionShape(asset1), 0);
	s1.addControl(body1);
	space.add(body1);
	space.update(1);
	
	// We set a sweep test with a body from the second asset. 
	boolean collision = false;
	for(ChildCollisionShape hull : getCollisionShape(asset2).getChildren())
		if(!space.sweepTest(hull.shape, Transform.IDENTITY, body2Transform).isEmpty()){
			collision = true;
			break;
		}
	space.remove(body1);
		
	if(!collision){
		// This is only for drawing and debugging
		// spatial 1
		Material m = new Material(am, "Common/MatDefs/Misc/Unshaded.j3md");
		m.getAdditionalRenderState().setWireframe(true);
		m.setColor("Color", ColorRGBA.Green);
		
		Spatial debugS1 = DebugShapeFactory.getDebugShape(getCollisionShape(asset1));
		debugS1.setLocalTransform(body1Transform);
		debugS1.setMaterial(m);
		asset2.links.add(debugS1);

		// spatial 2
		Material m2 = new Material(am, "Common/MatDefs/Misc/Unshaded.j3md");
		m2.getAdditionalRenderState().setWireframe(true);
		m2.setColor("Color", ColorRGBA.Red);
		
		Spatial debugS2 = DebugShapeFactory.getDebugShape(getCollisionShape(asset2));
		debugS2.setLocalTransform(body2Transform);
		debugS2.setMaterial(m2);
		asset2.links.add(debugS2);

		// link
		Material mlink = new Material(am, "Common/MatDefs/Misc/Unshaded.j3md");
		mlink.getAdditionalRenderState().setWireframe(true);
		mlink.setColor("Color", ColorRGBA.Blue);
		
		Geometry link = new Geometry();
		Line l = new Line(debugS1.getLocalTranslation().add(0, 0, 0.5f), debugS2.getLocalTranslation().add(0, 0, 0.5f));
		link.setMesh(l);
		link.setMaterial(mlink);
		asset2.links.add(link);
	}
	return collision; 
}

private static Spatial getSpatialFromAsset(Asset asset){
	if (!models.containsKey(asset.modelPath))
		models.put(asset.modelPath, am.loadModel("models/" + asset.modelPath));
	Spatial res = models.get(asset.modelPath).clone();
	
	res.setLocalScale((float)asset.scale);
	res.setLocalTranslation(TranslateUtil.toVector3f(asset.pos));
	res.setLocalRotation(new Quaternion().fromAngles(0, 0, (float)asset.yaw));
	return res;
}

private static CompoundCollisionShape getCollisionShape(Asset asset){
	Spatial s = models.get(asset.modelPath);
	CompoundCollisionShape res = new CompoundCollisionShape();
	if(s instanceof Node){
		for(Spatial child : ((Node)s).getChildren())
			if(child instanceof Geometry){
				HullCollisionShape hull = new HullCollisionShape(((Geometry)child).getMesh());
				hull.setScale(Vector3f.UNIT_XYZ.mult((float)asset.scale));
				res.addChildShape(hull, Vector3f.ZERO);
			}
	} else {
		logger.info("Houston, we've got a problem.");
	}
	return res;
}

Take a look on your collisions group, it may be the cause.