Cam.setDirection anomaly

Hello fellow monkeys …



This is my first post … and first up, a big THANK YOU to the jME team. Great software … Great job … Great docs … Great community spirit.



Here’s my first encounter with a problem …



I noticed when setting up a project prototype that on setting the camera to face a certain object, that object would appear off-center in the camera view. I expected it to be dead center. There are a couple of parameters involved that may or may not influence this. One is the frustum far being quite big at around five thousand. And the second is that my object is place at the end of a three-tier node tree. Or it may be a true jME3 camera anomaly.



I have reproduced the code in a simplified form to illustrate what is happening. So the code is pasted below.



You will see that the camera direction is set and then gotten and printed. You will see that the object is off-center, being slightly off to the top left. There is a five second wait in the code to allow you to center the object on the screen with the mouse and then a second “println” routine is done. You will see that this time, those two vectors are pretty much the same. So cam.getDirection() seems fine. The hello_picking examples worked fine too, which would suggest the same.



Well, here is the code. Try it out and see if you come to the same conclusion as me. The conclusion being that cam.setDirection() is not setting the director properly into the cam object for some reason.



… oops - I don’t know which tag to use to embed code … can’t find info on the site … so I’ll try the JAVA tags and hope this is intuitive.



[java]

public class Main extends SimpleApplication {



public static void main(String[] args) {

Main app = new Main();

app.start();

}



Node cosmos;

Node farAway;

Vector3f translationVector, translationUnityVector;

float myTimer = 0.0f;



@Override

public void simpleInitApp() {

Box b = new Box(Vector3f.ZERO, 100, 100, 100);

Geometry 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);





translationUnityVector = new Vector3f(translationVector.normalize());

cam.setFrustumFar(5000.0f);

cam.setDirection(translationUnityVector);

// cam.updateViewProjection();

// cam.update();

System.out.println("TansUnity Vector = " + translationUnityVector);

System.out.println("Camera Direction = " + cam.getDirection());



}



@Override

public void simpleUpdate(float tpf) {

myTimer += tpf;

if (myTimer > 5){

System.out.println(“After five seconds: …”);

System.out.println("TansUnity Vector = " + translationUnityVector);

System.out.println("Camera Direction = " + cam.getDirection());

myTimer = -1000000.0f;

}

}

}

[/java]



Thank you for your time.

1 Like

EDIT: forgot to add switch for method==5, fixed code now

great another bug :slight_smile: well or so it appears (EDIT: it’s great as in: hooray it’s found! let’s all celebrate)

in Camera.java

[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]

    quick fix would be:

    [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]

    anyway here’s my test case which uses 4 methods(as in: ways) (a 5th one is with different up vector)

    Just run it and press keys from 1 to 5, only method 1 is bugged (that is cam.setDirection() is bugged that is, it doesn’t work as expected but it does work as specified by its comment header, right?)

    The methods attempt to make the camera look in the direction of the box, as to have the box in center, even if the camera position is different from Vector3f.ZERO

    Note that cam.setDirection(direction) as it is, appears to always do a half rotation towards the direction (that is, rotate to half of current camera rotation and the expected camera rotation)

    pre type="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;


    }





    }


    }
    /pre

EDIT2:

[java]cam.lookAt(farAway.localToWorld(Vector3f.ZERO, null), Vector3f.UNIT_Y);[/java]

that will look at farAway’s 0,0,0 localtranslation, which is where to center of that box is, though farAway is at 1200.0f, 1000.0f, 1600.0f in localtranslation of its parent (aka cosmos)

======

try

cam.lookAt(translationVector, Vector3f.UNIT_Y);

instead;

actually it should be like:

[java]cam.lookAt(farAway.localToWorld(translationVector, null),

Vector3f.UNIT_Y);[/java]

where we knew translationVector is local translation in farAway

EDIT1: ok my bad, first off should be:

[java]cam.lookAt(farAway.localToWorld(Vector3f.ZERO, null), Vector3f.UNIT_Y);[/java]

then this works when rootNode is not transformed at all



Here’s something that (I think should work but doesn’t)

