GhostControl collision oddness

Even not using ridged it still gives wrong data when the ghost is rotated with the parent spatial



Works Fine without rotation (commented out):

[java]



import com.jme3.app.SimpleApplication;

import com.jme3.bullet.BulletAppState;

import com.jme3.bullet.PhysicsSpace;

import com.jme3.bullet.PhysicsTickListener;

import com.jme3.bullet.collision.PhysicsCollisionObject;

import com.jme3.bullet.collision.shapes.BoxCollisionShape;

import com.jme3.bullet.control.GhostControl;

import com.jme3.math.FastMath;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.scene.Node;



public class CollisionIssue extends SimpleApplication implements PhysicsTickListener

{

static CollisionIssue app;

private BulletAppState bulletAppState;



static GhostControl ghostControl;

Node model;



public static void main(String[] args)

{

app = new CollisionIssue();

app.start();

}



@Override

public void simpleInitApp()

{

bulletAppState = new BulletAppState();

bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);

stateManager.attach(bulletAppState);

bulletAppState.getPhysicsSpace().setAccuracy(1f / 60f);

bulletAppState.getPhysicsSpace().enableDebug(assetManager);



cam.setLocation(new Vector3f(0, 50f, 0f));

cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_X);



ghostControl = new GhostControl(new BoxCollisionShape(new Vector3f(3f, 3f, 3f)));

// model = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(3f, 3f, 3f)), 1);

model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");

ghostControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);

ghostControl.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);

// model.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(7f, 0f, 7f));

// model.getControl(RigidBodyControl.class).setPhysicsRotation(new Quaternion().fromAngleAxis(45FastMath.DEG_TO_RAD, new Vector3f(0,1,0)));

model.setLocalTranslation(new Vector3f(7f, 0f, 7f));

// model.rotate(new Quaternion().fromAngleAxis(45
FastMath.DEG_TO_RAD, new Vector3f(0,1,0)));

getPhysicsSpace().add(ghostControl);

model.addControl(ghostControl);

rootNode.attachChild(model);



GhostControl ghostControl1 = new GhostControl(new BoxCollisionShape(new Vector3f(3f, 3f, 3f)));

// Node tmodel = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(3f, 3f, 3f)), 0);

Node tmodel = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");

ghostControl1.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);

ghostControl1.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);

tmodel.addControl(ghostControl1);

// tmodel.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f, 0f, 0f));

rootNode.attachChild(tmodel);

getPhysicsSpace().add(ghostControl1);



getPhysicsSpace().addTickListener(this);

}



private PhysicsSpace getPhysicsSpace()

{

return bulletAppState.getPhysicsSpace();

}



public void prePhysicsTick(PhysicsSpace space, float f)

{

//place holder

}



public void physicsTick(PhysicsSpace space, float f)

{

if (ghostControl.getOverlappingObjects().size() > 0)

{

fpsText.setText("Overlapping objects: " + ghostControl.getOverlappingObjects().toString());

}

}

}



[/java]



Broken when model is rotated (i.e. ghost rotated as it does on its own)

[java]



import com.jme3.app.SimpleApplication;

import com.jme3.bullet.BulletAppState;

import com.jme3.bullet.PhysicsSpace;

import com.jme3.bullet.PhysicsTickListener;

import com.jme3.bullet.collision.PhysicsCollisionObject;

import com.jme3.bullet.collision.shapes.BoxCollisionShape;

import com.jme3.bullet.control.GhostControl;

import com.jme3.math.FastMath;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.scene.Node;



public class CollisionIssue extends SimpleApplication implements PhysicsTickListener

{

static CollisionIssue app;

private BulletAppState bulletAppState;



static GhostControl ghostControl;

Node model;



public static void main(String[] args)

{

app = new CollisionIssue();

app.start();

}



@Override

public void simpleInitApp()

{

bulletAppState = new BulletAppState();

bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);

stateManager.attach(bulletAppState);

bulletAppState.getPhysicsSpace().setAccuracy(1f / 60f);

bulletAppState.getPhysicsSpace().enableDebug(assetManager);



cam.setLocation(new Vector3f(0, 50f, 0f));

cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_X);



ghostControl = new GhostControl(new BoxCollisionShape(new Vector3f(3f, 3f, 3f)));

// model = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(3f, 3f, 3f)), 1);

model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");

ghostControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);

ghostControl.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);

// model.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(7f, 0f, 7f));

