Is there a difference between geometry and node translation operations?

Brand new to jMonkey, and I’m trying to move from working with geometry translations, which I seem to have down, to node translations, which seem to…well, go insane.



If you look in the updateLocalTranslation() method of my MobileObject class, the first two lines use geometry translations. Using WASD keys I can nicely move the human cube around with continuous drifting, in any of the Y and/or Z directions.



When I comment those two lines out and uncomment the last two which use node translations, I had thought it’d work exactly the same. Instead, within milliseconds of pressing one of the 4 keys, the System.out.println("MobileObject.updateLocalTranslation(): translationVelocities: "+translationVelocities); statement clearly shows that the velocity for that direction climbs to infinity. I can scroll up the log to see it progressing from 0 to infinity in the proper direction. So, why is there this immense difference in velocities between geometry and node translations? Thanks!



Notes:

  1. MyApplication is just a copy/paste of SimpleApplication, from which I’ve removed the FlyByCam and just use cam instead.
  2. Human extends MobileObject, and right now simply calls the super constructors, so no need to include.



    [java]package mygame;





    import mygame.MobileObjects.*;

    import com.jme3.input.KeyInput;

    import com.jme3.input.controls.ActionListener;

    import com.jme3.input.controls.AnalogListener;

    import com.jme3.input.controls.KeyTrigger;

    import com.jme3.material.Material;

    import com.jme3.math.ColorRGBA;

    import com.jme3.math.Vector3f;

    import com.jme3.scene.CameraNode;

    import com.jme3.scene.Geometry;

    import com.jme3.scene.Node;

    import com.jme3.scene.control.CameraControl.ControlDirection;

    import com.jme3.scene.shape.Box;





    public class Main extends MyApplication {



    //Class level variables

    protected Geometry playerBody;

    protected Node playerNode;

    CameraNode cameraNode;

    Geometry geom;

    Boolean isRunning = true;

    Spaceship spaceship;

    Human human;



    public static void main(String[] args) {

    Main app = new Main();

    app.start();

    }



    /**
  • Initializes the scene components */

    @Override

    public void simpleInitApp() {

    initCamera();

    initGeometry();

    initKeys();

    }



    @Override

    public void simpleUpdate(float tpf) {

    human.updateVelocity(tpf);

    }





    /**
  • Set up the custom keybindings for the scene */

    private void initKeys() {

    // You can map one or several inputs to one named action

    inputManager.addMapping("Pause", new KeyTrigger(KeyInput.KEY_P));

    inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));

    inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));

    inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));

    inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));

    inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE));



    // Add the names to the action listener.

    inputManager.addListener(actionListener, new String[]{"Pause"});

    inputManager.addListener(analogListener, new String[]{"Left", "Right", "Rotate", "Up", "Down"});



    }

    private ActionListener actionListener = new ActionListener() {



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

    if (name.equals("Pause") && !keyPressed) {

    isRunning = !isRunning;

    }

    }

    };

    private AnalogListener analogListener = new AnalogListener() {



    public void onAnalog(String name, float value, float ftp) {



    if (isRunning) {

    if (name.equals("Rotate")) {

    human.updateVelocity(null, new Vector3f(0,1,0), ftp);

    }

    if (name.equals("Right")) {

    human.updateVelocity(new Vector3f(-value, 0, 0), null, ftp);

    }

    if (name.equals("Left")) {

    human.updateVelocity(new Vector3f(value, 0, 0), null, ftp);

    }

    if (name.equals("Up")) {

    human.updateVelocity(new Vector3f(0, 0, value), null, ftp);

    }

    if (name.equals("Down")) {

    human.updateVelocity(new Vector3f(0, 0, -value), null, ftp);

    }

    } else {

    System.out.println("Press P to unpause.");

    }

    }

    };



    /**
  • Set up the camera for the scene */

    private void initCamera(){

    cameraNode = new CameraNode("cameraNode", cam);

    cameraNode.setControlDir(ControlDirection.SpatialToCamera);

    }



    /**
  • Sets up the geometry for the scene /

    private void initGeometry() {



    //Create a red “player body”

    Box playerBox = new Box(Vector3f.ZERO, 1, 1, 1);

    playerBody = new Geometry(“Player”, playerBox);

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

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

    playerBody.setMaterial(mat);



    //attach the player geometry to it’s own node, and attach that to the root node

    playerNode = new Node(“playerNode”);

    rootNode.attachChild(playerNode);

    playerNode.attachChild(playerBody);

    human = new Human(playerBody, playerNode);



    //attach the camera to the player’s node



    playerNode.attachChild(cameraNode);



    cameraNode.setLocalTranslation(0, 0, -40);

    playerBody.setLocalTranslation(0, 0, 5);



    //Create green “ground”

    Box ground = new Box(Vector3f.ZERO, 1000, 0, 1000);

    Geometry groundGeom = new Geometry(“Box2”, ground);

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

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

    groundGeom.setMaterial(mat3);

    rootNode.attachChild(groundGeom);

    groundGeom.setLocalTranslation(0, -10, 0);

    }

    }

    [/java]



    [java]/

  • To change this template, choose Tools | Templates
  • and open the template in the editor.

    */

    package mygame.MobileObjects;



    import com.jme3.math.Vector3f;

    import com.jme3.scene.Geometry;

    import com.jme3.scene.Node;

    /**

    *
  • @author Jonathan Lamb

    */

    public class MobileObject {



    public Geometry geometry;

    public Node parentNode;

    private Vector3f translationVelocities;

    private Vector3f rotationVelocities;



    protected MobileObject(){



    }



    public MobileObject(Geometry geometry, Node parentNode) {

    this.geometry = geometry;

    this.parentNode = parentNode;

    this.translationVelocities = parentNode.getLocalTranslation();

    //this.translationVelocities.z = .0001f;

    this.rotationVelocities = new Vector3f();

    }



    //update location with any new changes

    public void updateVelocity(float timePerFrame){

    updateVelocity(null, null, timePerFrame);

    }

    public void updateVelocity(Vector3f translationVelocityChanges, Vector3f rotationVelocityChanges, float timePerFrame) {



    if (rotationVelocityChanges != null){

    this.rotationVelocities.x += rotationVelocityChanges.x * timePerFrame;

    this.rotationVelocities.y += rotationVelocityChanges.y * timePerFrame;

    this.rotationVelocities.z += rotationVelocityChanges.z * timePerFrame;

    }

    updateLocalRotation();



    if (translationVelocityChanges != null){

    this.translationVelocities.x += translationVelocityChanges.x * timePerFrame;

    this.translationVelocities.y += translationVelocityChanges.y * timePerFrame;

    this.translationVelocities.z += translationVelocityChanges.z * timePerFrame;

    }

    updateLocalTranslation();

    }



    public void updateNoVelocity(float timePerFrame){

    updateVelocity(null, null, timePerFrame);

    }

    public void updateNoVelocity(Vector3f translationChanges, Vector3f rotationChanges, float timePerFrame){

    if (rotationChanges != null){

    this.rotationVelocities.x = rotationChanges.x * timePerFrame;

    this.rotationVelocities.y = rotationChanges.y * timePerFrame;

    this.rotationVelocities.z = rotationChanges.z * timePerFrame;

    }

    updateLocalRotation();



    if (translationChanges != null){

    this.translationVelocities.x = translationChanges.x * timePerFrame;

    this.translationVelocities.y = translationChanges.y * timePerFrame;

    this.translationVelocities.z = translationChanges.z * timePerFrame;

    }

    updateLocalTranslation();

    }



    //update location based on current location

    private void updateLocalTranslation(){

    //Vector3f localTranslation = geometry.getLocalTranslation();

    //geometry.setLocalTranslation(localTranslation.x + translationVelocities.x, localTranslation.y + translationVelocities.y, localTranslation.z + translationVelocities.z);



    System.out.println("MobileObject.updateLocalTranslation(): translationVelocities: "+translationVelocities);



    Vector3f localTranslation = parentNode.getLocalTranslation();

    parentNode.setLocalTranslation(localTranslation.x + translationVelocities.x, localTranslation.y + translationVelocities.y, localTranslation.z + translationVelocities.z);

    }



    //update rotation based on current rotation

    private void updateLocalRotation() {

    geometry.rotate(rotationVelocities.x, rotationVelocities.y, rotationVelocities.z);

    }

    }

    [/java]

https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:scenegraph_for_dummies

@wezrule: Sorry, but I don’t think you actually read my post. I’m not asking what the difference is between geometry and nodes, but what is causing the specific differences in translation that I mentioned. Nutshell: I’m seeing that geometry moves slowly, and nodes move blazingly fast. Weird.

This line in your constructor looks really suspicious:

this.translationVelocities = parentNode.getLocalTranslation();



That means that you are grabbing a reference to your parent node’s local translation and then directly modifying it elsewhere in your code… adding it back to itself and then setting the values as the local translation again. If local translation started as 1,0,0 then next time it will be 2,0,0 then 4,0,0 then 8,0,0… and so on.



Edit: and that’s even if you weren’t also adding things to it separately.

1 Like

Ah, thanks Pspeed! I was so confused :slight_smile: Removed, working as intended.