when rootNode and other subnodes are transformed… good luck making the camera look at one? then again I don’t know much … I’m such a monkey xD

http://i.imgur.com/CxlT0.jpg

pre type="java"
package org.jme3.tests;





import com.jme3.app.SimpleApplication;


import com.jme3.material.Material;


import com.jme3.math.ColorRGBA;


import com.jme3.math.FastMath;


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;





public class Main2 extends SimpleApplication {


public static void main(String[] args) {


Main2 app = new Main2();


app.start();


}





Node cosmos;


Node farAway;


Vector3f translationVector, translationUnityVector;


float myTimer = 0.0f;





@Override


public void simpleInitApp() {


flyCam.setMoveSpeed(4000f);


Box b = new Box(Vector3f.ZERO, 100, 100, 100);


Geometry 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);


rootNode.setLocalRotation(new Quaternion().fromAngles(


FastMath.DEG_TO_RAD * 34, FastMath.DEG_TO_RAD * 78,


FastMath.DEG_TO_RAD * 187));


rootNode.setLocalScale(2.3f, 7f, 5.2f);


rootNode.setLocalTranslation(12f, -8f, -5.2f);


translationVector = new Vector3f(1200.0f, 1000.0f, 1600.0f);


farAway.setLocalTranslation(translationVector);


farAway.setLocalScale(2.3f, 0.7f, 0.2f);


farAway.setLocalRotation(new Quaternion().fromAngles(


FastMath.DEG_TO_RAD * 24, FastMath.DEG_TO_RAD * 98,


FastMath.DEG_TO_RAD * 217));


// translationUnityVector = new Vector3f(translationVector.normalize());


// System.out.println(geom.getWorldTranslation());


cam.setFrustumFar(15000.0f);


// cam.setDirection(translationUnityVector);


// cam.lookAt(rootNode.worldToLocal(translationVector, null),


// rootNode.worldToLocal(cam.getUp(), null));// Vector3f.UNIT_Y


cam.lookAt(farAway.localToWorld(translationVector, null),


Vector3f.UNIT_Y);


// cam.lookAt(translationVector, Vector3f.UNIT_Y);


// cam.update();


// flyCam.


// cam.updateViewProjection();


// cam.update();


System.out.println("TansUnity Vector = " + translationUnityVector);


System.out.println("Camera Direction = " + cam.getDirection());


}





@Override


public void simpleUpdate(float tpf) {


// myTimer += tpf;


// if (myTimer > 5) {


// System.out.println(“After five seconds: …”);


// System.out.println("TansUnity Vector = " + translationUnityVector);


// System.out.println("Camera Direction = " + cam.getDirection());


// myTimer = -1000000.0f;


// }


}





}
/pre

EDIT: even better:

[java]geom.getWorldTransform();//unbugging the following:

cam.lookAt(geom.localToWorld(Vector3f.ZERO, null), Vector3f.UNIT_Y);// bugged[/java]

====

actually (jme3 rev.7261)

[java]cam.lookAt(farAway.localToWorld(Vector3f.ZERO, null), Vector3f.UNIT_Y);[/java]

this is bugged

this will work instead:

[java]cam.lookAt(

farAway.getWorldTransform()

.transformVector(Vector3f.ZERO, null), Vector3f.UNIT_Y);[/java]

thank you for helping me find it xD

here’s the final code:

pre type="java"
package org.jme3.tests;





import com.jme3.app.SimpleApplication;


import com.jme3.material.Material;


import com.jme3.math.ColorRGBA;


import com.jme3.math.FastMath;


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;





