GhostControl collision oddness

I am getting strange results when using the GhostControls for testing collisions. It seems the accuracy of the control is off a bit. I have tested this using the different Collisions shapes (Box, Sphere, Capsule) and I get the same result:



  • While Stationary - the Controls that are physically close (say within 1-2 units, but not touching) are returning a "Overlapping objects" results

  • While Moving - the Controls will return random "Overlapping objects" results at greater distances



Now I am more than likely trying to use the control improperly, but to make sure I altered the physics TestWalkingChar example to show how I am trying to use the controls.

[java]
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.GhostControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
import java.util.ArrayList;
import java.util.List;

public class TestWalkingCollision extends SimpleApplication implements ActionListener, PhysicsCollisionListener, AnimEventListener
{
private BulletAppState bulletAppState;
//character
static CharacterControl character;
Node model;
//temp vectors
Vector3f walkDirection = new Vector3f();
//terrain
TerrainQuad terrain;
RigidBodyControl terrainPhysicsNode;
//Materials
Material matRock;
Material matBullet;
//animation
AnimChannel animationChannel;
AnimControl animationControl;
float airTime = 0;
//camera
boolean left = false, right = false, up = false, down = false;
ChaseCamera chaseCam;

//control for ghost testing
private GhostControl ghostControl;

public static void main(String[] args)
{
TestWalkingCollision app = new TestWalkingCollision();
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);
setupKeys();
createLight();
createSky();
createTerrain();
createCharacter();
createDummyCharacter();
setupChaseCamera();
setupAnimationController();
}

private PhysicsSpace getPhysicsSpace()
{
return bulletAppState.getPhysicsSpace();
}

private void setupKeys()
{
inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping("CharUp", new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping("CharDown", new KeyTrigger(KeyInput.KEY_S));
inputManager.addListener(this, "CharLeft");
inputManager.addListener(this, "CharRight");
inputManager.addListener(this, "CharUp");
inputManager.addListener(this, "CharDown");
}

private void createLight()
{
Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();
DirectionalLight dl = new DirectionalLight();
dl.setDirection(direction);
dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
rootNode.addLight(dl);
}

private void createSky()
{
rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
}

private void createTerrain()
{
matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
matRock.setBoolean("useTriPlanarMapping", false);
matRock.setBoolean("WardIso", true);
matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap", grass);
matRock.setFloat("DiffuseMap_0_scale", 64);
Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_1", dirt);
matRock.setFloat("DiffuseMap_1_scale", 16);
Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
rock.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_2", rock);
matRock.setFloat("DiffuseMap_2_scale", 128);
Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
normalMap0.setWrap(WrapMode.Repeat);
Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
normalMap1.setWrap(WrapMode.Repeat);
Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
normalMap2.setWrap(WrapMode.Repeat);
matRock.setTexture("NormalMap", normalMap0);
matRock.setTexture("NormalMap_1", normalMap2);
matRock.setTexture("NormalMap_2", normalMap2);

AbstractHeightMap heightmap = null;
try
{
heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);
heightmap.load();
}
catch (Exception e)
{
e.printStackTrace();
}

terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
terrain.setMaterial(matRock);
terrain.setLocalScale(new Vector3f(2, 2, 2));

terrainPhysicsNode = new RigidBodyControl(CollisionShapeFactory.createMeshShape(terrain), 0);
terrain.addControl(terrainPhysicsNode);
rootNode.attachChild(terrain);
getPhysicsSpace().add(terrainPhysicsNode);
}

private void createCharacter()
{
ghostControl = new GhostControl(new CapsuleCollisionShape(3f, 4f, 1));

CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f, 1);
character = new CharacterControl(capsule, 0.01f);
model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");

model.addControl(character);

ghostControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
ghostControl.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);

model.addControl(ghostControl);

character.setPhysicsLocation(new Vector3f(-140, 15, -10));
ghostControl.setPhysicsLocation(new Vector3f(-140, 15, -10));

rootNode.attachChild(model);
getPhysicsSpace().add(character);
getPhysicsSpace().add(ghostControl);
}