// model.getControl(RigidBodyControl.class).setPhysicsRotation(new Quaternion().fromAngleAxis(45FastMath.DEG_TO_RAD, new Vector3f(0,1,0)));

model.setLocalTranslation(new Vector3f(7f, 0f, 7f));

model.rotate(new Quaternion().fromAngleAxis(45
FastMath.DEG_TO_RAD, new Vector3f(0,1,0)));

getPhysicsSpace().add(ghostControl);

model.addControl(ghostControl);

rootNode.attachChild(model);



GhostControl ghostControl1 = new GhostControl(new BoxCollisionShape(new Vector3f(3f, 3f, 3f)));

// Node tmodel = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(3f, 3f, 3f)), 0);

Node tmodel = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");

ghostControl1.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);

ghostControl1.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);

tmodel.addControl(ghostControl1);

// tmodel.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f, 0f, 0f));

rootNode.attachChild(tmodel);

getPhysicsSpace().add(ghostControl1);



getPhysicsSpace().addTickListener(this);

}



private PhysicsSpace getPhysicsSpace()

{

return bulletAppState.getPhysicsSpace();

}



public void prePhysicsTick(PhysicsSpace space, float f)

{

//place holder

}



public void physicsTick(PhysicsSpace space, float f)

{

if (ghostControl.getOverlappingObjects().size() > 0)

{

fpsText.setText("Overlapping objects: " + ghostControl.getOverlappingObjects().toString());

}

}

}



[/java]

What is wrong exactly? Setting the rotation on the GhostControl doesn’t do anything per se as it takes its location and rotation from the spatial by default. If you don’t want a link to a Spatial use the PhysicsGhostObject directly. In your special case though, as you first set the physics location of the PhysicsGhostControl (the ancestor of GhostControl) and then add it to the physics space that location will be registered as the fist location for the next physics tick. Though after that the GhostControl will commence and do what its supposed to that which is setting the PhysicsGhostObjects location to the world location of the spatial it is attached to.

Is the problem that when the character/object rotates, the ghostcontrol does not rotate with it? I couldn’t quite tell if that is what the complaint is about. Isn’t it a fact that ghostobjects in bullet are always axis aligned and not object aligned?

@iwgeric said:
Is the problem that when the character/object rotates, the ghostcontrol does not rotate with it? I couldn't quite tell if that is what the complaint is about. Isn't it a fact that ghostobjects in bullet are always axis aligned and not object aligned?

Actually yes. And actually they are even based on the AABB (AxisAlignedBpundingBox) by default according to the bullet docs (which are often outdated and inaccurate) which would also explain the box behavior, still the test cases are pretty convoluted still and I have no PC right now. I thought the native bullet code at least had a different approach for that thats why I asked.

The issue is that when what-ever the GhostNode is attached to, be it Spatial or Physics (Ridged, CharacterControl, etc), rotates (as shows in the prior couple of posts) it reports collisions when/where there are none.



Like in my last post.



The first sample code just loads the first GhostControl, adds it to a Spatial, then moves the Spatial to Vector3f(7f, 0f, 7f) (this is the one that will be used to do all the testing with at the moment). And then makes another to test against at Vector3f(0f, 0f, 0f) ( that is outside of the testing GhostContol’s view) . Nothing more and it works just fine. No rotations are done here – just pure 0f,0f,0f rotations.



The second sample code does the same exact stuff. However, it simply rotates the Spatial (the center does not move anywhere x,y,z – just spun around axis y) and we suddenly have collisions reported.

No, your test is totally off. Like I said the ghost object is only at 7/0/7 for one physics frame and then goes to 0/0/0 where the spatial is. When you have the additional RigidBody also then it will first be at 7/0/7, then the ghost control will set it to the spatial location at 0/0/0 and after that the RigidyBodyControl will set it right back to 7/0/7 in the next update loop. As the RigidBody is never actually added to the physics space its just seting the location of the spatial continuously from then on, fighting with the gost control that sets its ghost to the spatial location.

That is not what is being reported by the code (added additional reporting and cleaned it up a little) :



[java]

import com.jme3.app.SimpleApplication;

import com.jme3.bullet.BulletAppState;

import com.jme3.bullet.PhysicsSpace;

import com.jme3.bullet.PhysicsTickListener;

import com.jme3.bullet.collision.PhysicsCollisionObject;

import com.jme3.bullet.collision.shapes.BoxCollisionShape;

import com.jme3.bullet.control.GhostControl;

import com.jme3.math.FastMath;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.scene.Node;



