"Model Editor" camera control - How to?

The first thing I wanted to make for my project was a model editor. I now have it working, and most of the required functionality is there and works. But moving around the model using the default FlyCam is a pain. I need something that makes it easy to turn around the origin, while keeping the camera pointed towards the origin.

Is there an example of something like that somewhere? And has this kind of UI interaction got a specific name (so that it would be easier for me to search for examples of it)?

OK. I managed to get something reasonable with this code; maybe it will help someone else …

public class MyFlyByCamera extends FlyByCamera {
    public MyFlyByCamera(Camera cam){
        super(cam);
    }

    @Override
    protected void rotateCamera(float value, Vector3f axis){
        final Vector3f loc = cam.getLocation();
        final Quaternion turn = new Quaternion();
        turn.fromAngleAxis(value, axis);
        turn.multLocal(loc);
        cam.setLocation(loc);
        super.rotateCamera(value, axis);
    }

    @Override
    public void onAnalog(String name, float value, float tpf) {
        if (!enabled) {
            return;
        }
        if (name.equals("FLYCAM_ZoomIn")) {
            rotateCamera(value/10, cam.getDirection());
        } else if (name.equals("FLYCAM_ZoomOut")) {
            rotateCamera(-value/10, cam.getDirection());
        } else {
            super.onAnalog(name, value, tpf);
        }
    }
}

To use my own FlyByCamera impl, I had to clone the FlyCamAppState, and use that instead of the default one.

@monster said: OK. I managed to get something reasonable with this code; maybe it will help someone else ... .... ... To use my own FlyByCamera impl, I had to clone the FlyCamAppState, and use that instead of the default one.

Can you show video how it works? Just curious what you wanted to make…

Hmmm, well it’s not top secret, but I’m afraid I’m rather “video-challenged” :smiley:

I tried once to setup a screen-recording program for my kid, because he wanted to make “Minecraft Tutorials” :smiley: but I must say we didn’t have much luck with any free recording program I tried, and I tried about half a dozen of them … But I understand there’s some way to get JME to create the videos natively? :-? If that’s not too hard, I could have a try. Also, I don’t think I ever uploaded a video in my entire life, so I would need some account somewhere first…

OK. I found about VideoRecorderAppState. It works great. :slight_smile: Now I just have to get the video on the Net.

I so wanted to have a look at your camera that i spent some time tu run your example. It works pretty cool! I really like it.

The only issue is that camera always rotates towards vector(0,0,0). And if i move camera to right or left it rotates to 0,0,0 but not different rotation point. Is it possible to fix such an issue to have a camera like Chasecam but with your cool rotation implementation?

Looking at your code i caanot understand where the point towards camera is rotated…

I haven’t tried the Chasecam , as I’m only at the beginning of my first game. It looks like a mighty complicated beast.

I see what you mean with rotation after translating the camera (with keyboard keys). I’m not sure how to handle this yet, but I’ll post it back, once I have an improved version. I think the FlyByCam itself was designed more as an easy starting-point, then as a reusable component. I suspect that most people eventually replace it with their own fully customized implementation.

I managed to upload the video. Not really impressive, but I only wanted to show what the cam control works like. [video]http://www.youtube.com/watch?v=cxzKLbYuvjw[/video]

Ok, man. I’ll try to think about it too. I’m not good in vector math…

If you wish to rotate around some object, the easiest is to parent it.

You have :

  • Object
  • Node(center)
  • CameraNode
    ++ Camera

You move the camera forward/backward translating the CameraNode.
You rotate the camera rotating the Node(center).

If you wish I can provide a simple test-case, or even an AppState.

Edit : You can also make it with Vector Math, but this is the hard way ;-).

1 Like
@yang71 said: If you wish to rotate around some object, the easiest is to parent it.

You have :

  • Object
  • Node(center)
  • CameraNode
    ++ Camera

You move the camera forward/backward translating the CameraNode.
You rotate the camera rotating the Node(center).

If you wish I can provide a simple test-case, or even an AppState.

Edit : You can also make it with Vector Math, but this is the hard way ;-).

You will save the world with your example! Post your code and we will check your way, man!

@yang71 That would be nice. :slight_smile:
@mifth Agreed. My example worked more by chance then real by understanding of vector math…

Ok here is a polished appState that do the trick.
There’s still one thing I don’t like: you HAVE to call the setRootNode, because I cannot figure out how to get it without modifying the SimpleApplication… I’m sure there’s a simple way, but I can’t find it this evening.

