Possible unwanted behaviour with cam.setDirection(direction) jme3 7265

as per this testcase in this post here:

http://hub.jmonkeyengine.org/groups/general-2/forum/topic/cam-setdirection-anomaly/#post-125437

in Camera.java the original:

[java]/**

  • <code>setDirection</code> sets the direction this camera is facing. In
  • most cases, this changes the up and left vectors of the camera. If your
  • left or up vectors change, you must updates those as well for correct
  • culling.

    *
  • @param direction the direction this camera is facing.
  • @see Camera#setDirection(com.jme.math.Vector3f)

    */

    public void setDirection(Vector3f direction) {

    //this.rotation.lookAt(direction, getUp());

    Vector3f left = getLeft();

    Vector3f up = getUp();

    this.rotation.fromAxes(left, up, direction);

    onFrameChange();

    }[/java]

    it seems to be better just:

    [java] public void setDirection(Vector3f direction) {

    this.rotation.lookAt(direction, getUp());

    //Vector3f left = getLeft();

    //Vector3f up = getUp();

    //this.rotation.fromAxes(left, up, direction);

    onFrameChange();

    }[/java]

    though I do realize it says so in its header documentation, that it doesn’t change the UP&Left vectors,

    but is that really a good idea? If yes then maybe add a comment saying use cam.lookAt(pos,upVec) instead, or make a method that will do lookAt(direction,upVec) ~= cam.rotation.lookAt(direction, getUp()); well I don’t know :slight_smile:

    Like for example: pressing C while ingame reports

    Camera Direction = (0.1719328f, 0.19651715f, 0.9653083f)

    then going incode and executing a:

    cam.setDirection(new Vector3f(0.1719328f, 0.19651715f, 0.9653083f));

    and then running it and ingame pressing C again yields different result:

    Camera Direction = (0.12157485, 0.1389586, 0.9754693)

    unless the above fix is applied OR unless camera rotation is also gotten and set (but on this latter, the fixed setDirection also works this way)



    a more crazy fix would be(this is just an alternate method to using cam.setDirection()):

    [java]public void setDirection2(Vector3f direction) {

    // assumed direction wasn’t normalized:

    Vector3f dir = direction.normalize();

    Vector3f up = cam.getUp();// already normalized

    if (!up.isUnitVector()) {

    throw null;// will never happen

    }

    // up.normalizeLocal(); but up is already normalized

    Vector3f left = up.cross(dir);// result isn’t normalized!!?

    // …even though we crossed two normalized vectors

    left.normalizeLocal();

    if (!left.isUnitVector()) {

    // this will happen if we don’t manually normalize left

    throw null;

    }



    // the fix comes from recalculating the up below:

    up = dir.cross(left);// make sure new up is | on our dir

    if (!up.isUnitVector()) {

    throw null;// will never happen

    }



    cam.getRotation().fromAxes(left, up, dir);

    cam.onFrameChange();

    }[/java]

I think its okay. When you want to set the direction of the camera, you use that method.

I don’t understand what the issue is?

Momoko_Fan said:
I think its okay. When you want to set the direction of the camera, you use that method.
I don't understand what the issue is?