public class CollisionIssue extends SimpleApplication implements PhysicsTickListener

{

static CollisionIssue app;

private BulletAppState bulletAppState;



static GhostControl ghostControl;

static GhostControl ghostControl1;

Node model;



public static void main(String[] args)

{

app = new CollisionIssue();

app.start();

}



@Override

public void simpleInitApp()

{

bulletAppState = new BulletAppState();

bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);

stateManager.attach(bulletAppState);

bulletAppState.getPhysicsSpace().setAccuracy(1f / 60f);

bulletAppState.getPhysicsSpace().enableDebug(assetManager);



cam.setLocation(new Vector3f(0, 50f, 0f));

cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_X);



ghostControl = new GhostControl(new BoxCollisionShape(new Vector3f(3f, 3f, 3f)));

model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");

ghostControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);

ghostControl.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);

model.setLocalTranslation(new Vector3f(7f, 0f, 7f));

model.rotate(new Quaternion().fromAngleAxis(45*FastMath.DEG_TO_RAD, new Vector3f(0,1,0)));

getPhysicsSpace().add(ghostControl);

model.addControl(ghostControl);

rootNode.attachChild(model);



ghostControl1 = new GhostControl(new BoxCollisionShape(new Vector3f(3f, 3f, 3f)));

Node tmodel = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");

ghostControl1.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);

ghostControl1.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);

tmodel.addControl(ghostControl1);

rootNode.attachChild(tmodel);

getPhysicsSpace().add(ghostControl1);



getPhysicsSpace().addTickListener(this);

}



private PhysicsSpace getPhysicsSpace()

{

return bulletAppState.getPhysicsSpace();

}



public void prePhysicsTick(PhysicsSpace space, float f)

{

//place holder

}



public void physicsTick(PhysicsSpace space, float f)

{

if (ghostControl.getOverlappingObjects().size() > 0)

{

fpsText.setText("Overlapping objects: " + cleanupObjectName( ghostControl.getOverlappingObjects().toString() ) + " – Tester (" + cleanupObjectName( ghostControl.toString() ) + ") Loc: " + ghostControl.getPhysicsLocation() + " – Collision Object (" + cleanupObjectName( ghostControl1.toString() ) + ") Loc: " + ghostControl1.getPhysicsLocation());

System.out.println("Overlapping objects: " + cleanupObjectName( ghostControl.getOverlappingObjects().toString() ) + " – Tester (" + cleanupObjectName( ghostControl.toString() ) + ") Loc: " + ghostControl.getPhysicsLocation() + " – Collision Object (" + cleanupObjectName( ghostControl1.toString() ) + ") Loc: " + ghostControl1.getPhysicsLocation());

}

}



static String cleanupObjectName( String name )

{

return name.replaceAll("com.jme3.bullet.control.", "");

}

}



[/java]



Is returning that the objects are where I say they should be (excerpt from output):



Overlapping objects: [GhostControl@eff0c] -- Tester (GhostControl@412161) Loc: (7.0, 0.0, 7.0) -- Collision Object (GhostControl@eff0c) Loc: (0.0, 0.0, 0.0)
Overlapping objects: [GhostControl@eff0c] -- Tester (GhostControl@412161) Loc: (7.0, 0.0, 7.0) -- Collision Object (GhostControl@eff0c) Loc: (0.0, 0.0, 0.0)
Overlapping objects: [GhostControl@eff0c] -- Tester (GhostControl@412161) Loc: (7.0, 0.0, 7.0) -- Collision Object (GhostControl@eff0c) Loc: (0.0, 0.0, 0.0)
Overlapping objects: [GhostControl@eff0c] -- Tester (GhostControl@412161) Loc: (7.0, 0.0, 7.0) -- Collision Object (GhostControl@eff0c) Loc: (0.0, 0.0, 0.0)
Overlapping objects: [GhostControl@eff0c] -- Tester (GhostControl@412161) Loc: (7.0, 0.0, 7.0) -- Collision Object (GhostControl@eff0c) Loc: (0.0, 0.0, 0.0)
Overlapping objects: [GhostControl@eff0c] -- Tester (GhostControl@412161) Loc: (7.0, 0.0, 7.0) -- Collision Object (GhostControl@eff0c) Loc: (0.0, 0.0, 0.0)

So whats “flipping out” now? The collisions are continuously reported. This would probably mean that the AABB is actually used in this case (it would be 4.3 units wide in each direction and thus overlap).