Rotate plane

Hi guys!



Today I tested my first JME PHYSICS and I have to say: It is so great! I love playing with falling cubes and stuff.



But now to something different: I try to move a simple plane (a box) with keys. I managed to apply a force to it so that it moves. That works fine, but is very confusing.



What I want to achieve is a working application that allows the user to move the plane with 4 keys. As you can see in my code, I want to place a sphere on it to have a first game. The problem is that all forces are applied to the local coordinates of the object, I guess. So the object moves in an unexpectable way when you press a lot of keys. :wink: I want to apply the force in world coordinates (I hope I got that right) so the plane only moves in X and Z axis.



Excuse my bad explanation, but I'm not a native english speaker… :expressionless:



Hope someone can help me!


import java.util.logging.Level;

import com.jme.app.SimpleGame;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.MaterialState;
import com.jme.util.LoggingSystem;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.util.SimplePhysicsGame;

public class PhysicTest extends SimplePhysicsGame {
    protected void simpleInitGame() {
        // first we will create the floor like in Lesson2
        final DynamicPhysicsNode staticNode = getPhysicsSpace().createDynamicNode();
        rootNode.attachChild( staticNode );

        final Box visualFloor = new Box( "floor", new Vector3f(), 10, 0.25f, 10 );
        staticNode.attachChild( visualFloor );
        visualFloor.getLocalTranslation().set( 0, 0, 0 );
        staticNode.generatePhysicsGeometry();
        staticNode.setAffectedByGravity(false);

        InputAction left = new KeyInputAction() {
            @Override
            public void performAction(InputActionEvent arg0) {
//                System.out.println("left!");
                staticNode.addForce(
                        new Vector3f(0, 1, 0),
                        new Vector3f(1, 0, 0));
                staticNode.addForce(
                        new Vector3f(0, -1, 0),
                        new Vector3f(-1, 0, 0));
            }
        };
        InputAction right = new KeyInputAction() {
            @Override
            public void performAction(InputActionEvent arg0) {
//                System.out.println("right!");
                staticNode.addForce(
                        new Vector3f(0, -1, 0),
                        new Vector3f(1, 0, 0));
                staticNode.addForce(
                        new Vector3f(0, 1, 0),
                        new Vector3f(-1, 0, 0));
            }
        };
        InputAction up = new KeyInputAction() {
            @Override
            public void performAction(InputActionEvent arg0) {
//                System.out.println("up!");
                staticNode.addForce(
                        new Vector3f(0, 0, 1),
                        new Vector3f(0, -1, 0));
                staticNode.addForce(
                        new Vector3f(0, 0, -1),
                        new Vector3f(0, 1, 0));
            }
        };
        InputAction down = new KeyInputAction() {
            @Override
            public void performAction(InputActionEvent arg0) {
//                System.out.println("down!");
                staticNode.addForce(
                        new Vector3f(0, 0, -1),
                        new Vector3f(0, -1, 0));
                staticNode.addForce(
                        new Vector3f(0, 0, 1),
                        new Vector3f(0, 1, 0));
            }
        };
       
        KeyBindingManager keyboard = KeyBindingManager.getKeyBindingManager();
        keyboard.set("left", KeyInput.KEY_H);
        input.addAction(left, "left", false);
        keyboard.set("right", KeyInput.KEY_K);
        input.addAction(right, "right", false);
        keyboard.set("up", KeyInput.KEY_U);
        input.addAction(up, "up", false);
        keyboard.set("down", KeyInput.KEY_J);
        input.addAction(down, "down", false);

//        DynamicPhysicsNode dynamicNode = createSphere();
//        dynamicNode.getLocalTranslation().set( 0, 5, 0 );
       
        pause = false;
    }

    public static void main( String[] args ) {
        LoggingSystem.getLogger().setLevel( Level.WARNING ); // to see the important stuff
        PhysicTest l = new PhysicTest();
        l.setDialogBehaviour(SimpleGame.FIRSTRUN_OR_NOCONFIGFILE_SHOW_PROPS_DIALOG);
        l.start();
    }
}

My first try would be using a Joint instead of applying forces directly. Create a joint with 2 rotational axes, attach it to your box, and set torque and velocity for the axes.

