Moving an object at low world unit causes a "jumping" on Y axis... how to solve?

Hi, I have an object. I move it at costant speed with low world unit values, eg 0.0000001, 0.0000002 and so on.



But it jumps (always on Y axis)… like it was not fixed on its new position, please can you give me som hint how to solve it?



In the final body of update() I put the following, and I removed it, without any successful fix:






       observedNode.setLocalTranslation(observedPosition);
       observerNode.getCamera().setLocation( observedPosition.clone().addLocal(0, SCALE/2F, SCALE/2F) );
       //LOOKAT, without does not jump
       observerNode.getCamera().lookAt(observedPosition, IJmeConsts3D.UP);
       //chaser.update(time); // also chaser jumps

       //observerNode.getCamera().update();
       observerNode.updateFromCamera();

       // ==> FROM HERE
       observedNode.updateGeometricState(time, true);
       observedNode.updateWorldData(time);

       observerNode.updateGeometricState(time, true);
       observerNode.updateWorldData(time);
        
       InfoRegistry.class.cast(SingletonRegistry.getInfoRegistry()).set(InfoRegistry.KEY_XYZ, observedPosition.clone());
        InfoRegistry.class.cast(SingletonRegistry.getInfoRegistry()).set(InfoRegistry.KEY_SPEED, speedcam);



I am not able to figure out if the cause is my implementation, pratically I am using
several gamestates, but every object is rendered by distinct RenderPasses,
one for background, one for HUD and so on. Could it be the cause?

Maybe it's a precision problem, as the JME coordinate system is using floats?

This is probably a long shot, but the bit about calling lookAt reminded me : you have to call camera.update() sometimes after you've changed the camera parameters.  I had a problem that sounded similar as the camera does get updated at other times, which can cause a little jump in the frame it gets updated.  So try throwing this in after the lookAt call :



observerNode.getCamera().update();



Sooner or later I'll make a suggestion that actually works out, hope this is the one :)

Thank you for suggestion, it works, but unfortunately only when I rescale up the object.




Tumaini said:

Maybe it's a precision problem, as the JME coordinate system is using floats?


Yes everywhere for what I know, but I don't know well JME2 and JOGL/LWJGL to say
if it can influence coordinates under 1.E-7 or 0.000001. Effectively I noticed the same
anomaly when moving only the camera in some other test, when passed up 1.E-7
frame jumping disappeared.


EDIT 2:

I prepared a test with a cameranode, input keys are arrows to select direction and z to apply speed,
pratically when I turn in any direction the object starts to jump... please can you help me to find
why the object is jumping?


package test;

import static com.jme.input.KeyInput.*;

import java.util.ArrayList;
import java.util.logging.Logger;

import com.jme.bounding.BoundingBox;
import com.jme.input.controls.GameControl;
import com.jme.input.controls.GameControlManager;
import com.jme.input.controls.binding.KeyboardBinding;
import com.jme.light.PointLight;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.CameraNode;
import com.jme.scene.Controller;
import com.jme.scene.Geometry;
import com.jme.scene.Line;
import com.jme.scene.Node;
import com.jme.scene.shape.Box;
import com.jme.scene.state.LightState;
import com.jme.system.DisplaySystem;
import com.jmex.game.StandardGame;
import com.jmex.game.state.BasicGameState;
import com.jmex.game.state.BasicGameStateNode;
import com.jmex.game.state.GameStateManager;

public class TestGame {
   
   private static StandardGame instance;
   
    public static void main(String[] args) {
       
        Runtime.getRuntime().addShutdownHook(new Thread(){
            public void run() {
                System.runFinalizersOnExit(true);
            }
        });
       
       
        instance = new StandardGame("Test Game", StandardGame.GameType.GRAPHICAL, null);
        instance.start();
       
        BasicGameStateNode<BasicGameState> base = new BasicGameStateNode<BasicGameState>("Test View");
        GameStateManager.getInstance().attachChild(base);
               
        TestGS stateTest = new TestGS();
        stateTest.setActive(true);
        base.attachChild(stateTest);
       

        base.setActive(true);
       
    }
}

