How to add new KEY_ to rotate flyCam?

I am using SimpleApplication and I was wondering how to go about adding KEY_E to do a roll rotation to the right, not necessarily how to do the rotation, but rather how to add the key and code to the SimpleApplication’s camera, what’s the idea behind doing such a change?

Then what about overriding KEY_Q to do roll to left and remapping the old KEY_Q to TAB key…



I see SimpleApplication wrapped the Application’s “cam” object around the flyCam FlyByCamera object, should I do something similar and wrap the flyCam around my object which implements the changes I need? not that I know exactly how to yet



The way I see it, I need to subclass FlyByCamera and override some methods to add the desired mappings then extend SimpleApplicaiton and override initialize() to “new” the flyCam field to my subclassed instance ?!

I obviously don’t want to copy/paste code just in case jme does some updates/bug fixes of the classes I use (SimpleApplication and FlyByCamera)

This is what I have so far, but the left/right (mouse) doesn’t work as expected probably because it uses the initialUpVec (which remains as it were when FlyByCamera was constructed) instead of cam.getUpVector() ? I noticed that when moving mouse left/right the movement is not relative to current camera position due to what I just said, I realize this was intentional…



keys Q and E are used to roll camera but this messed up the camera turns via moving mouse left/right



the image is rather irrelevant, code is below:

http://i.imgur.com/l6fRz.png



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





import com.jme3.app.SimpleApplication;


import com.jme3.font.BitmapText;


import com.jme3.input.FlyByCamera;


import com.jme3.input.KeyInput;


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.FastMath;


import com.jme3.math.Matrix3f;


import com.jme3.math.Quaternion;


import com.jme3.math.Spline;


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.debug.Grid;


import com.jme3.scene.shape.Box;


import com.jme3.scene.shape.Curve;





/


  Sample 2 - How to use nodes as handles to manipulate objects in the scene


 
 graph. You can rotate, translate, and scale objects by manipulating their


  parent nodes. The Root Node is special: Only what is attached to the Root


 
 Node appears in the scene.


 /


public class HelloNode6 extends SimpleApplication {





    private static final float moveSpeed = 1f;


    private static final float rotSpeed = 2f;


    private static final float yawSpeed = 4f;


    private static final float rollSpeed = 7f;


    private static final float pitchSpeed = 11f;


    private Vector3f cornerPos;





    private Geometry geoCurve;





    private Geometry geoBox;





    private final Spline spline = new Spline();


    private Node coord;


    private Box box;


    private final Vector3f centerOfBox = new Vector3f();


    private final Quaternion qRotation = new Quaternion();


    float yaw = 0f;


    float roll = 0f;


    float pitch = 0f;


    private final String mapIncDelay = “IncDelay”;


    private final String mapDecDelay = “DecDelay”;


    long sleep = 0;


    private float fpsNow;


    private float maxSeenFps = 30;


    private static final long sleepIncrement = 10;


    private BitmapText helloText;


    private final String mapRollRight = “rollRight”;


    private final String mapRollLeft = “rollLeft”;





    public static void main(String[] args) {


HelloNode6 app = new HelloNode6();


app.setShowSettings(false);


app.start();


    }





    /



      (non-Javadoc)


     
 


      @see com.jme3.app.SimpleApplication#simpleUpdate(float)


     
/


    @Override