the problem was/is that cam.setDirection(direction) doesn't set the direction correctly, it only rotates half the way between where the camera was and where the camera should be
And the camera should be in the same direction as the passed direction vector, just as cam.rotation.lookAt(direction, getUp());
if you run this example, I attempt here to setDirection of the camera such that the box is in the middle of the camera...this requires several calls to setDirection using the same unchanged `direction` vector to do exactly that.
...in the update method cam.setDirection(direction) is called (initially, if you don't press any key), it should point the camera in the direction of the blue box instantly (on the first call) but instead as you can see, it requires several calls[each call happens after 1 second] (and the `direction` vector doesn't change at all), where on each call it moves half the way from current camera position to the final camera position
That being said, I'm unsure whether this method performs as expected: even if it doesn't change the Up/Left vectors it still fails to point in the expected direction, I mean ... how can it ever point in that direction if the up/left vectors don't change ? O_o (I don't get this probably because I'm new xD)
EDIT: like, after setDirection(direction) then cam.getDirection() and `direction` don't point in the same way disregarding floating point precision
[java]package org.jme3.tests;
import com.jme3.app.SimpleApplication;
import com.jme3.font.BitmapText;
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.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Line;
import com.jme3.system.AppSettings;
public class Main5 extends SimpleApplication {
public static void main(String[] args) {
Main5 app = new Main5();
AppSettings aps = new AppSettings(true);
aps.setVSync(true);
app.setSettings(aps);
app.setShowSettings(false);
app.start();
}
private final static String mapMethod1 = "mapMethod1";
private final static String mapMethod2 = "mapMethod2";
private final static String mapMethod3 = "mapMethod3";
private final static String mapMethod4 = "mapMethod4";
private final static String mapMethod5 = "mapMethod5";
private int method = 1;
private BitmapText methodText;
private BitmapText helpText;
Geometry geom;
Node cosmos;
Node farAway;
Vector3f translationVector, translationUnityVector;
float myTimer = 0.0f;
Vector3f boxsWorldPosition;
@Override
public void simpleInitApp() {
flyCam.setMoveSpeed(800f);
Box b = new Box(Vector3f.ZERO, 100, 100, 100);
geom = new Geometry("Box", b);
// geom.updateModelBound();
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/SolidColor.j3md");
mat.setColor("m_Color", ColorRGBA.Blue);
geom.setMaterial(mat);
cosmos = new Node("cosmos");
farAway = new Node("farAway");
farAway.attachChild(geom);
cosmos.attachChild(farAway);
rootNode.attachChild(cosmos);
translationVector = new Vector3f(1200.0f, 1000.0f, 1600.0f);
farAway.setLocalTranslation(translationVector);
cam.setLocation(Vector3f.ZERO.clone());
// just in case any nodes have transformations (rot/scale/trans)
boxsWorldPosition = geom.localToWorld(Vector3f.ZERO, null);
translationUnityVector = new Vector3f(boxsWorldPosition.normalize());
cam.setFrustumFar(5000.0f);
// box is on top left corner initially:
cam.setDirection(new Vector3f(0.17789759f, 0.1996023f, 0.96359295f));
// System.out.println("TansUnity Vector = " + translationUnityVector);
// System.out.println("Camera Direction = " + cam.getDirection());
initKey();
initGUI();
}
private void initGUI() {
methodText = new BitmapText(guiFont, false);
methodText.setSize(guiFont.getCharSet().getRenderedSize());
methodText.setLocalTranslation(30, 20 * methodText.getLineHeight(), 0);
guiNode.attachChild(methodText);
methodReport();
helpText = new BitmapText(guiFont, false);
helpText.setSize(guiFont.getCharSet().getRenderedSize());
helpText.setLocalTranslation(0, 23 * helpText.getLineHeight(), 0);
guiNode.attachChild(helpText);
helpText.setText("press keys from 1 to 5 to choose the method for setting camera direction towards the boxnOnly method 1 is bugged");
}
private void initKey() {
inputManager.addMapping(mapMethod1, new KeyTrigger(KeyInput.KEY_1));
inputManager.addMapping(mapMethod2, new KeyTrigger(KeyInput.KEY_2));
inputManager.addMapping(mapMethod3, new KeyTrigger(KeyInput.KEY_3));
inputManager.addMapping(mapMethod4, new KeyTrigger(KeyInput.KEY_4));
inputManager.addMapping(mapMethod5, new KeyTrigger(KeyInput.KEY_5));
inputManager.addListener(actionListener, mapMethod1, mapMethod2,
mapMethod3, mapMethod4, mapMethod5);
}
private final ActionListener actionListener = new ActionListener() {
@SuppressWarnings("synthetic-access")
@Override
public void onAction(String name, boolean isPressed, float tpf) {
do {
if (isPressed) {
if (mapMethod1 == name) {
method = 1;
break;
}
if (mapMethod2 == name) {
method = 2;
break;
}
if (mapMethod3 == name) {
method = 3;
break;
}
if (mapMethod4 == name) {
method = 4;
break;
}
if (mapMethod5 == name) {
method = 5;
break;
}
}
} while (false);
methodReport();
}
};
private void methodReport() {
switch (method) {
case 1:
methodText
.setText("- now using cam.setDirection(direction) which uses upVector==cam.getUp()");
break;
case 2:
methodText
.setText("- now using setDirection1 aka cam.rotation.lookAt(direction,cam.getUp())");
break;
case 3:
methodText
.setText("- now using setDirection2 aka the FIXED cam.setDirection(direction) which uses upVector==cam.getUp()");
break;
case 4:
methodText
.setText("- now using cam.lookAt(position,...) which uses upVector==Vector3f.UNIT_Y)");
break;
case 5:
methodText
.setText("- now using cam.lookAt(position,...) which uses upVector==cam.getUp()");
break;
default:
throw null;
}
}
private void methodUse(Vector3f direction) {
switch (method) {
case 1:
cam.setDirection(direction);
break;
case 2:
setDirection1(direction);
break;
case 3:
setDirection2(direction);
break;
case 4:
cam.lookAt(boxsWorldPosition, Vector3f.UNIT_Y);
break;
case 5:
cam.lookAt(boxsWorldPosition, cam.getUp());
break;
default:
throw null;
}
}
// XXX: should perhaps use Vector3f.UNIT_Y as upvector? unsure yet
public void setDirection1(Vector3f direction) {
// assume 'direction' wasn't normalized already
// though lookAt will normalize direction anyway, so not needed
cam.getRotation().lookAt(direction.normalize(), cam.getUp());
cam.onFrameChange();
}
// XXX: should perhaps use Vector3f.UNIT_Y as upvector? unsure yet
public void setDirection2(Vector3f direction) {
// assumed `direction` wasn't normalized:
Vector3f dir = direction.normalize();
Vector3f up = cam.getUp();// already normalized
if (!up.isUnitVector()) {
throw null;// will never happen
}
// up.normalizeLocal(); but up is already normalized
Vector3f left = up.cross(dir);// result isn't normalized!!?
// ...even though we crossed two normalized vectors
left.normalizeLocal();
if (!left.isUnitVector()) {
// this will happen if we don't manually normalize `left`
throw null;
}
// the fix comes from recalculating the `up` below:
up = dir.cross(left);// make sure new `up` is _|_ on our `dir`
if (!up.isUnitVector()) {
throw null;// will never happen
}
cam.getRotation().fromAxes(left, up, dir);
cam.onFrameChange();
}
@Override
public void simpleUpdate(float tpf) {
myTimer += tpf;
if (myTimer > 1) {// every 5 seconds
// cam.setDirection(translationUnityVector);
// setDirection1(translationUnityVector);
// direction towards to box is relative to camera position,
// thus recalculate direction just in case camera pos changed
translationUnityVector = boxsWorldPosition.subtract(
cam.getLocation()).normalizeLocal();
// now point the camera at that direction
methodUse(translationUnityVector);
// System.out.println("After five seconds: ...");
// System.out.println("TansUnity Vector = " +
// translationUnityVector);
// System.out.println("Camera Direction = " + cam.getDirection());
myTimer = 0f;
}
}
}[/java]