public class Main3 extends SimpleApplication {


public static void main(String[] args) {


Main3 app = new Main3();


app.start();


}





Node cosmos;


Node farAway;


Vector3f translationVector, translationUnityVector;


float myTimer = 0.0f;





@Override


public void simpleInitApp() {


flyCam.setMoveSpeed(4000f);


Box b = new Box(Vector3f.ZERO, 100, 100, 100);


Geometry 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);


rootNode.setLocalRotation(new Quaternion().fromAngles(


FastMath.DEG_TO_RAD * 34, FastMath.DEG_TO_RAD * 78,


FastMath.DEG_TO_RAD * 187));


rootNode.setLocalScale(2.3f, 7f, 5.2f);


rootNode.setLocalTranslation(12f, -8f, -5.2f);


translationVector = new Vector3f(1200.0f, 1000.0f, 1600.0f);


farAway.setLocalTranslation(translationVector);


farAway.setLocalScale(2.3f, 0.7f, 0.2f);


farAway.setLocalRotation(new Quaternion().fromAngles(


FastMath.DEG_TO_RAD * 24, FastMath.DEG_TO_RAD * 98,


FastMath.DEG_TO_RAD * 217));


// translationUnityVector = new Vector3f(translationVector.normalize());


// System.out.println(geom.getWorldTranslation());


cam.setFrustumFar(115000.0f);


// cam.setDirection(translationUnityVector);


// cam.lookAt(rootNode.worldToLocal(translationVector, null),


// rootNode.worldToLocal(cam.getUp(), null));// Vector3f.UNIT_Y


// cam.lookAt(farAway.localToWorld(Vector3f.ZERO, null),


// Vector3f.UNIT_Y);


cam.lookAt(farAway.localToWorld(Vector3f.ZERO, null), Vector3f.UNIT_Y);


cam.lookAt(


farAway.getWorldTransform()


.transformVector(Vector3f.ZERO, null), Vector3f.UNIT_Y);


// cam.lookAt(translationVector, Vector3f.UNIT_Y);


// cam.update();


// flyCam.


// cam.updateViewProjection();


// cam.update();


System.out.println("TansUnity Vector = " + translationUnityVector);


System.out.println("Camera Direction = " + cam.getDirection());


}





@Override


public void simpleUpdate(float tpf) {


// myTimer += tpf;


// if (myTimer > 5) {


// System.out.println(“After five seconds: …”);


// System.out.println("TansUnity Vector = " + translationUnityVector);


// System.out.println("Camera Direction = " + cam.getDirection());


// myTimer = -1000000.0f;


// }


}


}
/pre

Here, I made this for you,

rootNode->cosmos->farAway->geom

rootNode->box2


There are 3 nodes and 1 geometry (gray box aka geom) and all are transformed, and the camera is pointed at the gray box, also adding another geometry to a different node (red box aka box2) which is in the same location as the gray box



pre type="java"
package org.jme3.tests;





import com.jme3.app.SimpleApplication;


import com.jme3.material.Material;


import com.jme3.math.ColorRGBA;


import com.jme3.math.FastMath;


import com.jme3.math.Quaternion;


import com.jme3.math.Vector3f;


import com.jme3.scene.Geometry;


import com.jme3.scene.Mesh;


import com.jme3.scene.Node;


import com.jme3.scene.Spatial.CullHint;


import com.jme3.scene.debug.Arrow;


import com.jme3.scene.shape.Box;





public class Main2 extends SimpleApplication {


    private static final float lineWidth = 5f;


    private static final float lineLen = 220f;





    public static void main(String[] args) {


Main2 app = new Main2();


app.start();


    }





    Node cosmos;


    Node farAway;


    Vector3f localPosOfGrayBoxInFarAwayNode, translationUnityVector;


    float myTimer = 0.0f;





    private Geometry makeBox(ColorRGBA color) {


Box b = new Box(Vector3f.ZERO, 100, 100, 100);


Geometry geom = new Geometry(“Box”, b);


Material mat = new Material(assetManager,


“Common/MatDefs/Misc/SolidColor.j3md”);


mat.setColor(“m_Color”, color);


geom.setMaterial(mat);


return geom;


    }





    @Override