private void createDummyCharacter()
{
GhostControl ghostControl1 = new GhostControl(new CapsuleCollisionShape(3f, 4f, 1));
CapsuleCollisionShape tcapsule = new CapsuleCollisionShape(3f, 4f, 1);

CharacterControl tcharacter = new CharacterControl(tcapsule, 1000f);
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.addControl(tcharacter);
tcharacter.setPhysicsLocation(new Vector3f(-160, 15, -10));
ghostControl1.setPhysicsLocation(new Vector3f(-160, 15, -10));

rootNode.attachChild(tmodel);
getPhysicsSpace().add(tcharacter);
getPhysicsSpace().add(ghostControl1);
}

private void setupChaseCamera()
{
flyCam.setEnabled(false);
chaseCam = new ChaseCamera(cam, model, inputManager);
}

private void setupAnimationController()
{
animationControl = model.getControl(AnimControl.class);
animationControl.addListener(this);
animationChannel = animationControl.createChannel();
}

@Override
public void simpleUpdate(float tpf)
{
Vector3f camDir = cam.getDirection().clone().multLocal(0.1f);
Vector3f camLeft = cam.getLeft().clone().multLocal(0.1f);
camDir.y = 0;
camLeft.y = 0;
walkDirection.set(0, 0, 0);
if (left)
{
walkDirection.addLocal(camLeft);
}
if (right)
{
walkDirection.addLocal(camLeft.negate());
}
if (up)
{
walkDirection.addLocal(camDir);
}
if (down)
{
walkDirection.addLocal(camDir.negate());
}
if (!character.onGround())
{
airTime = airTime + tpf;
}
else
{
airTime = 0;
}
if (walkDirection.length() == 0)
{
if (!"stand".equals(animationChannel.getAnimationName()))
{
animationChannel.setAnim("stand", 1f);
}
}
else
{
character.setViewDirection(walkDirection);
if (airTime > .3f)
{
if (!"stand".equals(animationChannel.getAnimationName()))
{
animationChannel.setAnim("stand");
}
}
else if (!"Walk".equals(animationChannel.getAnimationName()))
{
animationChannel.setAnim("Walk", 0.7f);
}
}

if (!ghostControl.getOverlappingObjects().isEmpty())
{
//System.out.println("Overlapping objects: " + ghostControl.getOverlappingObjects().toString());
fpsText.setText("Overlapping objects: " + ghostControl.getOverlappingObjects().toString());
}

character.setWalkDirection(walkDirection);

}

public void onAction(String binding, boolean value, float tpf)
{
if (binding.equals("CharLeft"))
{
if (value)
{
left = true;
}
else
{
left = false;
}
}
else if (binding.equals("CharRight"))
{
if (value)
{
right = true;
}
else
{
right = false;
}
}
else if (binding.equals("CharUp"))
{
if (value)
{
up = true;
}
else
{
up = false;
}
}
else if (binding.equals("CharDown"))
{
if (value)
{
down = true;
}
else
{
down = false;
}
}
else if (binding.equals("CharSpace"))
{
character.jump();
}
}

public void collision(PhysicsCollisionEvent event)
{
if (event.getObjectA() instanceof GhostControl)
{
System.out.println("character collision");
}
}

public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName)
{
}

public void onAnimChange(AnimControl control, AnimChannel channel, String animName)
{
}
}

[/java]

Thanks for any help/input.

try doing the overlapping check in a PhysicsTickListener

Thanks for pointing me in a direction.



It would have been nice if the devs made the test examples actually do it properly.

https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:physics_listeners

Also, when you check for overlaps of two physical objects using a GhostControl, you cannot just go ghost.getOverLappingObjects() somewhere outside the update loop. You have to make certain 1 physics tick has passed before the overlapping objects list is filled with data. Again, the PhysicsTickListener does the timing for you.

Yeah, I found that after you pointed me in that direction. However, it still didn’t help that the test code for JME3 is doing it the same way that I initially did. -.-



But now that I have implemented the PhysicsTickListener, I have run into an issue where the GhostControl is not rotating with the parent spatial (i.e. the CharacterControl it moving X, and GhostControl is testing Z when the controls are facing different rotations – and vice versa). Any idea how I can achieve this without building my own GhostControl or do evil math 8O in the prePhysicsTick()? Overall I think this is the reason for my initial issue, because it is acting in the same manner as using the above code.

