Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PhysicsCollisionEvent getIndex0 and getIndex1 with CompoundCollisionShape #45

Open
hazidh opened this issue Jan 12, 2025 · 11 comments
Open

Comments

@hazidh
Copy link

hazidh commented Jan 12, 2025

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

}
`
image
robot.zip

@stephengold
Copy link
Owner

Thank you for documenting this issue. I'll take a look at it.

@hazidh
Copy link
Author

hazidh commented Jan 13, 2025

Thank you for documenting this issue. I'll take a look at it.

If there are any new results, looking forward to your reply.

@stephengold
Copy link
Owner

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.

@hazidh
Copy link
Author

hazidh commented Jan 16, 2025

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.
image

Test.mp4

Test.zip

@hazidh
Copy link
Author

hazidh commented Feb 7, 2025

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.

Hello Sir, I think you may have a little deviation from my question. Could you take a look at this comment? I rearranged it.
#45 (comment)

@stephengold
Copy link
Owner

I'm busy right now, but I haven't forgotten this issue.

@hazidh
Copy link
Author

hazidh commented Feb 8, 2025

I'm busy right now, but I haven't forgotten this issue.

OK, thank you very much.

@stephengold
Copy link
Owner

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.

@hazidh
Copy link
Author

hazidh commented Feb 17, 2025

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.

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?

@stephengold
Copy link
Owner

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.

@hazidh
Copy link
Author

hazidh commented Feb 17, 2025

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 a CompoundCollisionShape, for which the index should be 0 (the child index) but it's not. I don't know how to solve this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants