jME3 Door and Hinge Physics Setup

Hello!



Decided to try something different today and got sorta stumped on how to proceed/debug a hinge physics node. The code is attached below. The first setup is just the standard hinge demo that’s outlined on the wiki. I just wanted to have it there to see if things are working right. However, I’m having some issues:


  1. mmmm, hard one to describe. If I setup the hinge demo just as stated in the wiki and let it sit for a second, I’ll notice the pendulum box shift over very slightly and it will not respond to motor commands. Not sure why this is. If I jump on it immediately with the enableMotor, it’s fine.


  2. Perhaps related. I have the “spinning ghetto door of doom” below it and it seems to have a similar problem at the start. But, it also has problems when I turn the motor off as well. It will spin around the sphere/pole/hinge for a while and then abruptly stop. At this point it’s done and won’t respond to anything.


  3. How would I go about limiting the motion of the hinge so that it responds like a normal door? setLimit is… Confusing as to what it’s doing. I looked at the reference manual for JBullet, but I didn’t see it outlined.



    [EDIT]: TestPhysicsHingeJoint does the same thing as (1). If I don’t get going and hit h or k to enable the motor, the pendulum will shift and it’s locked… Odd, I don’t recall this happening the last time I ran the test.



    [EDIT2]: Googled and got: http://code.google.com/p/jbullet-jme/issues/detail?id=34 - Was this an issue before and maybe the code change didn’t make it into jME3?



    So confused XD

    ~FlaH



    [java]

    package com.flah.playground.door;



    import java.util.Iterator;



    import com.jme3.app.SimpleApplication;

    import com.jme3.bullet.BulletAppState;

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

    import com.jme3.bullet.joints.PhysicsHingeJoint;

    import com.jme3.bullet.nodes.PhysicsNode;

    import com.jme3.collision.CollisionResult;

    import com.jme3.collision.CollisionResults;

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

    import com.jme3.math.Vector2f;

    import com.jme3.math.Vector3f;

    import com.jme3.scene.Geometry;

    import com.jme3.scene.shape.Box;

    import com.jme3.scene.shape.Sphere;



    public class PhysicsDoorTest extends SimpleApplication {



    private BulletAppState bulletAppState;



    private Geometry mouseHoverTarget;

    private Ray mouseRay = new Ray();

    private CollisionResults mouseRayResults = new CollisionResults();



    private Geometry pendGeom;



    private PhysicsHingeJoint joint;

    private PhysicsHingeJoint joint2;



    public static void main(String[] args) {

    PhysicsDoorTest app = new PhysicsDoorTest();

    app.start();

    }



    @Override

    public void simpleInitApp() {



    flyCam.setMoveSpeed(20f);

    inputManager.deleteMapping(“FLYCAM_Up”);

    inputManager.deleteMapping(“FLYCAM_Down”);

    inputManager.deleteMapping(“FLYCAM_Left”);

    inputManager.deleteMapping(“FLYCAM_Right”);



    mouseInput.setCursorVisible(true);



    bulletAppState = new BulletAppState();

    bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);

    stateManager.attach(bulletAppState);



    // Non-Selected Material

    Material mat = new Material(assetManager, “Common/MatDefs/Misc/SolidColor.j3md”);

    mat.setColor(“m_Color”, ColorRGBA.Red);



    // Hinge Geom

    Sphere hingeSphere = new Sphere(32, 32, 0.1f);

    Geometry hingeGeom = new Geometry(“HingeGeom”, hingeSphere);

    hingeGeom.setMaterial(mat);



    PhysicsNode hingeNode = new PhysicsNode(new BoxCollisionShape( new Vector3f(0.1f,0.1f,0.1f)) , 0 );

    hingeNode.setLocalTranslation(new Vector3f(0f,0,0f));

    hingeNode.attachDebugShape(assetManager);

    // hingeNode.attachChild(hingeGeom);

    rootNode.attachChild(hingeNode);

    bulletAppState.getPhysicsSpace().add(hingeNode);



    // Pendulum Geom

    Sphere pendSphere = new Sphere(32, 32, 0.2f);

    pendGeom = new Geometry(“PendGeom”, pendSphere);

    pendGeom.setMaterial(mat);



    PhysicsNode pendulumNode = new PhysicsNode(new BoxCollisionShape( new Vector3f(0.3f,0.3f,0.3f)) , 2 );

    pendulumNode.setLocalTranslation(new Vector3f(1f,0,0f));

    pendulumNode.attachDebugShape(assetManager);

    pendulumNode.attachChild(pendGeom);

    rootNode.attachChild(pendulumNode);

    bulletAppState.getPhysicsSpace().add(pendulumNode);



    joint = new PhysicsHingeJoint(

    hingeNode, pendulumNode, // two nodes

    Vector3f.ZERO, new Vector3f(1f,0,0f), // two pivot points

    Vector3f.UNIT_Z, Vector3f.UNIT_Z); // two DOF axes

    bulletAppState.getPhysicsSpace().add(joint);



    // Attempting to make a door now…

    Sphere hingeSphere2 = new Sphere(32, 32, 0.1f);

    Geometry hingeGeom2 = new Geometry(“DoorHinge”, hingeSphere2);

    hingeGeom2.setMaterial(mat);



    PhysicsNode doorHingeNode = new PhysicsNode(new BoxCollisionShape(new Vector3f(0.2f, 0.2f, 0.2f)), 0);

    doorHingeNode.setLocalTranslation(0, -3, 0);

    doorHingeNode.attachDebugShape(assetManager);

    // doorHingeNode.attachChild(hingeGeom2);

    rootNode.attachChild(doorHingeNode);

    bulletAppState.getPhysicsSpace().add(doorHingeNode);



    Box boxDoor = new Box(0.1f, 0.05f, 0.1f);

    Geometry doorGeom = new Geometry(“Door”, boxDoor);

    doorGeom.setMaterial(mat);



    PhysicsNode doorNode = new PhysicsNode(new BoxCollisionShape(new Vector3f(0.1f, 0.1f, 0.1f)), 1);

    doorNode.setLocalTranslation(0.5f, -3, 0);

    doorNode.attachDebugShape(assetManager);

    // doorNode.attachChild(doorGeom);

    rootNode.attachChild(doorNode);

    bulletAppState.getPhysicsSpace().add(doorNode);



    joint2 = new PhysicsHingeJoint(

    doorHingeNode, doorNode, // two nodes

    new Vector3f(0f, 0f, 0f), new Vector3f(-0.5f,0f,0f), // two pivot points

    Vector3f.UNIT_Y, Vector3f.UNIT_Y); // two DOF axes

    bulletAppState.getPhysicsSpace().add(joint2);



    // joint2.setLimit(0, 2);

    System.out.println(joint2.getHingeAngle());



    setupKeys();

    }



    private void setupKeys() {

    inputManager.addMapping(“MotorOn”, new KeyTrigger(KeyInput.KEY_G));

    inputManager.addMapping(“MotorOff”, new KeyTrigger(KeyInput.KEY_T));



    inputManager.addListener(motorListener, new String[]{“MotorOn”, “MotorOff”});

    }



    private ActionListener motorListener = new ActionListener() {

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

    if(name.equals(“MotorOn”) && keyPressed) {

    joint.enableMotor(true, 1, .1f);

    joint2.enableMotor(true, 1, .1f);

    }

    if(name.equals(“MotorOff”) && keyPressed) {

    joint.enableMotor(false, 0, 0);

    joint2.enableMotor(false, 0, 0);

    }

    }

    };



    @Override

    public void simpleUpdate(float tpf) {

    super.simpleUpdate(tpf);

    rootNode.updateLogicalState(tpf);

    rootNode.updateGeometricState();



    System.out.println(joint2.getHingeAngle());



    // Update Mouse Hover

    mouseHoverTarget = updateMouseRay();

    }



    private Geometry updateMouseRay() {

    if(mouseHoverTarget != null) {

    // mouseHoverTarget.deHighlight();

    mouseHoverTarget.getMaterial().getParam(“m_Color”).setValue(ColorRGBA.Red);

    }



    Vector2f mouseCoords = new Vector2f(inputManager.getCursorPosition());

    mouseRay = new Ray(cam.getWorldCoordinates(mouseCoords, 0),

    cam.getWorldCoordinates(mouseCoords, 1).subtractLocal(

    cam.getWorldCoordinates(mouseCoords, 0)).normalizeLocal());



    mouseRayResults.clear();

    pendGeom.collideWith(mouseRay, mouseRayResults);



    Iterator<CollisionResult> itr = mouseRayResults.iterator();

    while(itr.hasNext()) {

    Geometry mouseHoverGeom = itr.next().getGeometry();

    if(mouseHoverGeom.getName().equals(“PendGeom”)) {

    // mouseHoverGeom.setHighlighted();

    mouseHoverGeom.getMaterial().getParam(“m_Color”).setValue(ColorRGBA.Yellow);

    return mouseHoverGeom;

    }

    }



    return null;

    }

    }

    [/java]