Ok, I’ve updated the previous code and it now highlights where the GhostControl is reporting a collision. It is reporting hits at a larger distance when moving diaganaly. I thought that it might be an issue with the camera being used to calulate movement, so I also tested using the movement from TestPhysicsCharacter as it rotated the camera around instead with no change in results.



Oddly the testing shows that the collisions form (a plus like it should when only doing forward and sideways movements or a box if adding in diagonal movements) around the object that is being tested against. However when using a CapsuleCollisionShape, I find this a bit disturbing.



GhostControl Collision Issue



EDIT: For Bad Value



[java]

import com.jme3.animation.AnimChannel;

import com.jme3.animation.AnimControl;

import com.jme3.animation.AnimEventListener;

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.PhysicsCollisionEvent;

import com.jme3.bullet.collision.PhysicsCollisionListener;

import com.jme3.bullet.collision.PhysicsCollisionObject;

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

import com.jme3.bullet.control.CharacterControl;

import com.jme3.bullet.control.GhostControl;

import com.jme3.bullet.control.RigidBodyControl;

import com.jme3.bullet.util.CollisionShapeFactory;

import com.jme3.input.ChaseCamera;

import com.jme3.input.KeyInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.light.DirectionalLight;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.renderer.Camera;

import com.jme3.scene.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.shape.Box;

import com.jme3.terrain.geomipmap.TerrainLodControl;

import com.jme3.terrain.geomipmap.TerrainQuad;

import com.jme3.terrain.heightmap.AbstractHeightMap;

import com.jme3.terrain.heightmap.ImageBasedHeightMap;

import com.jme3.texture.Texture;

import com.jme3.texture.Texture.WrapMode;

import com.jme3.util.SkyFactory;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.Callable;



public class TestWalkingCollision extends SimpleApplication implements ActionListener, AnimEventListener, PhysicsTickListener

{

static TestWalkingCollision app;

private BulletAppState bulletAppState;

//character

CharacterControl character;

Node model;

//temp vectors

Vector3f walkDirection = new Vector3f();

//terrain

TerrainQuad terrain;

RigidBodyControl terrainPhysicsNode;

//Materials

Material matRock;

//animation

AnimChannel animationChannel;

AnimControl animationControl;

float airTime = 0;

//camera

boolean left = false, right = false, up = false, down = false;

ChaseCamera chaseCam;

//control for ghost testing

static GhostControl ghostControl;



public static void main(String[] args)

{

app = new TestWalkingCollision();

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);

setupKeys();

createLight();

createSky();

createTerrain();

createCharacter();

createDummyCharacter();

setupChaseCamera();

setupAnimationController();

setupMarkers();

getPhysicsSpace().addTickListener(this);

}



private PhysicsSpace getPhysicsSpace()

{

return bulletAppState.getPhysicsSpace();

}



private void setupKeys()

{

inputManager.addMapping(“CharLeft”, new KeyTrigger(KeyInput.KEY_A));

inputManager.addMapping(“CharRight”, new KeyTrigger(KeyInput.KEY_D));

inputManager.addMapping(“CharUp”, new KeyTrigger(KeyInput.KEY_W));

inputManager.addMapping(“CharDown”, new KeyTrigger(KeyInput.KEY_S));

inputManager.addListener(this, “CharLeft”);

inputManager.addListener(this, “CharRight”);

inputManager.addListener(this, “CharUp”);

inputManager.addListener(this, “CharDown”);

}



private void createLight()

{

Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();

DirectionalLight dl = new DirectionalLight();

dl.setDirection(direction);

dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));

rootNode.addLight(dl);

}



private void createSky()

{

rootNode.attachChild(SkyFactory.createSky(assetManager, “Textures/Sky/Bright/BrightSky.dds”, false));

}



private void createTerrain()