    public void simpleUpdate(float tpf) {


yaw = (yaw + FastMath.DEG_TO_RAD  rotSpeed  yawSpeed  tpf)


% (FastMath.PI 
 2);


roll = (roll + FastMath.DEG_TO_RAD  rotSpeed  rollSpeed  tpf)


% (FastMath.PI 
 2);


pitch = (pitch + FastMath.DEG_TO_RAD  rotSpeed  pitchSpeed  tpf)


% (FastMath.PI 
 2);


qRotation.fromAngles(yaw, roll, pitch);


// rotating the box to these absolute angles (from its origin pos)


geoBox.setLocalRotation(qRotation);


// move box “forward” too, that is, forward relative to itself ie.


// spaceship moving forward


geoBox.move(geoBox.getLocalRotation().getRotationColumn(2)


.mult(moveSpeed * tpf));





Vector3f clonedCornerPos = cornerPos.clone();


// now applying the same transform (pos/rot/scale) to the corner as the


// box has


// clonedCornerPos =


geoBox.getLocalTransform().transformVector(clonedCornerPos,// in,


clonedCornerPos// store


);


// even if I comment the following “coord” related updates still low fps


geoBox.getLocalTransform().transformVector(box.getCenter().clone(),


centerOfBox);


// same orientation as Box


coord.setLocalTransform(geoBox.getLocalTransform());


// move coord system at center of Box


coord.setLocalTranslation(centerOfBox);


// til here





// we have the corner’s exact pos now, relative to the Node the geoBox &


// geoCurve are both in


// we add that pos to the curve


spline.addControlPoint(clonedCornerPos);// must be cloned!


// we must create new Curve object because we can’t add/update the


// spline in existing one (?!)


fpsNow = timer.getFrameRate();


if (fpsNow > maxSeenFps) {


    maxSeenFps = fpsNow;


}


int subSegments = (int) Math.floor(maxSeenFps / fpsNow);


geoCurve.setMesh(new Curve(spline, subSegments));


helloText.setText("MaxSeenFps: " + (int) Math.ceil(maxSeenFps)


+ " / subSegs: " + subSegments);


try {


    Thread.sleep(sleep);


} catch (InterruptedException e) {


    e.printStackTrace();


}


    }





    private void initGUI() {


// guiFont =


// assetManager.loadFont( “Interface/Fonts/Default.fnt” );


helloText = new BitmapText(guiFont, false);


helloText.setSize(guiFont.getCharSet().getRenderedSize());


helloText.setLocalTranslation(300, helloText.getLineHeight(), 0);


guiNode.attachChild(helloText);


    }





    private void initKeys() {


inputManager


.addMapping(mapIncDelay, new KeyTrigger(KeyInput.KEY_SPACE));


inputManager.addMapping(mapDecDelay, new KeyTrigger(


KeyInput.KEY_LCONTROL));





inputManager.addMapping(mapRollRight, new KeyTrigger(KeyInput.KEY_E));


inputManager.addMapping(mapRollLeft, new KeyTrigger(KeyInput.KEY_Q));





inputManager.addListener(analogListener, mapIncDelay, mapDecDelay,


mapRollRight, mapRollLeft);


inputManager.deleteMapping(“FLYCAM_Rise”);// delete prev. Q mapping


  // (the lame way)


    }





    /



      from {@link FlyByCamera#rotateCamera(float, Vector3f)} which is


     
 protected, copied it here


      


     
 @param value


      @param axis


     
/


    protected void rotateCamera(float value, Vector3f axis) {





Matrix3f mat = new Matrix3f();


mat.fromAngleNormalAxis(


// flyCam.rotationSpeed//can’t use it not visible


1f * value, axis);





Vector3f up = cam.getUp();


Vector3f left = cam.getLeft();


Vector3f dir = cam.getDirection();





mat.mult(up, up);


mat.mult(left, left);


mat.mult(dir, dir);





Quaternion q = new Quaternion();


q.fromAxes(left, up, dir);


q.normalize();





cam.setAxes(q);


    }





    private final AnalogListener analogListener = new AnalogListener() {





@Override


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


    if (mapRollRight == name) {


System.out.println(mapRollRight);


rotateCamera(value, cam.getDirection());


return;


    }


    if (mapRollLeft == name) {


System.out.println(mapRollLeft);


rotateCamera(-value, cam.getDirection());


return;


    }


    if (mapIncDelay == name) {


sleep += sleepIncrement;


    }


    if (mapDecDelay == name) {


if (sleep > 0) {


    sleep -= sleepIncrement;


}


if (sleep < 0) {


    sleep = 0;


}


    }


    System.out.println(“Sleep now at:” + sleep);


}


    };





    @Override


