PhysicsCollisionEvent getIndex0 and getIndex1 with CompoundCollisionShape
I built a robot collision scenario and created a GImpactShape for each geom of the robot, then combined these Gimpactshapes into a CompoundCollisionShape. But when I use getIndex0 and getIndex1 in PhysicsCollisionEvent, the comment is If shape is a CompoundCollisionShape, the index identifies a child shape, but the result will say more than the length of the array. ' public class HelloGeomQuery extends SimpleApplication {
private BulletAppState bulletAppState;
private Spatial robotKinematic1;
private Spatial robotKinematic2;
boolean init;
boolean startGeomQuery;
private RigidBodyControl floor_phy;
private PhysicsCollisionListener collisionListener;
ArrayList<Boolean> overlapStateList;
ArrayList<Float> timeList;
private RigidBodyControl bodyControl1;
private RigidBodyControl bodyControl2;
private ChildCollisionShape[] childCollisionShapes1;
private ChildCollisionShape[] childCollisionShapes;
public static void main(String[] args) {
//配置参数
AppSettings setting = new AppSettings(true);
setting.setResolution(1580, 960); // 设置窗口大小
setting.setAudioRenderer(null);
HelloGeomQuery app = new HelloGeomQuery();
app.setSettings(setting);
app.setShowSettings(false);
app.start();
}
@Override
public void simpleInitApp() {
cam.setLocation(new Vector3f(-2.1795945f, 5.9709883f, 19.46696f));
cam.setRotation(new Quaternion(0.0045973924f, 0.9947644f, -0.087805875f, 0.052084833f));
flyCam.setDragToRotate(true);
flyCam.setMoveSpeed(2);
bulletAppState = new BulletAppState();
bulletAppState.setDebugEnabled(true);
stateManager.attach(bulletAppState);
initInputs();
initFloor();
bulletAppState.getPhysicsSpace().addCollisionListener(new PhysicsCollisionListener() {
@Override
public void collision(PhysicsCollisionEvent event) {
init = false;
int index0 = event.getIndex0();
int index1 = event.getIndex1();
CollisionShape shape = childCollisionShapes[index0].getShape();
CollisionShape shape1 = childCollisionShapes[index1].getShape();
}
});
init = false;
startGeomQuery = false;
timeList = new ArrayList<>();
overlapStateList = new ArrayList<>();
/*
光源参数与添加节点
*/
//创建光源
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(-1, -2, -3));
//环境光
AmbientLight ambient = new AmbientLight();
//调整光照亮度
ColorRGBA lightColor = new ColorRGBA();
sun.setColor(lightColor.mult(0.6f));
ambient.setColor(lightColor.mult(0.4f));
rootNode.addLight(sun);
rootNode.addLight(ambient);
}
public void initFloor() {
Box floor = new Box(10f, 0.15f, 5f);
Material floor_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
Geometry floor_geo = new Geometry("Floor", floor);
floor_geo.setMaterial(floor_mat);
floor_geo.setLocalTranslation(0, -0.1f, 0);
floor_phy = new RigidBodyControl(0.0f);
floor_geo.addControl(floor_phy);
bulletAppState.getPhysicsSpace().add(floor_phy);
this.rootNode.attachChild(floor_geo);
}
private void initInputs() {
inputManager.addMapping("robot1",
new KeyTrigger(KeyInput.KEY_1));
inputManager.addMapping("robot2",
new KeyTrigger(KeyInput.KEY_2));
inputManager.addMapping("startGeomQueryKey",
new KeyTrigger(KeyInput.KEY_3));
inputManager.addMapping("saveFile",
new KeyTrigger(KeyInput.KEY_4));
inputManager.addListener(actionListener, "robot1");
inputManager.addListener(actionListener, "robot2");
inputManager.addListener(actionListener, "startGeomQueryKey");
inputManager.addListener(actionListener, "saveFile");
}
final private ActionListener actionListener = new ActionListener() {
@Override
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals("robot1") && !keyPressed) {
robotKinematic1 = createRobotList(new Vector3f(0f, 0.5f, 0f), new Vector3f(0, 0, 0),
true);
bodyControl1 = robotKinematic1.getControl(RigidBodyControl.class);
CompoundCollisionShape collisionShape = (CompoundCollisionShape) bodyControl1.getCollisionShape();
childCollisionShapes = collisionShape.listChildren();
} else if (name.equals("robot2") && !keyPressed) {
robotKinematic2 = createRobotList(new Vector3f(5, 1.2f, 0.5f), new Vector3f(90, -90, -90),
true);
bodyControl2 = robotKinematic2.getControl(RigidBodyControl.class);
CompoundCollisionShape collisionShape1 = (CompoundCollisionShape) bodyControl2.getCollisionShape();
childCollisionShapes1 = collisionShape1.listChildren();
init = true;
} else if (name.equals("startGeomQueryKey") && !keyPressed) {
startGeomQuery = true;
} else if (name.equals("saveFile") && !keyPressed) {
startGeomQuery = false;
saveFile("F:\\11-物理引擎\\Physx\\BulletTime.csv");
}
}
};
private Spatial createRobot(Vector3f translation, Vector3f rotation, boolean isKinematic) {
Spatial robotSpatial = assetManager.loadModel("Models/Oto/robot.j3o");
robotSpatial.setLocalTranslation(translation.x, translation.y, translation.z);
robotSpatial.rotate((float) Math.toRadians(rotation.x), (float) Math.toRadians(rotation.y),
(float) Math.toRadians(rotation.z));
CollisionShape mergedMeshShape = CollisionShapeFactory.createGImpactShape(robotSpatial);
//不依靠shape,自行判断
RigidBodyControl robotControl = new RigidBodyControl(mergedMeshShape);
if (isKinematic) {
robotControl.setKinematic(true);
}
robotSpatial.addControl(robotControl);
bulletAppState.getPhysicsSpace().add(robotControl);
this.rootNode.attachChild(robotSpatial);
return robotSpatial;
}
private Spatial createRobotList(Vector3f translation, Vector3f rotation, boolean isKinematic) {
Spatial robotSpatial = assetManager.loadModel("Models/Oto/robot.j3o");
ArrayList<Geometry> geomList = new ArrayList<>();
traverseGeometry((Node) robotSpatial, geomList);
CompoundCollisionShape shape = new CompoundCollisionShape(geomList.size());
for (Geometry geom : geomList) {
CollisionShape mergedMeshShape = CollisionShapeFactory.createGImpactShape(geom);
geom.setLocalTranslation(geom.getWorldTranslation());
geom.setLocalRotation(geom.getWorldRotation());
geom.setLocalScale(geom.getWorldScale());
shape.addChildShape(mergedMeshShape, geom.getWorldTransform());
}
robotSpatial.setLocalTranslation(translation.x, translation.y, translation.z);
robotSpatial.rotate((float) Math.toRadians(rotation.x), (float) Math.toRadians(rotation.y),
(float) Math.toRadians(rotation.z));
RigidBodyControl robotControl = new RigidBodyControl(shape);
if (isKinematic) {
robotControl.setKinematic(true);
}
robotSpatial.addControl(robotControl);
bulletAppState.getPhysicsSpace().add(robotControl);
this.rootNode.attachChild(robotSpatial);
return robotSpatial;
}
private ArrayList<Geometry> traverseGeometry(Node node, ArrayList<Geometry> geomList) {
// 遍历当前节点下的所有子节点
for (int i = 0; i < node.getQuantity(); i++) {
com.jme3.scene.Spatial spatial = node.getChild(i);
if (spatial instanceof Geometry) {
Geometry geometry = (Geometry) spatial;
// 复制几何体以保留变换
Geometry geometryCopy = geometry.clone();
// 应用父节点的全局变换到子节点上
geometryCopy.setLocalTranslation(geometry.getWorldTranslation());
geometryCopy.setLocalRotation(geometry.getWorldRotation());
geometryCopy.setLocalScale(geometry.getWorldScale());
geomList.add(geometryCopy);
} else if (spatial instanceof Node) {
// 如果是 Node 类型,则递归遍历其子节点
traverseGeometry((Node) spatial, geomList); // 递归遍历时传递当前父节点
}
}
return geomList;
}
private Spatial createRobotMesh(Vector3f translation, Vector3f rotation, boolean isKinematic) {
Spatial robotSpatial = assetManager.loadModel("Models/Oto/robot.j3o");
robotSpatial.setLocalTranslation(translation.x, translation.y, translation.z);
robotSpatial.rotate((float) Math.toRadians(rotation.x), (float) Math.toRadians(rotation.y),
(float) Math.toRadians(rotation.z));
CollisionShape meshShape = CollisionShapeFactory.createMeshShape(robotSpatial);
//不依靠shape,自行判断
RigidBodyControl robotControl = new RigidBodyControl(meshShape, 0);
if (isKinematic) {
robotControl.setKinematic(true);
}
robotSpatial.addControl(robotControl);
bulletAppState.getPhysicsSpace().add(robotControl);
this.rootNode.attachChild(robotSpatial);
return robotSpatial;
}
private Spatial createRobotSensor(Vector3f translation, Vector3f rotation) {
Spatial robotSpatial = assetManager.loadModel("Models/Oto/robot.j3o");
robotSpatial.setLocalTranslation(translation.x, translation.y, translation.z);
robotSpatial.rotate((float) Math.toRadians(rotation.x), (float) Math.toRadians(rotation.y),
(float) Math.toRadians(rotation.z));
CollisionShape meshShape = CollisionShapeFactory.createMeshShape(robotSpatial);
//不依靠shape,自行判断
GhostControl robotControl = new GhostControl(meshShape);
robotSpatial.addControl(robotControl);
bulletAppState.getPhysicsSpace().add(robotControl);
this.rootNode.attachChild(robotSpatial);
return robotSpatial;
}
@Override
public void simpleUpdate(float tpf) {
if (init) {
RigidBodyControl bodyControl2 = robotKinematic2.getControl(RigidBodyControl.class);
bodyControl2.setAngularVelocity(new Vector3f(1f, 0, 0));
robotKinematic2.setLocalTranslation(robotKinematic2.getLocalTranslation().add(new Vector3f(-tpf, 0, 0)));
}
}
private void saveFile(String filePath) {
BufferedWriter writer = null;
try {
// 初始化
writer = new BufferedWriter(new FileWriter(filePath, true));
String overlapStateText = "重叠状态";
String timeText = "时间(s)";
// 写入标题
writeContentToCell(writer, overlapStateText, ",");
writeContentToCell(writer, timeText, ",");
String dataTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH.mm.ss"));
writeContentToCell(writer, dataTime, ",");
//换行
writer.newLine();
for (int i = 0; i < Math.min(overlapStateList.size(), timeList.size()); i++) {
writeContentToCell(writer, String.valueOf(overlapStateList.get(i)).replaceAll("\\s", ""),
",");
writeContentToCell(writer, String.valueOf(timeList.get(i)), ",");
writer.newLine();
}
System.out.println("saveFileSucceed");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void writeContentToCell(BufferedWriter writer, String content, String separator) throws IOException {
// 写入内容进去csv
writer.write(content);
writer.write(separator);
}
}
`
robot.zip
Thank you for documenting this issue. I'll take a look at it.
Thank you for documenting this issue. I'll take a look at it.
If there are any new results, looking forward to your reply.
After adding imports and the standard Oto model, I ran the test application. Pressing the "1" key produced an ArrayIndexOutOfBoundsException in the CollisionListener.
The exception was caused by using index1 as an array index when its value was -1.
In the collision event, object A was the robot (a kinematic rigid-body control with a compound shape) and object B was the floor (a static rigid-body control with a box shape).
The javadoc says that getIndex0() returns the triangle index in the shape of object A, provided it is a compound shape. In this particular collision, however, getIndex0() seems to refer to object B (which has a convex shape) whilegetIndex1() (=2435) seems to refer to object A. In other words, the objects are swapped, making the javadoc incorrect.
Unfortunately, I don't see an easy way to tell (in general) whether getIndex0() refers to object A or object B.
I suspect there's a similar issue with the documentation for getPartId0() and getPartId1() in the PhysicsCollisionEvent class.
I will correct the javadoc. And if I find how to to tell when getIndex0() refers to object B, I'll let you know.
Yes,sir,I think what you said is right. But the other problem I have is that I'm creating a CompoundCollisionShape, and its children are all GImpactCollisionShape. Pressing KEY_1 and KEY_2 creates two robots and collides, I use getIndex0 and getIndex1 in PhysicsCollisionListener, and I want to know which child shape caused the collision, Because getIndex0 and getIndex1 are written in the comment like this: If shape is a CompoundCollisionShape, the index identifies a child shape. Then after I use getIndex0 and getIndex1 should is access to the child in the shape of index information, but in the actual Java happened. Lang. ArrayIndexOutOfBoundsException. I have recorded a video and attached a test case that you can test by pressing KEY_1 and KEY_2.
https://github.com/user-attachments/assets/5672174c-0630-4ce7-8498-3120c3daad96
After adding imports and the standard Oto model, I ran the test application. Pressing the "1" key produced an
ArrayIndexOutOfBoundsExceptionin theCollisionListener.The exception was caused by using
index1as an array index when its value was-1.In the collision event, object A was the robot (a kinematic rigid-body control with a compound shape) and object B was the floor (a static rigid-body control with a box shape).
The javadoc says that
getIndex0()returns the triangle index in the shape of object A, provided it is a compound shape. In this particular collision, however,getIndex0()seems to refer to object B (which has a convex shape) whilegetIndex1()(=2435) seems to refer to object A. In other words, the objects are swapped, making the javadoc incorrect.Unfortunately, I don't see an easy way to tell (in general) whether
getIndex0()refers to object A or object B.I suspect there's a similar issue with the documentation for
getPartId0()andgetPartId1()in thePhysicsCollisionEventclass.I will correct the javadoc. And if I find how to to tell when
getIndex0()refers to object B, I'll let you know.
Hello Sir, I think you may have a little deviation from my question. Could you take a look at this comment? I rearranged it. https://github.com/stephengold/Minie/issues/45#issuecomment-2595000347
I'm busy right now, but I haven't forgotten this issue.
I'm busy right now, but I haven't forgotten this issue.
OK, thank you very much.
When I press KEY_1, I get a collision with index0 = -1 and index1 = 2435.
The shape of object 0 is a BoxCollisionShape, a convex shape, for which the index is undefined.
The shape of object 1 is a CompoundCollisionShape, for which the index should be 0 (the child index) but it's not.
I don't know how to solve this.
When I press KEY_1, I get a collision with index0 = -1 and index1 = 2435. The shape of object 0 is a
BoxCollisionShape, a convex shape, for which the index is undefined. The shape of object 1 is aCompoundCollisionShape, for which the index should be 0 (the child index) but it's not. I don't know how to solve this.
Will The index1 be the index of the triangle mesh instead of the child index? Is there any other way to get which child shape of CompoundCollisionShape collided with when the collision occurred?
Will The index1 be the index of the triangle mesh instead of the child index?
Apparently it is.
Is there any other way to get which child shape of CompoundCollisionShape collided with when the collision occurred?
Not that I know of.
Ok, thank you very much. Please let me know if there is a solution to this problem
When I press KEY_1, I get a collision with index0 = -1 and index1 = 2435. The shape of object 0 is a
BoxCollisionShape, a convex shape, for which the index is undefined. The shape of object 1 is aCompoundCollisionShape, for which the index should be 0 (the child index) but it's not. I don't know how to solve this.