{

matRock = new Material(assetManager, “Common/MatDefs/Terrain/TerrainLighting.j3md”);

matRock.setBoolean(“useTriPlanarMapping”, false);

matRock.setBoolean(“WardIso”, true);

matRock.setTexture(“AlphaMap”, assetManager.loadTexture(“Textures/Terrain/splat/alphamap.png”));

Texture heightMapImage = assetManager.loadTexture(“Textures/Terrain/splat/mountains512.png”);

Texture grass = assetManager.loadTexture(“Textures/Terrain/splat/grass.jpg”);

grass.setWrap(WrapMode.Repeat);

matRock.setTexture(“DiffuseMap”, grass);

matRock.setFloat(“DiffuseMap_0_scale”, 64);

Texture dirt = assetManager.loadTexture(“Textures/Terrain/splat/dirt.jpg”);

dirt.setWrap(WrapMode.Repeat);

matRock.setTexture(“DiffuseMap_1”, dirt);

matRock.setFloat(“DiffuseMap_1_scale”, 16);

Texture rock = assetManager.loadTexture(“Textures/Terrain/splat/road.jpg”);

rock.setWrap(WrapMode.Repeat);

matRock.setTexture(“DiffuseMap_2”, rock);

matRock.setFloat(“DiffuseMap_2_scale”, 128);

Texture normalMap0 = assetManager.loadTexture(“Textures/Terrain/splat/grass_normal.jpg”);

normalMap0.setWrap(WrapMode.Repeat);

Texture normalMap1 = assetManager.loadTexture(“Textures/Terrain/splat/dirt_normal.png”);

normalMap1.setWrap(WrapMode.Repeat);

Texture normalMap2 = assetManager.loadTexture(“Textures/Terrain/splat/road_normal.png”);

normalMap2.setWrap(WrapMode.Repeat);

matRock.setTexture(“NormalMap”, normalMap0);

matRock.setTexture(“NormalMap_1”, normalMap2);

matRock.setTexture(“NormalMap_2”, normalMap2);



AbstractHeightMap heightmap = null;

try

{

heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);

heightmap.load();

}

catch (Exception e)

{

e.printStackTrace();

}



terrain = new TerrainQuad(“terrain”, 65, 513, heightmap.getHeightMap());

List<Camera> cameras = new ArrayList<Camera>();

cameras.add(getCamera());

TerrainLodControl control = new TerrainLodControl(terrain, cameras);

terrain.addControl(control);

terrain.setMaterial(matRock);

terrain.setLocalScale(new Vector3f(2, 2, 2));



terrainPhysicsNode = new RigidBodyControl(CollisionShapeFactory.createMeshShape(terrain), 0);

terrain.addControl(terrainPhysicsNode);

rootNode.attachChild(terrain);

getPhysicsSpace().add(terrainPhysicsNode);

}



private void createCharacter()

{

ghostControl = new GhostControl(new CapsuleCollisionShape(3f, 4f, 1));



CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f, 1);



character = new CharacterControl(capsule, 0.01f);



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



model.addControl(character);



ghostControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);

ghostControl.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);



model.addControl(ghostControl);



character.setPhysicsLocation(new Vector3f(-140, 15, -10));

ghostControl.setPhysicsLocation(new Vector3f(-140, 15, -10));



rootNode.attachChild(model);

getPhysicsSpace().add(character);

getPhysicsSpace().add(ghostControl);

}

CharacterControl tcharacter;



private void createDummyCharacter()

{

GhostControl ghostControl1 = new GhostControl(new CapsuleCollisionShape(3f, 4f, 1));

CapsuleCollisionShape tcapsule = new CapsuleCollisionShape(3f, 4f, 1);



tcharacter = new CharacterControl(tcapsule, 0.01f);



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.addControl(tcharacter);

tcharacter.setPhysicsLocation(new Vector3f(-160, 15, -10));

ghostControl1.setPhysicsLocation(new Vector3f(-160, 15, -10));



rootNode.attachChild(tmodel);

getPhysicsSpace().add(tcharacter);

getPhysicsSpace().add(ghostControl1);

}



private void setupChaseCamera()

{

flyCam.setEnabled(false);

chaseCam = new ChaseCamera(cam, model, inputManager);

}



private void setupAnimationController()

{

animationControl = model.getControl(AnimControl.class);

animationControl.addListener(this);

animationChannel = animationControl.createChannel();

}



@Override

public void simpleUpdate(float tpf)