The physics nodes get deactivated to save computation time after a while, just activate() them to make them react to forces again.

Hello again!



oh hah. I put the activate on just before enabling the motor since I’m not too concerned with the freewheeling of the door. Though it is pretty cool if I enable the motor and immediately disable it to watch the hinge bounce off it’s limit! :slight_smile:



New question though, so I set the limit on the joint2 where I have it commented out. I changed it to setLimit(0, FastMath.PI/2); so that it would open 90 degrees. Watching the angle of the joint, I noticed that even with the limits it’s allowed to flop past the limit by about 0.056039035 too much on both sides of the limit. Is this expected?



Slightly different question: Soooooo, I was planning on making this test to be able to click on the box to make it move. Meh, no problem, I’ve got those bases covered. The thing is, I realized, “oh crap, the geometry I need to click is attached to that physicsnode!” This makes it interesting to do a picking ray test since attempting collideWith on a node containing anything more than triangle data goes BOOM. And since all the doors or any other ‘whatevers’ I make will likely also be attached to their own physicsNode, this has me stumped on how to handle the scenegraph easily for ray picking. Is there some trick I’m forgetting?



I have plenty more questions on how to play with this hinge node, but basic functionality is plenty right now. Honestly, it’s pretty damn cool! :smiley:



Thanks!