    public void simpleInitApp() {


// rootNode->cosmos->farAway->geom


flyCam.setMoveSpeed(8000f);


cam.setLocation(new Vector3f(574.51807f, -486.68802f, -6949.275f));





cosmos = new Node(“cosmos”);


farAway = new Node(“farAway”);


rootNode.attachChild(cosmos);


cosmos.attachChild(farAway);


Geometry geom = makeBox(ColorRGBA.Gray);


farAway.attachChild(geom);





attachCoordinateAxes(Vector3f.ZERO, rootNode);


attachCoordinateAxes(Vector3f.ZERO, cosmos);


attachCoordinateAxes(Vector3f.ZERO, farAway);





geom.setLocalScale(0.9f, 2.3f, 0.7f);


geom.setLocalRotation(new Quaternion().fromAngles(


FastMath.DEG_TO_RAD  104, FastMath.DEG_TO_RAD  168,


FastMath.DEG_TO_RAD  17));


geom.setLocalTranslation(170f, -800f, 900f);





cosmos.setLocalTranslation(600f, 200f, -300f);


cosmos.setLocalScale(3f, 0.89f, 2.4f);


cosmos.setLocalRotation(new Quaternion().fromAngles(


FastMath.DEG_TO_RAD 
 14, FastMath.DEG_TO_RAD  68,


FastMath.DEG_TO_RAD 
 270));





rootNode.setLocalRotation(new Quaternion().fromAngles(


FastMath.DEG_TO_RAD  34, FastMath.DEG_TO_RAD  78,


FastMath.DEG_TO_RAD  187));


rootNode.setLocalScale(2.3f, 7f, 5.2f);


rootNode.setLocalTranslation(12f, -8f, -5.2f);





farAway.setLocalTranslation(new Vector3f(1200.0f, 1000.0f, 1600.0f));


farAway.setLocalScale(2.3f, 0.7f, 0.2f);


// farAway.setLocalScale(3f);


farAway.setLocalRotation(new Quaternion().fromAngles(


FastMath.DEG_TO_RAD 
 24, FastMath.DEG_TO_RAD  98,


FastMath.DEG_TO_RAD 
 217));





cam.setFrustumFar(115000.0f);





geom.getWorldTransform();// unbugging localToWorld()&worldToLocal()


Vector3f worldPosOfGrayBoxsCenter = geom.localToWorld(Vector3f.ZERO,


null);


cam.lookAt(worldPosOfGrayBoxsCenter, Vector3f.UNIT_Y);





// make a red box2 and add it to rootNode (note that rootNode is


// transformed already it’s not ~= world coords anymore)


// make sure this box2 is in the same position as the gray box,


// that is same center, even though they are in two different nodes!


Geometry box2 = makeBox(ColorRGBA.Red);


rootNode.attachChild(box2);


Vector3f localPosInRootNode_OfGrayBoxsCenter = rootNode.worldToLocal(


worldPosOfGrayBoxsCenter, null);


box2.setLocalTranslation(localPosInRootNode_OfGrayBoxsCenter);


    }





    private void attachCoordinateAxes(Vector3f pos, Node toNode) {


Arrow arrow = new Arrow(Vector3f.UNIT_X.mult(lineLen));


// make arrow thicker,


arrow.setLineWidth(lineWidth);


putShape(arrow, ColorRGBA.Red, toNode).setLocalTranslation(pos);





arrow = new Arrow(Vector3f.UNIT_Y.mult(lineLen));


arrow.setLineWidth(lineWidth); // make arrow thicker


putShape(arrow, ColorRGBA.Green, toNode).setLocalTranslation(pos);





arrow = new Arrow(Vector3f.UNIT_Z.mult(lineLen));


arrow.setLineWidth(lineWidth); // make arrow thicker


putShape(arrow, ColorRGBA.Blue, toNode).setLocalTranslation(pos);


    }





    private Geometry putShape(Mesh shape, ColorRGBA color, Node onNode) {


Geometry g = new Geometry(“coordinate axis”, shape);


Material mat = new Material(assetManager,


“Common/MatDefs/Misc/Unshaded.j3md”);


mat.getAdditionalRenderState().setWireframe(true);


mat.setColor(“Color”, color);


g.setMaterial(mat);


onNode.attachChild(g);


g.setCullHint(CullHint.Inherit);


return g;


    }





}
/pre

Thank you for your reply, cyuczieekc. However that is a lot of code to get the camera looking in the right direction. I agree with your comment a few posts up, that this function is bugged. I may take a look at the source code of the Camera Class sometime to try to see why the camera won’t look in the direction it is set. There seems to be a proportional offset to the cam.setDirection() function.