Here is the class :
[java]

package mygame;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.CameraControl;

/**

  • A camera that fly around the specified object
  • @author jeanpierre
    */
    public class FlyAroundAppState extends AbstractAppState implements ActionListener, AnalogListener {

protected boolean dragToRotate,dragged;
protected Camera cam;
protected Spatial target;
protected Node rotate;
protected CameraNode translate;
protected boolean up, down, left, right, fwd, bkwd, clicked;
protected float linSpeed, rotSpeed;
protected InputManager inputManager;

/**

  • Builds a new fly-around camera
    */
    public FlyAroundAppState() {
    rotate = new Node(“orbit”);
    dragToRotate = dragged = false;
    }

@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
cam = app.getCamera();
translate = new CameraNode(“dolly”, cam);
translate.setControlDir(CameraControl.ControlDirection.SpatialToCamera);
rotate.attachChild(translate);
up = down = left = right = false;
linSpeed = 5;
rotSpeed = 0.5f;
inputManager = app.getInputManager();
initInputAZERTY();
initInputMouse();
inputManager.setCursorVisible(dragToRotate);
}

/**

  • Sets the move speed of the camera
  • @param lin the linear speed
  • @param ang the angular speed
    */
    public void setSpeed(float lin, float ang) {
    linSpeed = lin;
    rotSpeed = ang;
    }

/**

  • Attaches the camera to the rootnode. MUST be used!
  • @param rootNode the root node
    */
    public void setRootNode(Node rootNode) {
    rootNode.attachChild(rotate);
    }

/**

  • Sets the distance of the camera to the target
  • @param d the distance to set
    */
    public void setDistance(float d) {
    translate.setLocalTranslation(0, 0, -d);
    }

/**

  • Sets the target to turn around
  • @param to the spatial to turn around or null for free-flight
    */
    public void setTarget(Spatial to) {
    if (to != null) {
    rotate.setLocalTranslation(cam.getLocation());
    translate.setLocalTranslation(Vector3f.ZERO);
    }
    if (to != null && target != to) {
    float d = rotate.getWorldTranslation().distance(to.getWorldTranslation());
    rotate.lookAt(to.getWorldTranslation(), cam.getUp());
    rotate.setLocalTranslation(to.getWorldTranslation());
    translate.setLocalTranslation(0, 0, -d);
    target = to;
    } else {
    rotate.setLocalTranslation(cam.getLocation());
    translate.setLocalTranslation(Vector3f.ZERO);
    target = null;
    }
    }

/**

  • Sets the camera to control
  • @param cam the camera to control
    */
    public void setCamera(Camera cam) {
    this.cam = cam;
    }

/**

  • Set if drag to rotate mode is enabled.
  • When true, the user must hold the mouse button and drag over the screen to
  • rotate the camera, and the cursor is visible until dragged. Otherwise, the
  • cursor is invisible at all times and holding the mouse button is not needed
  • to rotate the camera. This feature is disabled by default.
  • @param dragToRotate True if drag to rotate mode is enabled.
    */
    public void setDragToRotate(boolean dragToRotate) {
    this.dragToRotate = dragToRotate;
    if (inputManager != null) {
    inputManager.setCursorVisible(dragToRotate);
    }
    }

/**

  • @return true iff the latest click has moved the camera
    */
    public boolean hasDragged() {
    return dragged;
    }

/**

  • @return If drag to rotate feature is enabled.
  • @see FlyAroundAppState#setDragToRotate(boolean)
    */
    public boolean isDragToRotate() {
    return dragToRotate;
    }

@Override
public void update(float tpf) {
float speed;
if (target == null) {
// no anchor, move the rotate point
speed = -rotSpeed; // rotation inverted when no target
if (fwd) {
rotate.move(cam.getDirection().mult(tpf * linSpeed));
}
if (bkwd) {
rotate.move(cam.getDirection().mult(tpf * -linSpeed));
}
} else {
// anchor is set, move around it
speed = +rotSpeed;
if (fwd) {
translate.move(0, 0, tpf * linSpeed);
}
if (bkwd) {
translate.move(0, 0, tpf * -linSpeed);
}
}
if (up) {
rotate.rotate(tpf * speed, 0, 0);
}
if (down) {
rotate.rotate(tpf * -speed, 0, 0);
}
if (left) {
rotate.rotate(0, tpf * -speed, 0);
}
if (right) {
rotate.rotate(0, tpf * speed, 0);
}
} // update(tpf)