class TestGS extends BasicGameState {

   private static final long serialVersionUID = 1L;
   
   private static final Logger LOGGER = Logger.getLogger(TestGS.class.getName());
   
   private float gridFactor = 1000F;
   private float frustumNear = 0.00000001F;
   private float frustumFar =  1000000000F;
   
   private void addGridNode(float spacing, float extent) {
      //Node gridNode = ObjectLibrary.createGridNode(Vector3f.ZERO.clone(), gridFactor*100F, gridFactor);
      Node gridNode = new Node("grid");
      ArrayList<Vector3f> regularVertices = new ArrayList<Vector3f>();
      for (int i = 0; i * spacing <= extent; i++) {
            regularVertices.add(new Vector3f(-extent, 0, i * spacing));
            regularVertices.add(new Vector3f(extent, 0, i * spacing));
            regularVertices.add(new Vector3f(-extent, 0, -i * spacing));
            regularVertices.add(new Vector3f(extent, 0, -i * spacing));
            regularVertices.add(new Vector3f(i * spacing, 0, -extent));
            regularVertices.add(new Vector3f(i * spacing, 0, extent));
            regularVertices.add(new Vector3f(-i * spacing, 0, -extent));
            regularVertices.add(new Vector3f(-i * spacing, 0, extent));
      }
      Geometry regularGrid = new Line("regularLine", regularVertices
                .toArray(new Vector3f[] {}), null, null, null);
        regularGrid.getDefaultColor().set(ColorRGBA.darkGray);
        gridNode.attachChild(regularGrid);
      super.getRootNode().attachChild(gridNode);
   }
      
   private CameraNode createCameraNode(final float boxExtent) {
      
      // movable object
      Box box = new Box("Box", Vector3f.ZERO.clone(), boxExtent,boxExtent,boxExtent);
        box.setModelBound(new BoundingBox());
        box.updateModelBound();
        box.setRandomColors();
        box.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
       
        Node boxNode = new Node("node");
        boxNode.attachChild(box);
        boxNode.updateWorldBound();
             
      boxNode.getLocalTranslation().set(
            0F,
            0F,
            ((BoundingBox) boxNode.getWorldBound()).zExtent*10F
            );
      
      CameraNode camNode = new CameraNode("view",DisplaySystem.getDisplaySystem().getRenderer().getCamera());
      camNode.setLocalTranslation(0F, 10F, 0F);
      camNode.attachChild(boxNode);

      camNode.getCamera().setFrustumPerspective(55F, (float) DisplaySystem.getDisplaySystem().getWidth() / (float) DisplaySystem.getDisplaySystem().getHeight(), frustumNear, frustumFar);
      
      return camNode;
   }
   
   private void addLightNode() {
      PointLight light = new PointLight();
        light.setAttenuate(true);
        light.setDiffuse(new ColorRGBA(1.0F, 1.0F, 0.25F, 1.0F));
        light.setAmbient(new ColorRGBA(0.1F, 0.1F, 0.1F, 1.0F));
        light.setLocation(new Vector3f(0F, 0F, 0F));
        light.setEnabled(true);
       
        LightState lightState = DisplaySystem.getDisplaySystem().getRenderer().createLightState();
        lightState.setEnabled(true);
        lightState.attach(light);
        super.getRootNode().setRenderState(lightState);
   }
   
   public TestGS() {
      super("gsTest");
      
      // world grid
      this.addGridNode(gridFactor/10F, gridFactor*100F);
      
      // camera node
      CameraNode cameraNode = this.createCameraNode(0.0001F);
      super.getRootNode().attachChild(cameraNode);
      
        TestController controller = new TestController(cameraNode);
        super.getRootNode().addController(controller);
      
        //light
      this.addLightNode();
       
      super.getRootNode().updateGeometricState(0, true);
      
   }

   @Override
   public void update(float tpf) {
      super.getRootNode().updateWorldData(tpf);
      super.update(tpf);
   }
   