If you take my original code from the first post and add cam.setDirection(translationUnityVector); to line 37 you will get this for the simpleUpdate() method. The method will be as follows:



[java]

@Override

public void simpleUpdate(float tpf) {

myTimer += tpf;

if (myTimer > 5){

cam.setDirection(translationUnityVector);

System.out.println(“After five seconds: …”);

System.out.println("TansUnity Vector = " + translationUnityVector);

System.out.println("Camera Direction = " + cam.getDirection());

myTimer = 0.0f;

}

}

[/java]



This updated method causes the box to step into the center, taking one step every five seconds. I think this is a clear exhibition that something about setting the camera direction is not right. If you look at the console printouts, the evidence is clear.



Regarding your other contributions … I will take a better look into the cam.LookAt() method and I’ll look into your code to see how you got the object in the center of the camera with offset nodes. The code you gave ran fine here.

The method Camera.setDirection() is now deprecated. Instead you should use Camera.getRotation() and Camera.setRotation() for manipulating the camera’s rotation directly. An easier to use method is Camera.lookAt() which generates a correct rotation from a direction and up vector.

1 Like

To: @cyuczieekc1p

Many thanks for your input into this issue. I appreciate your disposition to get to the root of the problem. You seem to have a good understanding of jME. Maybe I’ll get there too one day.



To @Momoko_Fan,

I have tried the methods you suggested in this post … and I have good news and bad news.



First the good news …

cam.setRotation(Quaternion cameraRotation) works fine in both cases I tested. That was with a normalized vector in the first test and a non-normalized vector in the second test. In each case, the appropriate vectors were supplied to the newly instanciated quaternion “cameraRotation” via the Quaternion.lookAt() method.



Now the bad news …

cam.lookAt(Vector3f vec, Vector3f.UNIT_Y) works fine in the second case with a non-normalized vector.



But when a normalized vector is given, the camera looks into cold dark space with no blue box to be seen.



This behaviour seems to be similar to the cam.setDirection() result when a either a normalized or a non-normalized vector is given.



Here is a listing of the buggy code. It passes a normalized vector to the cam.lookAt() method. The result is that the blue box is not even on the screen … on my computer, anyway.

[java]