~FlaH

Hm? The geometry picking should work as normal, what goes “boom”?

Hello again!



Was just about to post the error but realized what it could be. I output all the nodes in the scenegraph and noticed the debugShapes I’d attached. I commented those out and it works without errors. With the debugShape attached, it was erroring out with:



[java]

SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

java.lang.UnsupportedOperationException: Only ‘Triangles’ mesh mode is supported.

at com.jme3.collision.bih.BIHTree.(BIHTree.java:130)

at com.jme3.collision.bih.BIHTree.(BIHTree.java:135)

at com.jme3.scene.Mesh.createCollisionData(Mesh.java:465)

at com.jme3.scene.Mesh.collideWith(Mesh.java:476)

at com.jme3.scene.Geometry.collideWith(Geometry.java:232)

at com.jme3.scene.Node.collideWith(Node.java:498)

at com.jme3.scene.Node.collideWith(Node.java:498)

at com.jme3.scene.Node.collideWith(Node.java:498)

at com.flah.playground.door.PhysicsDoorTest.updateMouseRay(PhysicsDoorTest.java:214)

at com.flah.playground.door.PhysicsDoorTest.simpleUpdate(PhysicsDoorTest.java:198)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:208)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:144)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:141)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:198)

at java.lang.Thread.run(Unknown Source)

[/java]



So that’s all good and well, should be easy enough to do picking now. Just that you can never attach debugShapes WITH the picking code running. Wonder if there would be a way to ignore debug shapes on collideWith?



Also, any ideas on the setLimit exceeding? Or is this expected?



Posting my goofy Test class below for giggles, with debugShapes commented out:



[java]