    public void simpleInitApp() {


flyCam.setMoveSpeed(20f);


// flyCam.


// cam.setLocation( new Vector3f(


// -7.3405366f,


// 23.450567f,


// -20.834333f ) );


// cam.setRotation( new Quaternion(


// 0.46806002f,


// 0.095443755f,


// -0.050935324f,


// 0.8770495f ) );


Node subNode = new Node();


subNode.setLocalTranslation(new Vector3f(2, 0.5f, 1));


subNode.rotate(1f, -2f, 4f);


// a box with non 0,0,0 center which means a position of x,y,z relative


// to it’s parent geoBox(below)


box = new Box(new Vector3f(2, 3, 4), 0.5f, 0.9f, 1.3f);


geoBox = new Geometry(“Box”, box);


geoBox.setLocalTranslation(1f, 2f, 3f);


geoBox.scale(1.3f, 1.2f, 1.1f);// always ok


geoBox.rotate(2f, -4f, 0.4f);


Material mat2 = new Material(assetManager,


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


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


geoBox.setMaterial(mat2);





Material curveMat = new Material(assetManager,


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


curveMat.setColor(“Color”, ColorRGBA.Green);


geoCurve = new Geometry(“trails”);


geoCurve.setMaterial(curveMat);


subNode.attachChild(geoCurve);


subNode.attachChild(geoBox);





rootNode.setLocalTranslation(-1, -2, -4);


rootNode.rotate(-1f, 2f, -4f);





rootNode.attachChild(subNode);


rootNode.scale(1.5f);// this is always ok


subNode.scale(0.4f);// this is always ok too


// subNode.scale(1.3f, 0.7f, 0.4f);// XXX: bug when uncommented


// rootNode.scale(0.4f, 1.4f, 1.1f);// XXX: or/and this





// doing this here only once


cornerPos = new Vector3f();


// calculating position of corner, first getting corner as it were if


// box were at 0,0,0 and no rotation/scale


cornerPos.set(





box.getXExtent(), box.getYExtent(), box.getZExtent());


// now considering box may have a different than 0,0,0 center


cornerPos.addLocal(box.getCenter());


coord = new Node();


coord.setCullHint(CullHint.Never);


attachCoordinateAxes(Vector3f.ZERO, coord);


subNode.attachChild(coord);


// coord.setLocalTranslation( geoBox.getLocalTranslation() );//


// box.getCenter() );


initKeys();


initGUI();


attachGrid(Vector3f.ZERO, 100, ColorRGBA.Yellow);





    }





    private void attachCoordinateAxes(Vector3f pos, Node toNode) {


Arrow arrow = new Arrow(Vector3f.UNIT_X);


// make arrow thicker,


arrow.setLineWidth(1); // XXX:set this from 1 to 4 to see random culling


       // and 35 of 60 fps


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





arrow = new Arrow(Vector3f.UNIT_Y);


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


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





arrow = new Arrow(Vector3f.UNIT_Z);


arrow.setLineWidth(1); // 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;


    }





    private Geometry attachGrid(Vector3f pos, int size, ColorRGBA color) {


Geometry g = new Geometry(“wireframe grid”, new Grid(size, size, 1f));


Material mat = new Material(assetManager,


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


mat.getAdditionalRenderState().setWireframe(true);


mat.setColor(“Color”, color);


g.setMaterial(mat);


g.center().move(pos);


rootNode.attachChild(g);


return g;


    }


}



/pre

You probably should just extend fly cam or copy it to add the needed modification to the up vector

1 Like
Momoko_Fan said:
You probably should just extend fly cam or copy it to add the needed modification to the up vector

ok I guess, should I then do (1) flyCam=new MyExtendedFlyByCam(cam); sorta ? in simpleInitApp(), or you were thinking of another way?
I was trying to avoid copying to remain in sync with svn updates, extending sounds good, but I kinda needed to extend SimpleInitApp and it's flyCam both, else at least one flyCam would be inited and left over for garbage collector when I do (1) assuming that is safe, maybe others are already making use of the flyCam instance (assuming not) by the time I get in simpleInitApp to set a new one...

Still, it's good to know there are no other `official ways` of doing it

I say thank you for your reply, I'll add some code as soon as I do (if I do them) the modifications ...

None of the code in SimpleApplication really uses the fly cam. It just sets it up and thats it.

So you can just disable the original flyCam via setEnabled(false) and then set up your own.

1 Like