   @Override
   public void render(float tpf) {
      super.render(tpf);
   }
   
}



abstract class AController extends Controller {
   
   private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = Logger.getLogger(AController.class.getName());
   
    enum ETravelAction {
        LEFT, RIGHT, UP, DOWN,
        INCREASE, DECREASE,
        STRAFE_UP,STRAFE_DOWN,STRAFE_LEFT,STRAFE_RIGHT,
        ULTRAZOOMIN, ULTRAZOOMOUT, ZOOMIN, ZOOMOUT,
        TARGET_SHIP,
        EXIT}
   
   
   protected GameControlManager manager;
   
   protected void bindKey(ETravelAction action, int...keys) {
        final GameControl control = manager.getControl(action.name());
        for (int key : keys) {
          control.addBinding(new KeyboardBinding(key));
        }
    }
 
   protected float enumValue(ETravelAction action) {
        return manager.getControl(action.name()).getValue();
    }
   
}


class TestController extends AController {
   
   private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = Logger.getLogger(TestController.class.getName());

   private static final float TURN_SPEED = 2F;
   
    private CameraNode cameraNode;

   private float hAngle;
   private float vAngle;
   
   private float speedcam = 1F;
   
   
   private Quaternion q = new Quaternion();
   
   
    public TestController(CameraNode cameraNodePar) {
       this.cameraNode = cameraNodePar;
       
       this.manager = new GameControlManager();
       
       //create all actions
        for (ETravelAction action : ETravelAction.values()) {
            manager.addControl(action.name());
        }
       
        bindKey(ETravelAction.EXIT, KEY_ESCAPE);
        bindKey(ETravelAction.INCREASE, KEY_Z);
        bindKey(ETravelAction.DECREASE, KEY_X);
        bindKey(ETravelAction.TARGET_SHIP, KEY_SPACE);
       
      bindKey(ETravelAction.ZOOMIN, KEY_ADD);
      bindKey(ETravelAction.ZOOMOUT, KEY_MINUS);
       
        bindKey(ETravelAction.UP, KEY_UP);
        bindKey(ETravelAction.DOWN, KEY_DOWN);
        bindKey(ETravelAction.LEFT, KEY_LEFT);
        bindKey(ETravelAction.RIGHT, KEY_RIGHT);
        bindKey(ETravelAction.STRAFE_UP, KEY_W);
        bindKey(ETravelAction.STRAFE_DOWN, KEY_S);
        bindKey(ETravelAction.STRAFE_LEFT, KEY_A);
        bindKey(ETravelAction.STRAFE_RIGHT, KEY_D);  
       
    }
   
   @Override
   public void update(float time) {
      
        if (enumValue(ETravelAction.EXIT) > 0) {
            System.exit(0);
        }
       
        if (enumValue(ETravelAction.LEFT) > 0||enumValue(ETravelAction.RIGHT) > 0
         ||enumValue(ETravelAction.UP) > 0||enumValue(ETravelAction.DOWN) > 0) {
           hAngle += TURN_SPEED * time * (enumValue(ETravelAction.LEFT) - enumValue(ETravelAction.RIGHT));
          vAngle += TURN_SPEED * time * (enumValue(ETravelAction.DOWN) - enumValue(ETravelAction.UP));
          this.cameraNode.getLocalRotation().set( q.fromAngles(vAngle, hAngle, 0f) );
        }
       
       
        if (enumValue(ETravelAction.INCREASE) > 0/*||enumValue(ETravelAction.DECREASE) > 0*/) {
           Vector3f shipDirection = new Vector3f();
           Vector3f observedPosition = this.cameraNode.getLocalTranslation();
           observedPosition.addLocal(this.cameraNode.getLocalRotation().getRotationColumn(2, shipDirection).multLocal(speedcam));
        }
               
   }

}


your frustum values are also very big, which makes the z-buffer imprecise.



private float frustumNear = 0.00000001F;

private float frustumFar =  1000000000F;



try to use some smaller values (frustum near should be as big as possible)