{

Vector3f camDir = cam.getDirection().clone().multLocal(0.1f);

Vector3f camLeft = cam.getLeft().clone().multLocal(0.1f);

camDir.y = 0;

camLeft.y = 0;

walkDirection.set(0, 0, 0);

if (left)

{

walkDirection.addLocal(camLeft);

}

if (right)

{

walkDirection.addLocal(camLeft.negate());

}

if (up)

{

walkDirection.addLocal(camDir);

}

if (down)

{

walkDirection.addLocal(camDir.negate());

}

if (!character.onGround())

{

airTime = airTime + tpf;

}

else

{

airTime = 0;

}

if (walkDirection.length() == 0)

{

if (!“stand”.equals(animationChannel.getAnimationName()))

{

animationChannel.setAnim(“stand”, 1f);

}

}

else

{

character.setViewDirection(walkDirection);

if (airTime > .3f)

{

if (!“stand”.equals(animationChannel.getAnimationName()))

{

animationChannel.setAnim(“stand”);

}

}

else if (!“Walk”.equals(animationChannel.getAnimationName()))

{

animationChannel.setAnim(“Walk”, 0.7f);

}

}



character.setWalkDirection(walkDirection);

}



public void onAction(String binding, boolean value, float tpf)

{

if (binding.equals(“CharLeft”))

{

if (value)

{

left = true;

}

else

{

left = false;

}

}

else if (binding.equals(“CharRight”))

{

if (value)

{

right = true;

}

else

{

right = false;

}

}

else if (binding.equals(“CharUp”))

{

if (value)

{

up = true;

}

else

{

up = false;

}

}

else if (binding.equals(“CharDown”))

{

if (value)

{

down = true;

}

else

{

down = false;

}

}

else if (binding.equals(“CharSpace”))

{

character.jump();

}

}



public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName)

{

}



public void onAnimChange(AnimControl control, AnimChannel channel, String animName)

{

}



public void prePhysicsTick(PhysicsSpace space, float f)

{

//place holder

}



public void physicsTick(PhysicsSpace space, float f)

{

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

{

final Vector3f bPoint = ghostControl.getPhysicsLocation();



try

{

app.enqueue(new Callable<Boolean>()

{

@Override

public Boolean call() throws Exception

{

app.addMarker(bPoint);

return true;

}

});

}

catch (Exception ex)

{

}



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

}

}



Material matMarker;



private void setupMarkers()

{

matMarker = new Material(getAssetManager(), “Common/MatDefs/Misc/Unshaded.j3md”);

matMarker.setColor(“Color”, ColorRGBA.Green);

matMarker.setColor(“GlowColor”, ColorRGBA.Green);

}



public void addMarker(Vector3f ori)

{

Geometry reBoxg = new Geometry(“marker”, new Box(Vector3f.ZERO, 0.1f, 0.1f, 0.1f));

reBoxg.setMaterial(matMarker);

reBoxg.setLocalTranslation(ori);

this.rootNode.attachChild(reBoxg);

}

}

[/java]

If you think you found an issue please make a test case (thats not your whole application or a huge test class like this).

@Dardzull said:
Yeah, I found that after you pointed me in that direction. However, it still didn't help that the test code for JME3 is doing it the same way that I initially did. -.-

If you copy-paste _some_ code that _seemingly_ does what you want you're always in for fun. Checking the overlaps in the normal update loop works fine, MonkeyZone and the tests all work fine. Waiting for the tick is needed when you want to be sure the overlaps have been evaluated, the BombControl uses it for example.

Sorry, I thought the above code was a test case. It only uses JME3 test assets and self contained. :frowning:



However if you run the above code, and move at a diagonal to the object (like in my picture) you will get collision info (the neat green boxes and text) showing that you are making collisions where there should not be.

first of all m_GlowColor doesn’t exist, use GlowColor.



And i tested your code and it seems to be functioning properly?

When i go inside the other control it starts painting the green looking thingy



http://i.imgur.com/odorC.jpg

Fixed the m_glowcolor in the code (sorry ripped from old jme2 code and it never reported error).



But if you notice when moving at an angle to the test object greater than the width of the moving object there is a collision reports (see my image showing the test results). If you move around enough (diagonally) you will eventually make a box around the test object vs a somewhat circular pattern when only moving back and forth (i.e. never altering the angle). If I’m using a spear or capsule, the system should only make a roundish area. This extra area that gets created is only happening when moving at an angle though, which is causing my worries.