Hm, thanks for that hint. I never thought about Joints before.I’d be happy if you could provide an example for my setup…  :’( I don’t know how to apply it to my box. I created a Joint and a JointAxis like this:


Joint joint = getPhysicsSpace().createJoint();
JointAxis rotAxis = joint.createRotationalAxis();



But what should I do now? How to attach it to my box?

Have a look at the tests please. If you are still stuck post back here.

Sorry, I'm too stupid. I'll browse through Lesson7 of the Tutorials. I hope I can take this step. :slight_smile: Thanks for your help!

Ok, I'm back already. :// I managed to create the first RotationalJointAxis, but when I create the second there is an Exception:


java.lang.UnsupportedOperationException: this implemantation supports two rotational axes only if the second one is relative to the second object, the first axis must be relative to the first object Problematic Joint: com.jmex.physics.impl.ode.joints.OdeJoint@1bbdd48
   at com.jmex.physics.impl.ode.joints.OdeJoint.unsupported(OdeJoint.java:335)
   at com.jmex.physics.impl.ode.joints.OdeJoint.updateJointType(OdeJoint.java:205)
   at com.jmex.physics.impl.ode.OdePhysicsSpace.update(OdePhysicsSpace.java:449)
   at com.jmex.physics.util.SimplePhysicsGame.update(SimplePhysicsGame.java:82)
   at com.jme.app.BaseGame.start(Unknown Source)
   at de.kopis.Scrobble.main(Scrobble.java:160)



This is how I create the axis:


final Joint jointForZ = getPhysicsSpace().createJoint();
final RotationalJointAxis rotationalAxisZ = jointForZ.createRotationalAxis();
rotationalAxisZ.setDirection( new Vector3f( 0, 0, 1 ) );
       
final RotationalJointAxis rotationalAxisX = jointForZ.createRotationalAxis();
rotationalAxisX.setDirection( new Vector3f( 1, 0, 0 ) );

jointForZ.attach( dynamicNode );
jointForZ.setAnchor( new Vector3f( 0, 0, 0 ) );



How can I set the second Axis relative to a second object, when I want to attach the axis to only 1 object? Or did I miss something here?

The second object is the world/space in this case. Simply calling rotationalAxisX.setRelativeToSecondObject( true ) should work.

Yes, you’re right. But I had to set the Minimium/Maximum position of the first joint, too. Now I have another problem.



When I let the plane flow free in my space, then it moves away when adding torque. I want it to stay in place, i.e. to attach the joint axis to a fixed center point. The plane should only rotate around that point.



And I have another problem: When I added a game ball to roll on the plane, the plane is falling down, too. The plane itself is not affected by gravity, but by the mass of the game ball. And soon they both fall down…



I tried to setup a Pyramid right below the plane and 4 stopper boxes in the corners. But after hitting the stoppers the plane jumps off the centered position.



Here’s a screenshot of my setup:







And here’s my code:



import java.util.logging.Level;

import com.jme.app.SimpleGame;
import com.jme.image.Texture;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Cylinder;
import com.jme.scene.shape.Pyramid;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.MaterialState;
import com.jme.scene.state.TextureState;
import com.jme.util.LoggingSystem;
import com.jme.util.TextureManager;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.Joint;
import com.jmex.physics.RotationalJointAxis;
import com.jmex.physics.StaticPhysicsNode;
import com.jmex.physics.util.SimplePhysicsGame;

public class PhysicTest extends SimplePhysicsGame {

    protected void simpleInitGame() {
        // first we will create the floor like in Lesson2
        final DynamicPhysicsNode physicsNode = getPhysicsSpace().createDynamicNode();
        rootNode.attachChild( physicsNode );
       
        final Box visualFloor = new Box( "floor", new Vector3f(), 10, 0.25f, 10 );
        physicsNode.attachChild( visualFloor );
        visualFloor.getLocalTranslation().set( 0, 0, 0 );
        physicsNode.generatePhysicsGeometry();
        physicsNode.setAffectedByGravity(false);
        material(visualFloor, TextureManager.loadTexture("planken.jpg", Texture.FM_LINEAR, Texture.FM_LINEAR));

        //TODO attach center point
        final StaticPhysicsNode centerNode = createStaticSphere();
        centerNode.getLocalTranslation().set(0, -1.5f, 0);
        centerNode.generatePhysicsGeometry();
       
        final StaticPhysicsNode stopper1 = createStaticBox();
        rootNode.attachChild( stopper1 );
        stopper1.getLocalTranslation().set( 9.5f, -2, 9.5f );
        stopper1.generatePhysicsGeometry();
        material(stopper1, TextureManager.loadTexture("brick03.jpg", Texture.FM_LINEAR, Texture.FM_LINEAR));
        final StaticPhysicsNode stopper2 = createStaticBox();
        rootNode.attachChild( stopper2 );
        stopper2.getLocalTranslation().set( -9.5f, -2, 9.5f );
        stopper2.generatePhysicsGeometry();
        material(stopper2, TextureManager.loadTexture("brick03.jpg", Texture.FM_LINEAR, Texture.FM_LINEAR));
        final StaticPhysicsNode stopper3 = createStaticBox();
        rootNode.attachChild( stopper3 );
        stopper3.getLocalTranslation().set( -9.5f, -2, -9.5f );
        stopper3.generatePhysicsGeometry();
        material(stopper3, TextureManager.loadTexture("brick03.jpg", Texture.FM_LINEAR, Texture.FM_LINEAR));
        final StaticPhysicsNode stopper4 = createStaticBox();
        rootNode.attachChild( stopper4 );
        stopper4.getLocalTranslation().set( 9.5f, -2, -9.5f );
        stopper4.generatePhysicsGeometry();
        material(stopper4, TextureManager.loadTexture("brick03.jpg", Texture.FM_LINEAR, Texture.FM_LINEAR));
       
        final DynamicPhysicsNode ball = createSphere();
//        color(ball, new ColorRGBA(0, 0, 1f, 1f));
        ball.getLocalTranslation().set(0, 1, 0);
        material(ball, TextureManager.loadTexture("chromic.jpg", Texture.FM_LINEAR, Texture.FM_LINEAR));
       
        final Joint joint = getPhysicsSpace().createJoint();
        final RotationalJointAxis rotAxisZ = joint.createRotationalAxis();
        rotAxisZ.setDirection(new Vector3f(0, 0, 1));
        rotAxisZ.setPositionMaximum(15 * FastMath.DEG_TO_RAD);
        rotAxisZ.setPositionMinimum(-15 * FastMath.DEG_TO_RAD);
        joint.setAnchor(new Vector3f());
        final RotationalJointAxis rotAxisX = joint.createRotationalAxis();
        rotAxisX.setDirection(new Vector3f(1, 0, 0));
        rotAxisX.setRelativeToSecondObject(true);
        joint.setAnchor(new Vector3f());

        InputAction left = new KeyInputAction() {
            @Override
            public void performAction(InputActionEvent arg0) {
//                System.out.println("left!");
//                staticNode.addForce(
//                        new Vector3f(0, 1, 0),
//                        new Vector3f(1, 0, 0));
//                staticNode.addForce(
//                        new Vector3f(0, -1, 0),
//                        new Vector3f(-1, 0, 0));
               
                physicsNode.addTorque(new Vector3f(0, 0, -5));
            }
        };
        InputAction right = new KeyInputAction() {
            @Override
            public void performAction(InputActionEvent arg0) {
//                System.out.println("right!");
//                staticNode.addForce(
//                        new Vector3f(0, -1, 0),
//                        new Vector3f(1, 0, 0));
//                staticNode.addForce(
//                        new Vector3f(0, 1, 0),
//                        new Vector3f(-1, 0, 0));
                physicsNode.addTorque(new Vector3f(0, 0, 5));
            }
        };
        InputAction up = new KeyInputAction() {
            @Override
            public void performAction(InputActionEvent arg0) {
//                System.out.println("up!");
//                staticNode.addForce(
//                        new Vector3f(0, 0, 1),
//                        new Vector3f(0, -1, 0));
//                staticNode.addForce(
//                        new Vector3f(0, 0, -1),
//                        new Vector3f(0, 1, 0));
                physicsNode.addTorque(new Vector3f(-5, 0, 0));
            }
        };
        InputAction down = new KeyInputAction() {
            @Override
            public void performAction(InputActionEvent arg0) {
//                System.out.println("down!");
//                staticNode.addForce(
//                        new Vector3f(0, 0, -1),
//                        new Vector3f(0, -1, 0));
//                staticNode.addForce(
//                        new Vector3f(0, 0, 1),
//                        new Vector3f(0, 1, 0));
                physicsNode.addTorque(new Vector3f(5, 0, 0));
            }
        };
       
        KeyBindingManager keyboard = KeyBindingManager.getKeyBindingManager();
        keyboard.set("left", KeyInput.KEY_H);
        input.addAction(left, "left", false);
        keyboard.set("right", KeyInput.KEY_K);
        input.addAction(right, "right", false);
        keyboard.set("up", KeyInput.KEY_U);
        input.addAction(up, "up", false);
        keyboard.set("down", KeyInput.KEY_J);
        input.addAction(down, "down", false);
       
//        showNormals = true;
//        showDepth = true;
//        showBounds = true;
//        showPhysics = true;
        pause = false;
    }

    private void color( Spatial spatial, ColorRGBA color ) {
        final MaterialState materialState = display.getRenderer().createMaterialState();
        materialState.setDiffuse( color );
        if ( color.a < 1 ) {
            final AlphaState alphaState = display.getRenderer().createAlphaState();
            alphaState.setEnabled( true );
            alphaState.setBlendEnabled( true );
            alphaState.setSrcFunction( AlphaState.SB_SRC_ALPHA );
            alphaState.setDstFunction( AlphaState.DB_ONE_MINUS_SRC_ALPHA );
            spatial.setRenderState( alphaState );
            spatial.setRenderQueueMode( Renderer.QUEUE_TRANSPARENT );
        }
        spatial.setRenderState( materialState );
    }

    private void material( Spatial spatial, Texture tex) {
        final TextureState textureState = display.getRenderer().createTextureState();
        textureState.setTexture(tex);
        spatial.setRenderState( textureState );
    }

    private DynamicPhysicsNode createBox() {
        DynamicPhysicsNode dynamicNode = getPhysicsSpace().createDynamicNode();
        rootNode.attachChild( dynamicNode );
        final Box visualFallingBox = new Box( "falling box", new Vector3f(), 1f, 1f, 1f );
        dynamicNode.attachChild( visualFallingBox );
        dynamicNode.generatePhysicsGeometry();
        return dynamicNode;
    }

    private StaticPhysicsNode createStaticBox() {
        StaticPhysicsNode dynamicNode = getPhysicsSpace().createStaticNode();
        rootNode.attachChild( dynamicNode );
        final Box visualFallingBox = new Box( "static box", new Vector3f(), 1f, 1f, 1f );
        dynamicNode.attachChild( visualFallingBox );
        dynamicNode.generatePhysicsGeometry();
        return dynamicNode;
    }

    private DynamicPhysicsNode createSphere() {
        DynamicPhysicsNode dynamicNode = getPhysicsSpace().createDynamicNode();
        rootNode.attachChild( dynamicNode );
        final Sphere visualFallingBox = new Sphere( "falling box", new Vector3f(), 16, 16, 1 );
        dynamicNode.attachChild( visualFallingBox );
        dynamicNode.generatePhysicsGeometry();
        return dynamicNode;
    }

    private StaticPhysicsNode createStaticSphere() {
        StaticPhysicsNode dynamicNode = getPhysicsSpace().createStaticNode();
        rootNode.attachChild( dynamicNode );
        final Sphere visualFallingBox = new Sphere( "static sphere", new Vector3f(), 16, 16, 1 );
        dynamicNode.attachChild( visualFallingBox );
        dynamicNode.generatePhysicsGeometry();
        return dynamicNode;
    }

    public static void main( String[] args ) {
        LoggingSystem.getLogger().setLevel( Level.WARNING ); // to see the important stuff
        PhysicTest l = new PhysicTest();
        l.setDialogBehaviour(SimpleGame.FIRSTRUN_OR_NOCONFIGFILE_SHOW_PROPS_DIALOG);
        l.start();
    }
}

To shorten my explanations I have simply added a test to CVS: have a look at com.jmetest.physics.MarbleTest - it should do what you want to achieve.



To tell you what the problem was:

  • your joint was not attached to the floor
  • you were still using forces on the node and not on the joint/axes

Today I managed to get the CVS up and running. I checked JME and Physics2 out and I managed to run the examples in Eclipse. Thanks for your help again! I hope to get stuff going soon.



Hehe, and I tried the Projected Water examples, too. Nice work, really sweet. I think I try to build some demos for my personal use now.