public void onAction(String name, boolean isPressed, float tpf) {
if (“Click”.equals(name)) {
clicked = isPressed;
if (dragToRotate) {
inputManager.setCursorVisible(!clicked);
if (clicked)
dragged = false;
}
}
if (“Up”.equals(name)) {
up = isPressed;
} else if (“Down”.equals(name)) {
down = isPressed;
} else if (“Left”.equals(name)) {
left = isPressed;
} else if (“Right”.equals(name)) {
right = isPressed;
} else if (“Fwd”.equals(name)) {
fwd = isPressed;
} else if (“Bkwd”.equals(name)) {
bkwd = isPressed;
}
} // onAction(…)

protected void initInputAZERTY() {
inputManager.addMapping(“Up”, new KeyTrigger(KeyInput.KEY_Z));
inputManager.addMapping(“Down”, new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping(“Left”, new KeyTrigger(KeyInput.KEY_Q));
inputManager.addMapping(“Right”, new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping(“Fwd”, new KeyTrigger(KeyInput.KEY_PGUP));
inputManager.addMapping(“Bkwd”, new KeyTrigger(KeyInput.KEY_PGDN));
inputManager.addListener(this, “Up”, “Down”, “Left”, “Right”, “Fwd”, “Bkwd”);
}

protected void initInputQWERTY() {
inputManager.addMapping(“Up”, new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping(“Down”, new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping(“Left”, new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping(“Right”, new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping(“Fwd”, new KeyTrigger(KeyInput.KEY_PGUP));
inputManager.addMapping(“Bkwd”, new KeyTrigger(KeyInput.KEY_PGDN));
inputManager.addListener(this, “Up”, “Down”, “Left”, “Right”, “Fwd”, “Bkwd”);
}

protected void initInputMouse() {
inputManager.addMapping(“UpM”, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
inputManager.addMapping(“DownM”, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
inputManager.addMapping(“LeftM”, new MouseAxisTrigger(MouseInput.AXIS_X, true));
inputManager.addMapping(“RightM”, new MouseAxisTrigger(MouseInput.AXIS_X, false));
inputManager.addMapping(“FwdM”, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
inputManager.addMapping(“BkwM”, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));
inputManager.addMapping(“Click”, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addListener(this, “UpM”, “DownM”, “LeftM”, “RightM”, “FwdM”, “BkwM”, “Click”);
}

public void onAnalog(String name, float value, float tpf) {
float speed;
if (target == null) {
// no anchor, move the rotate point
speed = -rotSpeed * 100 * value; // rotation inverted when no target
if (“FwdM”.equals(name)) {
rotate.move(cam.getDirection().mult(tpf * linSpeed * value));
}
if (“BkwM”.equals(name)) {
rotate.move(cam.getDirection().mult(tpf * -linSpeed * value));
}
} else {
// anchor is set, move around it
speed = +rotSpeed * 100 * value;
if (“FwdM”.equals(name)) {
System.out.println("FWD "+value);
translate.move(0, 0, tpf * linSpeed * value);
}
if (“BkwM”.equals(name)) {
System.out.println("BKWD "+value);
translate.move(0, 0, tpf * -linSpeed * value);
}
}
if (clicked || !dragToRotate) {
if (“UpM”.equals(name)) {
rotate.rotate(tpf * speed, 0, 0);
if (dragToRotate)
dragged = true;
}
if (“DownM”.equals(name)) {
rotate.rotate(tpf * -speed, 0, 0);
if (dragToRotate)
dragged = true;
}
if (“LeftM”.equals(name)) {
rotate.rotate(0, tpf * -speed, 0);
if (dragToRotate)
dragged = true;
}
if (“RightM”.equals(name)) {
rotate.rotate(0, tpf * speed, 0);
if (dragToRotate)
dragged = true;
}
} // if rotation allowed
} // onAnalog(…)

} // class FlyAroundAppState
[/java]

Edit : remove the test-case that did not fit in :((

1 Like

And here is the test-case :

  • move freely with qzsd, page up/down
  • select a cube with a mouse click.
  • orbit around it with qzsd, page up/down or the mouse-drag or wheel.
  • select another and start again
  • click on the selected one or on nothing to return to free-move

Here is the code for it :
[java]
package mygame;

import com.jme3.app.FlyCamAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Quad;
import java.util.Random;

/**

  • Test for FlyAroundAppState
  • @author jeanpierre
    */
    public class FATest extends SimpleApplication implements ActionListener {

public static void main(String args[]) {
new FATest().start();
}
protected FlyAroundAppState around;

@Override
public void simpleInitApp() {
Box b = new Box(0.5f, 0.5f, 0.5f);
Random r = new Random();
for (int i = 0; i < 10; i++) {
Geometry g = new Geometry("box", b);
g.setMaterial(assetManager.loadMaterial("Common/Materials/WhiteColor.j3m"));
g.setLocalTranslation(r.nextInt(10) - 5, r.nextInt(10) - 5, 11 - r.nextInt(10));
rootNode.attachChild(g);
}

stateManager.detach(stateManager.getState(FlyCamAppState.class));
around = new FlyAroundAppState();
stateManager.attach(around);
around.setRootNode(rootNode);
around.setDragToRotate(true);

inputManager.addMapping(&quot;Select&quot;, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addListener(this, &quot;Select&quot;);

if (! around.isDragToRotate()) {
  Quad visor = new Quad(2, 20);
  Geometry g = new Geometry(&quot;visor&quot;, visor);
  g.setMaterial(assetManager.loadMaterial(&quot;Common/Materials/RedColor.j3m&quot;));
  g.setLocalTranslation(cam.getWidth()/2-1, cam.getHeight()/2-10, 1);
  guiNode.attachChild(g);
  visor = new Quad(20, 2);
  g = new Geometry(&quot;visor&quot;, visor);
  g.setMaterial(assetManager.loadMaterial(&quot;Common/Materials/RedColor.j3m&quot;));
  g.setLocalTranslation(cam.getWidth()/2-10, cam.getHeight()/2-1, 1);
  guiNode.attachChild(g);
}

} // simpleInitApp()

private static CollisionResult pick(Camera cam, Vector2f mouseLoc, Node node) {
CollisionResults results = new CollisionResults();
Ray ray = new Ray();
Vector3f pos, dir;
pos = cam.getWorldCoordinates(mouseLoc, 0).clone();
dir = cam.getWorldCoordinates(mouseLoc, 0.1f).clone();
dir.subtractLocal(pos).normalizeLocal();
ray.setOrigin(pos);
ray.setDirection(dir);
node.collideWith(ray, results);
CollisionResult result = results.getClosestCollision();
return result;
}

@Override
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals("Select") && !keyPressed) {
CollisionResult r;
if (around.isDragToRotate()) {
if (around.hasDragged())
return;
r = pick(cam, inputManager.getCursorPosition(), rootNode);
} else {
r = pick(cam, new Vector2f(cam.getWidth()/2,cam.getHeight()/2), rootNode);
}
if (r != null) {
around.setTarget(r.getGeometry());
} else
around.setTarget(null);
}
} // onAction
} // FATest
[/java]

Enjoy !

Edit : You can also change line 46 to get drag-to-move behavior.

@yang71 Thanks! Are you French? AFAIK, nobody has AZERTY keyboards apart from French speakers… BTW, I live in Germany, so I have QWERTZ :smiley:

Sorry for being French ! I didn’t know about QWERTZ keyboards either !
Now I need to sleep a little :wink:
See you.

@yang71 your code has issues with rotation. sometimes camerarotates along local Z axis.

Right @mifth, My fault. I feel so stupid.

Here is a new version, but rotation is a bit awkward when close to the top/bottom. You would think the camera would go down, but this would actually rotate your Z axis.
With this version, you have a turntable that you can look over or below… without tilting your head.

Is that what you are looking for ?
[java]
package mygame;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.CameraControl;

/**

  • A camera that fly around the specified object
  • @author jeanpierre
    */
    public class FlyAroundAppState extends AbstractAppState implements ActionListener, AnalogListener {

protected boolean dragToRotate, dragged;
protected Camera cam;
protected Spatial target;
protected Node rotH,rotV;
protected CameraNode translate;
protected boolean up, down, left, right, fwd, bkwd, clicked;
protected float linSpeed, rotSpeed;
protected InputManager inputManager;

/**

  • Builds a new fly-around camera
    */
    public FlyAroundAppState() {
    rotH = new Node(“orbitH”);
    rotV = new Node(“orbitV”);
    rotH.attachChild(rotV);
    dragToRotate = dragged = false;
    }

@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
cam = app.getCamera();
translate = new CameraNode(“dolly”, cam);
translate.setControlDir(CameraControl.ControlDirection.SpatialToCamera);
rotV.attachChild(translate);
up = down = left = right = false;
linSpeed = 5;
rotSpeed = 0.5f;
inputManager = app.getInputManager();
initInputAZERTY();
initInputMouse();
inputManager.setCursorVisible(dragToRotate);
}

/**

  • Sets the move speed of the camera
  • @param lin the linear speed
  • @param ang the angular speed
    */
    public void setSpeed(float lin, float ang) {
    linSpeed = lin;
    rotSpeed = ang;
    }

/**

  • Attaches the camera to the rootnode. MUST be used!
  • @param rootNode the root node
    */
    public void setRootNode(Node rootNode) {
    rootNode.attachChild(rotH);
    }

/**

  • Sets the distance of the camera to the target
  • @param d the distance to set
    */
    public void setDistance(float d) {
    translate.setLocalTranslation(0, 0, -d);
    }

/**

  • Sets the target to turn around

  • @param to the spatial to turn around or null for free-flight
    */
    public void setTarget(Spatial to) {
    if (to != null) {
    rotH.setLocalTranslation(cam.getLocation());
    translate.setLocalTranslation(Vector3f.ZERO);
    }
    if (to != null && target != to) {
    float d = rotH.getWorldTranslation().distance(to.getWorldTranslation());

    // rotH.lookAt(to.getWorldTranslation(), cam.getUp());
    Vector3f dir = to.getWorldTranslation().subtract(rotH.getWorldTranslation()).normalizeLocal();
    float elev = dir.angleBetween(Vector3f.UNIT_Y) - FastMath.HALF_PI;
    dir.y = 0;
    dir.normalizeLocal();
    float rot = dir.angleBetween(Vector3f.UNIT_Z);
    rotH.setLocalRotation(new Quaternion(new float[]{0,rot,0}));
    rotV.setLocalRotation(new Quaternion(new float[]{elev,0,0}));

    rotH.setLocalTranslation(to.getWorldTranslation());
    translate.setLocalTranslation(0, 0, -d);
    target = to;
    } else {
    rotH.setLocalTranslation(cam.getLocation());
    translate.setLocalTranslation(Vector3f.ZERO);
    target = null;
    }
    }

/**

  • Sets the camera to control
  • @param cam the camera to control
    */
    public void setCamera(Camera cam) {
    this.cam = cam;
    }

/**

  • Set if drag to rotate mode is enabled.
  • When true, the user must hold the mouse button and drag over the screen to
  • rotate the camera, and the cursor is visible until dragged. Otherwise, the
  • cursor is invisible at all times and holding the mouse button is not needed
  • to rotate the camera. This feature is disabled by default.
  • @param dragToRotate True if drag to rotate mode is enabled.
    */
    public void setDragToRotate(boolean dragToRotate) {
    this.dragToRotate = dragToRotate;
    if (inputManager != null) {
    inputManager.setCursorVisible(dragToRotate);
    }
    }

/**

  • @return true iff the latest click has moved the camera
    */
    public boolean hasDragged() {
    return dragged;
    }

/**

  • @return If drag to rotate feature is enabled.
  • @see FlyAroundAppState#setDragToRotate(boolean)
    */
    public boolean isDragToRotate() {
    return dragToRotate;
    }

@Override
public void update(float tpf) {
float speed;
if (target == null) {
// no anchor, move the rotate point
speed = -rotSpeed; // rotation inverted when no target
if (fwd) {
rotH.move(cam.getDirection().mult(tpf * linSpeed));
}
if (bkwd) {
rotH.move(cam.getDirection().mult(tpf * -linSpeed));
}
} else {
// anchor is set, move around it
speed = +rotSpeed;
if (fwd) {
translate.move(0, 0, tpf * linSpeed);
}
if (bkwd) {
translate.move(0, 0, tpf * -linSpeed);
}
}
if (up) {
rotV.rotate(tpf * speed, 0, 0);
}
if (down) {
rotV.rotate(tpf * -speed, 0, 0);
}
if (cam.getUp().dot(Vector3f.UNIT_Y) < 0)
speed = - speed;
if (left) {
rotH.rotate(0, tpf * -speed, 0);
}
if (right) {
rotH.rotate(0, tpf * speed, 0);
}
} // update(tpf)

public void onAction(String name, boolean isPressed, float tpf) {
if ("Click".equals(name)) {
clicked = isPressed;
if (dragToRotate) {
inputManager.setCursorVisible(!clicked);
if (clicked) {
dragged = false;
}
}
}
if ("Up".equals(name)) {
up = isPressed;
} else if ("Down".equals(name)) {
down = isPressed;
} else if ("Left".equals(name)) {
left = isPressed;
} else if ("Right".equals(name)) {
right = isPressed;
} else if ("Fwd".equals(name)) {
fwd = isPressed;
} else if ("Bkwd".equals(name)) {
bkwd = isPressed;
}
} // onAction(…)

protected void initInputAZERTY() {
inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_Z));
inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_Q));
inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping("Fwd", new KeyTrigger(KeyInput.KEY_PGUP));
inputManager.addMapping("Bkwd", new KeyTrigger(KeyInput.KEY_PGDN));
inputManager.addListener(this, "Up", "Down", "Left", "Right", "Fwd", "Bkwd");
}

protected void initInputQWERTY() {
inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping("Fwd", new KeyTrigger(KeyInput.KEY_PGUP));
inputManager.addMapping("Bkwd", new KeyTrigger(KeyInput.KEY_PGDN));
inputManager.addListener(this, "Up", "Down", "Left", "Right", "Fwd", "Bkwd");
}

protected void initInputMouse() {
inputManager.addMapping("UpM", new MouseAxisTrigger(MouseInput.AXIS_Y, false));
inputManager.addMapping("DownM", new MouseAxisTrigger(MouseInput.AXIS_Y, true));
inputManager.addMapping("LeftM", new MouseAxisTrigger(MouseInput.AXIS_X, true));
inputManager.addMapping("RightM", new MouseAxisTrigger(MouseInput.AXIS_X, false));
inputManager.addMapping("FwdM", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
inputManager.addMapping("BkwM", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));
inputManager.addMapping("Click", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addListener(this, "UpM", "DownM", "LeftM", "RightM", "FwdM", "BkwM", "Click");
}

public void onAnalog(String name, float value, float tpf) {
float speed;
if (target == null) {
// no anchor, move the rotate point
speed = -rotSpeed * 100 * value; // rotation inverted when no target
if ("FwdM".equals(name)) {
rotH.move(cam.getDirection().mult(tpf * linSpeed * value));
}
if ("BkwM".equals(name)) {
rotH.move(cam.getDirection().mult(tpf * -linSpeed * value));
}
} else {
// anchor is set, move around it
speed = +rotSpeed * 100 * value;
if ("FwdM".equals(name)) {
System.out.println("FWD " + value);
translate.move(0, 0, tpf * linSpeed * value);
}
if ("BkwM".equals(name)) {
System.out.println("BKWD " + value);
translate.move(0, 0, tpf * -linSpeed * value);
}
}
if (clicked || !dragToRotate) {
if ("UpM".equals(name)) {
rotV.rotate(tpf * speed, 0, 0);
if (dragToRotate) {
dragged = true;
}
}
if ("DownM".equals(name)) {
rotV.rotate(tpf * -speed, 0, 0);
if (dragToRotate) {
dragged = true;
}
}
if (cam.getUp().dot(Vector3f.UNIT_Y) < 0)
speed = - speed;
if ("LeftM".equals(name)) {
rotH.rotate(0, tpf * -speed, 0);
if (dragToRotate) {
dragged = true;
}
}
if ("RightM".equals(name)) {
rotH.rotate(0, tpf * speed, 0);
if (dragToRotate) {
dragged = true;
}
}
} // if rotation allowed
} // onAnalog(…)
} // class FlyAroundAppState
[/java]

@yang71 I’m also French! (and Canadian!) I just live in Germany …

Hello @yang71 and @monster . Sorry for late response. I did a new camera and fixed all issues mentioned above.
http://code.google.com/p/jme-simple-examples/source/detail?r=f29cba68bcabf9de329a2ac9c9136d264d9ee7a8

Just grab my chaseCamera class and replace the JME chaseCamera.

@monster i know you make an editor. I did my own too… possibly it will help you somehow: http://hub.jmonkeyengine.org/forum/topic/simple-world-editor/

Hi @mifth.

Nice to see you got something working. Much more complicated than mine, but if it fits your needs… From what I looked, you took the mathematical approach instead of nodes. Works well as long as you don’t fool with the matrices ;-).