But this should show a more effective view of the issue:



true collsion




false collision

Ok, I was wrong on the rotation of the parent object, BUT there is something odd going on.



HOPEFULLY this is a tight enough of a test case for @normen as I altered the TestMotionPath to show where collisions are happening (i.e. removing any input other then starting the sim).



Motion Test Case



So, Press SPACE to start and P to remove the path lines.



EDIT: Renamed to remove the possible conflict with the real test class in test lib.



[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.collision.shapes.CapsuleCollisionShape;

import com.jme3.bullet.control.GhostControl;

import com.jme3.cinematic.MotionPath;

import com.jme3.cinematic.events.MotionTrack;

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.math.FastMath;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.scene.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.shape.Box;

import java.util.concurrent.Callable;

import jme3test.bullet.PhysicsTestHelper;



public class TestMotionPathh extends SimpleApplication implements PhysicsTickListener

{

static TestMotionPathh app;

private boolean active = true;

private boolean playing = false;

private MotionPath path;

private MotionTrack motionControl;

static GhostControl ghostControl;

Node model;

private BulletAppState bulletAppState;

Material matMarker;



public static void main(String[] args)

{

app = new TestMotionPathh();

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);



createScene();

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

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

path = new MotionPath();

path.addWayPoint(new Vector3f(0, 0, 20));

path.addWayPoint(new Vector3f(10, 0, 10));

path.addWayPoint(new Vector3f(10, 0, 0));

path.addWayPoint(new Vector3f(0, 0, 10));

path.addWayPoint(new Vector3f(-10, 0, -10));

path.enableDebugShape(assetManager, rootNode);



motionControl = new MotionTrack(model, path);

motionControl.setDirectionType(MotionTrack.Direction.PathAndRotation);

motionControl.setRotation(new Quaternion().fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y));

motionControl.setInitialDuration(60f);

motionControl.setSpeed(2f);



initInputs();



getPhysicsSpace().addTickListener(this);

}



private void createScene()

{

createCharacter();

createDummyCharacter();

setupMarkers();

}



private void createCharacter()

{

ghostControl = new GhostControl(new CapsuleCollisionShape(3f, 4f, 1));

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

ghostControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);

ghostControl.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);

model.addControl(ghostControl);

ghostControl.setPhysicsLocation(new Vector3f(0, 0, 20));

rootNode.attachChild(model);

getPhysicsSpace().add(ghostControl);

}



private void createDummyCharacter()

{

GhostControl ghostControl1 = new GhostControl(new CapsuleCollisionShape(3f, 4f, 1));

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

ghostControl1.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);

ghostControl1.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);

tmodel.addControl(ghostControl1);

ghostControl1.setPhysicsLocation(new Vector3f(0, 0, 0));

rootNode.attachChild(tmodel);

getPhysicsSpace().add(ghostControl1);

}



private void setupMarkers()

{

matMarker = new Material(getAssetManager(), “Common/MatDefs/Misc/Unshaded.j3md”);

matMarker.setColor(“Color”, ColorRGBA.Green);

matMarker.setColor(“GlowColor”, ColorRGBA.Green);

}



public void addMarker(Vector3f ori)

{

Geometry reBoxg = new Geometry(“marker”, new Box(Vector3f.ZERO, 0.1f, 0.1f, 0.1f));

reBoxg.setMaterial(matMarker);

reBoxg.setLocalTranslation(ori);

this.rootNode.attachChild(reBoxg);

}



private PhysicsSpace getPhysicsSpace()

{

return bulletAppState.getPhysicsSpace();

}



private void initInputs()

{

inputManager.addMapping(“display_hidePath”, new KeyTrigger(KeyInput.KEY_P));

inputManager.addMapping(“play_stop”, new KeyTrigger(KeyInput.KEY_SPACE));

ActionListener acl = new ActionListener()

{

public void onAction(String name, boolean keyPressed, float tpf)

{

if (name.equals(“display_hidePath”) && keyPressed)

{

if (active)

{

active = false;

path.disableDebugShape();

}

else

{

active = true;

path.enableDebugShape(assetManager, rootNode);

}

}

if (name.equals(“play_stop”) && keyPressed)

{

if (playing)

{

playing = false;

motionControl.stop();

}

else

{

playing = true;

motionControl.play();

}

}

}

};

inputManager.addListener(acl, “display_hidePath”, “play_stop”);

}