public class Main extends SimpleApplication {



public static void main(String[] args) {

Main app = new Main();

app.start();

}



Node cosmos;

Node farAway;

Vector3f translationVector, translationUnityVector;

float myTimer = 0.0f;

Quaternion fromCameraRotation;



@Override

public void simpleInitApp() {

Box b = new Box(Vector3f.ZERO, 100, 100, 100);

Geometry geom = new Geometry(“Box”, b);



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);





translationUnityVector = new Vector3f(translationVector.normalize());

cam.setFrustumFar(5000.0f);



Quaternion cameraRotation = new Quaternion();

cameraRotation.lookAt(translationUnityVector, Vector3f.UNIT_Y);



// COMMENT OUT ONE OF THESE TWO LINES TO TEST ONE METHOD OR THE OTHER.

//cam.setRotation(cameraRotation);

cam.lookAt(translationUnityVector, Vector3f.UNIT_Y);



System.out.println(" Var translationUnityVector = " + translationUnityVector);

System.out.println("Camera Direction (cam.getDirection) = " + cam.getDirection());

System.out.println(“Quaternion to be sent to the camera = " + cameraRotation.toString());

System.out.println(” Quaternion retrieved from Camera = " + cam.getRotation().toString());



}



@Override

public void simpleUpdate(float tpf) {



}



@Override

public void simpleRender(RenderManager rm) {

//TODO: add render code

}

}

[/java]

As far as I understand 3D programming, normalized vectors are prefered over non-normalized ones. If this view is correct, then it would be nice to see the cam.lookAt() method giving appropriate results.



I trust my endeavour here is seen as trying to improve jME. And I hope I haven’t misled myself.



Finally, if Camera.setDirection() and Camera.getDirection() are deprecated. It would be good to reflect this in the jME Javadoc. I program with javadoc open all the time; it’s a java programmer’s best friend.



Thank you for your time.

The lookAt() method of the camera actually takes a position, not a direction. Instead I added a lookAtDirection() method in case you want to supply a normalized direction vector

1 Like

Btw, the quaternion’s lookAt method use a direction. Maybe for consistency sake we should call it lookAtDirection too?

1 Like

yeah what Momoko_Fan said :slight_smile:

(btw I’m just a beginner trying to understand jme, I likely get things wrong, but I’m doing these for me to better understand even though I appear to be helping you xD )

you can replace

[java] cam.lookAt(translationUnityVector, Vector3f.UNIT_Y);[/java]

with

[java]cam.lookAt(translationVector , Vector3f.UNIT_Y);[/java]

but you should know that cam.lookAt takes a world position and we’ve given it a local position (by position I mean translation), but it works in this case because none of our nodes are transformed (rotation/scale/translation), well only one node is…

Like for example if you scale the rootNode by adding

[java]rootNode.setLocalScale(2, 3, 4);[/java]

before the cam.lookAt and also change the frustrum to like 115000f (else you can’t see the box) all root’s children nodes are affected by this scale, so you cannot use the

[java]cam.lookAt(translationVector , Vector3f.UNIT_Y);[/java]

anymore to look at the box, you only get this(as you can see it’s not centered):

http://i.imgur.com/NKwbf.jpg

So if you want to look at the exact position (via cam.lookAt) of where the box really is, you need to transform the box’s local translation into world translation via the localToWorld method ie.

[java]cam.lookAt(geom.localToWorld(Vector3f.ZERO, null), Vector3f.UNIT_Y);[/java]

this works but it considers the box’s center is at Vector3f.ZERO aka 0,0,0 however if you change to:

[java]Box b = new Box(new Vector3f(620, 80, -300), 100, 100, 100);[/java]

and run you get this:

http://i.imgur.com/bHbC3.jpg

the box’s center is at 620,80,-300 so to take this into consideration, better use:

[java]cam.lookAt(geom.localToWorld(b.getCenter(), null), Vector3f.UNIT_Y);[/java]

now the cam is looking exactly at the box

Of course we knew that geom is the Geometry that was made from the Box b

that is why we used that in the cam.lookAt call above

Anyways, hopefully this was easy to understand (I still need to work on my express-myself skills which are non-existent xD), and here’s your final code(with the above changes only):

[java]package org.jme3.forum;

import com.jme3.app.SimpleApplication;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.renderer.RenderManager;

import com.jme3.scene.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.shape.Box;

public class Main extends SimpleApplication {

public static void main(String[] args) {

Main app = new Main();

app.start();

}

Node cosmos;

Node farAway;

Vector3f translationVector, translationUnityVector;

float myTimer = 0.0f;

Quaternion fromCameraRotation;

@Override

public void simpleInitApp() {

Box b = new Box(new Vector3f(620, 80, -300), 100, 100, 100);

Geometry geom = new Geometry(“Box”, b);

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);

rootNode.setLocalScale(2, 3, 4);

translationVector = new Vector3f(-1200.0f, 1000.0f, 1600.0f);

farAway.setLocalTranslation(translationVector);

translationUnityVector = new Vector3f(translationVector.normalize());

cam.setFrustumFar(115000.0f);

Quaternion cameraRotation = new Quaternion();

cameraRotation.lookAt(translationUnityVector, Vector3f.UNIT_Y);

// COMMENT OUT ONE OF THESE TWO LINES TO TEST ONE METHOD OR THE OTHER.

// cam.setRotation(cameraRotation);

cam.lookAt(geom.localToWorld(b.getCenter(), null), Vector3f.UNIT_Y);

System.out.println(" Var translationUnityVector = "

  • translationUnityVector);

    System.out.println("Camera Direction (cam.getDirection) = "
  • cam.getDirection());

    System.out.println("Quaternion to be sent to the camera = "
  • cameraRotation.toString());

    System.out.println(" Quaternion retrieved from Camera = "
  • cam.getRotation().toString());

    }

    @Override

    public void simpleUpdate(float tpf) {

    }

    @Override

    public void simpleRender(RenderManager rm) {

    // TODO: add render code

    }

    }[/java]