public class PhysicsDoorTest extends SimpleApplication {



private BulletAppState bulletAppState;



private Geometry mouseHoverTarget;

private Ray mouseRay = new Ray();

private CollisionResults mouseRayResults = new CollisionResults();



private Geometry pendGeom;

private Geometry doorGeom;



private PhysicsHingeJoint joint;

private PhysicsHingeJoint joint2;



private PhysicsNode doorNode;



public static void main(String[] args) {

PhysicsDoorTest app = new PhysicsDoorTest();

app.start();

}



@Override

public void simpleInitApp() {



flyCam.setMoveSpeed(20f);

flyCam.setEnabled(false);



mouseInput.setCursorVisible(true);



bulletAppState = new BulletAppState();

bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);

stateManager.attach(bulletAppState);



// Non-Selected Material

Material mat = new Material(assetManager, “Common/MatDefs/Misc/SolidColor.j3md”);

mat.setColor(“m_Color”, ColorRGBA.Red);



// Door Material

Material matDoor = new Material(assetManager, “Common/MatDefs/Misc/SolidColor.j3md”);

matDoor.setColor(“m_Color”, ColorRGBA.Red);



// Standard Wireframe Material

Material wireMat = new Material(assetManager, “Common/MatDefs/Misc/WireColor.j3md”);

wireMat.setColor(“m_Color”, ColorRGBA.Green);



// Hinge Geom

Sphere hingeSphere = new Sphere(32, 32, 0.1f);

Geometry hingeGeom = new Geometry(“HingeGeom”, hingeSphere);

hingeGeom.setMaterial(mat);



PhysicsNode hingeNode = new PhysicsNode(new BoxCollisionShape( new Vector3f(0.1f,0.1f,0.1f)) , 0 );

hingeNode.setName(“PendHengePN”);

hingeNode.setLocalTranslation(new Vector3f(0f,0,0f));

// hingeNode.attachDebugShape(assetManager);

hingeNode.attachChild(hingeGeom);

rootNode.attachChild(hingeNode);

bulletAppState.getPhysicsSpace().add(hingeNode);



// Pendulum Geom

Sphere pendSphere = new Sphere(32, 32, 0.2f);

pendGeom = new Geometry(“PendGeom”, pendSphere);

pendGeom.setMaterial(mat);



PhysicsNode pendulumNode = new PhysicsNode(new BoxCollisionShape( new Vector3f(0.3f,0.3f,0.3f)) , 2 );

pendulumNode.setName(“PendNodePN”);

pendulumNode.setLocalTranslation(new Vector3f(1f,0,0f));

// pendulumNode.attachDebugShape(assetManager);

pendulumNode.attachChild(pendGeom);

rootNode.attachChild(pendulumNode);

bulletAppState.getPhysicsSpace().add(pendulumNode);



joint = new PhysicsHingeJoint(

hingeNode, pendulumNode, // two nodes

Vector3f.ZERO, new Vector3f(1f,0,0f), // two pivot points

Vector3f.UNIT_Z, Vector3f.UNIT_Z); // two DOF axes

bulletAppState.getPhysicsSpace().add(joint);



// Attempting to make a door now…

Sphere hingeSphere2 = new Sphere(32, 32, 0.1f);

Geometry hingeGeom2 = new Geometry(“DoorHinge”, hingeSphere2);

hingeGeom2.setMaterial(mat);



PhysicsNode doorHingeNode = new PhysicsNode(new BoxCollisionShape(new Vector3f(0.2f, 0.2f, 0.2f)), 0);

doorHingeNode.setName(“DoorHingePN”);

doorHingeNode.setLocalTranslation(0, -3, 0);

// doorHingeNode.attachDebugShape(assetManager);

doorHingeNode.attachChild(hingeGeom2);

rootNode.attachChild(doorHingeNode);

bulletAppState.getPhysicsSpace().add(doorHingeNode);



Box boxDoor = new Box(0.35f, 0.35f, 0.1f);

doorGeom = new Geometry(“Door”, boxDoor);

doorGeom.setMaterial(matDoor);



doorNode = new PhysicsNode(new BoxCollisionShape(new Vector3f(0.1f, 0.1f, 0.1f)), 3);

doorNode.setName(“DoorNodePN”);

doorNode.setLocalTranslation(0.5f, -3, 0);

// doorNode.attachDebugShape(assetManager);

doorNode.attachChild(doorGeom);

rootNode.attachChild(doorNode);

bulletAppState.getPhysicsSpace().add(doorNode);



joint2 = new PhysicsHingeJoint(

doorHingeNode, doorNode, // two nodes

new Vector3f(0f, 0f, 0f), new Vector3f(-0.5f,0f,0f), // two pivot points

Vector3f.UNIT_Y, Vector3f.UNIT_Y); // two DOF axes

bulletAppState.getPhysicsSpace().add(joint2);



joint2.setLimit(0, FastMath.PI/2);

System.out.println(joint2.getHingeAngle());



setupKeys();



for (int i = 0; i < rootNode.getChildren().size(); i++) {

System.out.println(rootNode.getChild(i));

PhysicsNode r = (PhysicsNode) rootNode.getChild(i);

for(int j = 0; j < r.getChildren().size(); j++) {

System.out.println(r.getChild(j));

}

}

}