public void prePhysicsTick(PhysicsSpace space, float f)

{

//place holder

}



public void physicsTick(PhysicsSpace space, float f)

{

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

{

final Vector3f bPoint = ghostControl.getPhysicsLocation();

try

{

app.enqueue(new Callable<Boolean>()

{

@Override

public Boolean call() throws Exception

{

app.addMarker(bPoint);

return true;

}

});

}

catch (Exception ex)

{

}

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

}

}

}



[/java]

The “true” collision shape for the GhostControl seems to be a box that is just under 14 units wide when using a test shape (which isn’t a box) that is 6 units wide.



Using the following path points in the above test code you will end up mapping out the box shape seen in the image below.



EDIT: code was having an issue with 8) showing up



[java]

path.addWayPoint(new Vector3f(-13, 0, 2) );

path.addWayPoint(new Vector3f(0, 0, 12) );

path.addWayPoint(new Vector3f(12, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, -12) );

path.addWayPoint(new Vector3f(-12, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, 11) );

path.addWayPoint(new Vector3f(11, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, -11) );

path.addWayPoint(new Vector3f(-11, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, 10) );

path.addWayPoint(new Vector3f(10, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, -10) );

path.addWayPoint(new Vector3f(-10, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, 9) );

path.addWayPoint(new Vector3f(9, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, -9) );

path.addWayPoint(new Vector3f(-9, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, 8 ) );

path.addWayPoint(new Vector3f(8, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, -8 ) );

path.addWayPoint(new Vector3f(-8, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, 7) );

path.addWayPoint(new Vector3f(7, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, -7) );

path.addWayPoint(new Vector3f(-7, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, 6) );

path.addWayPoint(new Vector3f(6, 0, 0) );

path.addWayPoint(new Vector3f(0, 0, -6) );

path.addWayPoint(new Vector3f(-6, 0, 0) );

[/java]




Collision Issue Shape

The doubling is easily explained - it’s the radius of the two objects joined together (the edges have to meet, not the center).



It does look rather like it’s colliding with a bounding box not the capsule. Someone more knowledgeable than me will have to help on that though :slight_smile:

How does it react with native bullet?

I’m using the SKD3.0 with the latest updates on a windows vista system, so I’m only using the supplied .dll’s from the core at the moment.

@Dardzull said:
I'm using the SKD3.0 with the latest updates on a windows vista system, so I'm only using the supplied .dll's from the core at the moment.

Native bullet libraries are included. You basically switch a library (jar) in the project. Read this: http://hub.jmonkeyengine.org/2011/04/27/jme3-native-bullet-physics-is-available-for-testing/ it has a link and some instructions. Also, native bullet is not finished so don't expect everything to work.

Swapped to native (removed and renamed jbullet jars too) and its still doing the same thing – bounding box collision.

Please make a simpler test case, you create a PhysicsTestNode with BoxCollisionShape in there…?

The system is not able to handle items that are rotated. If you have something that is in perfect (0f,0f,0f) it works fine. As soon as you alter the rotation it starts flipping out and giving collisions that are not really there. i.e. its broken



Code with the issue – rotation == broken

[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.collision.shapes.CapsuleCollisionShape;

import com.jme3.bullet.control.GhostControl;

import com.jme3.bullet.control.RigidBodyControl;

import com.jme3.math.FastMath;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.scene.Node;

import jme3test.bullet.PhysicsTestHelper;



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);

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)));

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);

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]



Same code with the rotation commented out – works fine

[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.collision.shapes.CapsuleCollisionShape;

import com.jme3.bullet.control.GhostControl;

import com.jme3.bullet.control.RigidBodyControl;

import com.jme3.math.FastMath;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.scene.Node;

import jme3test.bullet.PhysicsTestHelper;



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);

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(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);

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]

Hello? You are creating a RigidBody and a GhostObject on one spatail there, are you aware of that?

Edit: also if you want to move the physics objects manually instead of with forces you need to set them to kinematic mode.