private void setupKeys() {

inputManager.addMapping(“MotorOn”, new KeyTrigger(KeyInput.KEY_G));

inputManager.addMapping(“MotorOff”, new KeyTrigger(KeyInput.KEY_T));

inputManager.addMapping(“CloseDoor”, new KeyTrigger(KeyInput.KEY_H));

inputManager.addMapping(“Deactivate”, new KeyTrigger(KeyInput.KEY_B));



inputManager.addMapping(“MotorMouse”, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));



inputManager.addListener(motorListener, new String[]{“MotorOn”, “MotorOff”, “CloseDoor”, “Deactivate”, “MotorMouse”});

}



private ActionListener motorListener = new ActionListener() {

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

if(name.equals(“MotorOn”) && keyPressed) {

doorNode.activate();

joint.enableMotor(true, 1, .1f);

joint2.enableMotor(true, 1, .1f);

}

if(name.equals(“CloseDoor”) && keyPressed) {

doorNode.activate();

joint2.enableMotor(true, -1, .1f);

}

if(name.equals(“MotorOff”) && keyPressed) {

joint.enableMotor(false, 0, 0);

joint2.enableMotor(false, 0, 0);

}

if(name.equals(“Deactivate”) && keyPressed) {

// doorNode.

}

if(name.equals(“MotorMouse”) && keyPressed) {

if(mouseHoverTarget != null && mouseHoverTarget.getName().equals(“Door”)) {

doorNode.activate();

if(joint2.getHingeAngle() >= FastMath.PI/2) {

joint2.enableMotor(true, -2, .1f);

}

else {

joint2.enableMotor(true, 2, .1f);

}

}

}

}

};



@Override

public void simpleUpdate(float tpf) {

super.simpleUpdate(tpf);

rootNode.updateLogicalState(tpf);

rootNode.updateGeometricState();



// System.out.println(joint2.getHingeAngle());

// System.out.println(joint2.getAppliedImpulse());



// Update Mouse Hover

// System.out.println(mouseHoverTarget);

mouseHoverTarget = updateMouseRay();

}



private Geometry updateMouseRay() {

if(mouseHoverTarget != null) {

// mouseHoverTarget.deHighlight();

mouseHoverTarget.getMaterial().getParam(“m_Color”).setValue(ColorRGBA.Red);

}



Vector2f mouseCoords = new Vector2f(inputManager.getCursorPosition());

// System.out.println(mouseCoords);

mouseRay = new Ray(cam.getWorldCoordinates(mouseCoords, 0),

cam.getWorldCoordinates(mouseCoords, 1).subtractLocal(

cam.getWorldCoordinates(mouseCoords, 0)).normalizeLocal());



mouseRayResults.clear();

rootNode.collideWith(mouseRay, mouseRayResults);



Iterator itr = mouseRayResults.iterator();

while(itr.hasNext()) {

Geometry mouseHoverGeom = itr.next().getGeometry();

if(mouseHoverGeom.getName().equals(“Door”)) {

// mouseHoverGeom.setHighlighted();

mouseHoverGeom.getMaterial().getParam(“m_Color”).setValue(ColorRGBA.Yellow);

return mouseHoverGeom;

}

}



return null;

}

}

[/java]

Not really, but the picking should just spawn a warning, not an exception, really… @Momoko_Fan?

heh, good to know that wasn’t intended. It would be nice to have the debug shapes available for the certain chance that I’m going to need them when I totally botch something! :stuck_out_tongue:



I was playing more with the limit and I had a derp derp moment and realized that the door passes it’s set limit because of the frequency of the physics update.



Would it make sense to build a controller, attached to the door physics node, that keeps tabs on the joint’s motion? I’m thinking this would be a way to add some more natural movement to the door as well so that it opens slowly rather than all at once. But is there something built in that already does this?



Thanks!

~FlaH

The joints have the methods you see… So there is no .makeDoorType() method, no :stuck_out_tongue:

haha. Well, that would’ve been really convenient! :stuck_out_tongue:



Alright, I’ll see if I can cook something up a bit later. I’ll definitely be looking at getting some finer control on it’s movement once I start making things more ‘pretty.’ But for the moment, I’ve just gotta get things to function right. ~ And together. heh XD



Thanks!

~FlaH